Version Description
Woocommerce compatibility raised to 6.5.1 WordPress compatibility raised to 6.0
Download this release
Release Info
Developer | printful |
Plugin | Printful Integration for WooCommerce |
Version | 2.1.32 |
Comparing to | |
See all releases |
Code changes from version 2.1.31 to 2.1.32
- printful-shipping.php +1 -1
- readme.txt +10 -2
- trunk/assets/css/dashboard.css +181 -0
- trunk/assets/css/global.css +19 -0
- trunk/assets/css/settings.css +46 -0
- trunk/assets/css/size-guide.css +169 -0
- trunk/assets/css/status.css +21 -0
- trunk/assets/css/support.css +62 -0
- trunk/assets/images/connect.svg +86 -0
- trunk/assets/images/loading.gif +0 -0
- trunk/assets/images/printful-logo-footer.png +0 -0
- trunk/assets/images/printful-menu-icon.png +0 -0
- trunk/assets/js/block-loader.js +22 -0
- trunk/assets/js/connect.js +60 -0
- trunk/assets/js/intercom.min.js +12 -0
- trunk/assets/js/product-customizer.js +207 -0
- trunk/assets/js/product-size-guide.js +468 -0
- trunk/assets/js/settings.js +44 -0
- trunk/i18n/languages/printful.pot +695 -0
- trunk/includes/class-printful-admin-dashboard.php +270 -0
- trunk/includes/class-printful-admin-settings.php +466 -0
- trunk/includes/class-printful-admin-status.php +597 -0
- trunk/includes/class-printful-admin-support.php +238 -0
- trunk/includes/class-printful-admin.php +172 -0
- trunk/includes/class-printful-carriers.php +90 -0
- trunk/includes/class-printful-client.php +192 -0
- trunk/includes/class-printful-customizer.php +168 -0
- trunk/includes/class-printful-integration.php +165 -0
- trunk/includes/class-printful-request-log.php +151 -0
- trunk/includes/class-printful-rest-api-controller.php +263 -0
- trunk/includes/class-printful-shipping.php +337 -0
- trunk/includes/class-printful-size-chart-tab.php +131 -0
- trunk/includes/class-printful-size-guide.php +221 -0
- trunk/includes/class-printful-taxes.php +189 -0
- trunk/includes/class-printful-template.php +77 -0
- trunk/includes/templates/ajax-loader.php +11 -0
- trunk/includes/templates/connect.php +40 -0
- trunk/includes/templates/customizer-hidden-input.php +1 -0
- trunk/includes/templates/error.php +1 -0
- trunk/includes/templates/footer.php +1 -0
- trunk/includes/templates/header.php +20 -0
- trunk/includes/templates/inline-script.php +3 -0
- trunk/includes/templates/order-table.php +69 -0
- trunk/includes/templates/personalize-button.php +12 -0
- trunk/includes/templates/quick-links.php +32 -0
- trunk/includes/templates/setting-group.php +105 -0
- trunk/includes/templates/setting-submit.php +24 -0
- trunk/includes/templates/shipping-notification.php +4 -0
- trunk/includes/templates/size-guide-button.php +10 -0
- trunk/includes/templates/stats.php +44 -0
- trunk/includes/templates/status-report.php +23 -0
- trunk/includes/templates/status-table.php +56 -0
- trunk/includes/templates/support-info.php +27 -0
- trunk/license.txt +697 -0
- trunk/printful-shipping.php +142 -0
- trunk/readme.txt +438 -0
printful-shipping.php
CHANGED
@@ -20,7 +20,7 @@ if ( ! defined( 'PF_PLUGIN_FILE' ) ) {
|
|
20 |
|
21 |
class Printful_Base {
|
22 |
|
23 |
-
const VERSION = '2.1.
|
24 |
const PF_HOST = 'https://www.printful.com/';
|
25 |
const PF_API_HOST = 'https://api.printful.com/';
|
26 |
|
20 |
|
21 |
class Printful_Base {
|
22 |
|
23 |
+
const VERSION = '2.1.32';
|
24 |
const PF_HOST = 'https://www.printful.com/';
|
25 |
const PF_API_HOST = 'https://api.printful.com/';
|
26 |
|
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: 5.3
|
5 |
-
Tested up to:
|
6 |
Requires PHP: 5.6
|
7 |
-
Stable tag: 2.1.
|
8 |
License: GPLv3 or later
|
9 |
License URI: https://www.gnu.org/licenses/gpl-3.0.en.html
|
10 |
|
@@ -65,6 +65,10 @@ Go to https://www.printful.com/dashboard/store , select your WooCommerce store,
|
|
65 |
|
66 |
== Upgrade Notice ==
|
67 |
|
|
|
|
|
|
|
|
|
68 |
= 2.1.31 =
|
69 |
Licence updated to GPLv3. WordPress compatibility raised to 5.9 and Woocommerce compatibility raised to 6.4
|
70 |
|
@@ -235,6 +239,10 @@ First release
|
|
235 |
|
236 |
== Changelog ==
|
237 |
|
|
|
|
|
|
|
|
|
238 |
= 2.1.31 =
|
239 |
* Licence updated to GPLv3
|
240 |
* WordPress compatibility raised to 5.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: 5.3
|
5 |
+
Tested up to: 6.0
|
6 |
Requires PHP: 5.6
|
7 |
+
Stable tag: 2.1.32
|
8 |
License: GPLv3 or later
|
9 |
License URI: https://www.gnu.org/licenses/gpl-3.0.en.html
|
10 |
|
65 |
|
66 |
== Upgrade Notice ==
|
67 |
|
68 |
+
= 2.1.32 =
|
69 |
+
Woocommerce compatibility raised to 6.5.1
|
70 |
+
WordPress compatibility raised to 6.0
|
71 |
+
|
72 |
= 2.1.31 =
|
73 |
Licence updated to GPLv3. WordPress compatibility raised to 5.9 and Woocommerce compatibility raised to 6.4
|
74 |
|
239 |
|
240 |
== Changelog ==
|
241 |
|
242 |
+
= 2.1.32 =
|
243 |
+
Woocommerce compatibility raised to 6.5.1
|
244 |
+
WordPress compatibility raised to 6.0
|
245 |
+
|
246 |
= 2.1.31 =
|
247 |
* Licence updated to GPLv3
|
248 |
* WordPress compatibility raised to 5.9
|
trunk/assets/css/dashboard.css
ADDED
@@ -0,0 +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 |
+
}
|
trunk/assets/css/global.css
ADDED
@@ -0,0 +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 |
+
}
|
trunk/assets/css/settings.css
ADDED
@@ -0,0 +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 |
+
}
|
trunk/assets/css/size-guide.css
ADDED
@@ -0,0 +1,169 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
width: 100%;
|
145 |
+
overflow-x: auto;
|
146 |
+
}
|
147 |
+
|
148 |
+
.pf-size-guide-modal-size-chart__table thead {
|
149 |
+
text-transform: uppercase;
|
150 |
+
font-weight: bold;
|
151 |
+
}
|
152 |
+
|
153 |
+
.pf-size-guide-modal-size-chart__table tr {
|
154 |
+
border: none;
|
155 |
+
border-collapse: collapse;
|
156 |
+
}
|
157 |
+
|
158 |
+
.pf-size-guide-modal-size-chart__table td {
|
159 |
+
border: none;
|
160 |
+
border-bottom: 1px solid #E5E5E5;
|
161 |
+
font-size: 16px;
|
162 |
+
text-transform: uppercase;
|
163 |
+
padding: 12px 8px;
|
164 |
+
}
|
165 |
+
|
166 |
+
.pf-size-guide-modal-clear {
|
167 |
+
clear: both;
|
168 |
+
}
|
169 |
+
|
trunk/assets/css/status.css
ADDED
@@ -0,0 +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 |
+
}
|
trunk/assets/css/support.css
ADDED
@@ -0,0 +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 |
+
}
|
trunk/assets/images/connect.svg
ADDED
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1544 500">
|
2 |
+
<defs>
|
3 |
+
<style>
|
4 |
+
.cls-1{isolation:isolate;}.cls-14,.cls-17,.cls-19,.cls-2,.cls-20,.cls-21,.cls-22,.cls-4{fill:none;}.cls-2{stroke:#94e0de;}.cls-14,.cls-17,.cls-2,.cls-3,.cls-4,.cls-5{stroke-miterlimit:10;stroke-width:2.45px;}.cls-3,.cls-7{fill:#fff;}.cls-3,.cls-4{stroke:#14aba3;}.cls-18,.cls-5{fill:#d4f2f2;}.cls-5{stroke:#f2fcfa;opacity:0.5;}.cls-6{fill:#7f54b3;}.cls-8{fill:#f2c994;}.cls-9{fill:#ed4642;}.cls-10{fill:#17bcb5;}.cls-11{fill:#df392f;}.cls-12{fill:#16342f;}.cls-13{fill:#15291a;}.cls-14{stroke:#7ccdcd;}.cls-15{fill:#b5e8e8;}.cls-16{fill:#7ccdcd;}.cls-17{stroke:#f2c994;mix-blend-mode:multiply;}.cls-19,.cls-20{stroke:#d4f2f2;}.cls-19,.cls-20,.cls-21,.cls-22{stroke-linecap:round;stroke-linejoin:round;stroke-width:8px;}.cls-20{stroke-dasharray:0.11
|
5 |
+
32.4;}.cls-21,.cls-22{stroke:#fff;}.cls-22{stroke-dasharray:0.12 35.99;}
|
6 |
+
</style>
|
7 |
+
</defs>
|
8 |
+
<g class="cls-1">
|
9 |
+
<g id="Layer_1" data-name="Layer 1">
|
10 |
+
<line class="cls-2" x1="70.8" y1="406.89" x2="1473.2" y2="406.89"/>
|
11 |
+
<polyline class="cls-3"
|
12 |
+
points="450.35 369.86 438.55 416.13 435.15 416.13 430.24 427.63 557.18 427.63 553.19 416.13 548.87 416.13 537.07 369.86 728.32 369.86 728.32 87.29 259.11 87.29 259.11 369.86 517.61 369.86"/>
|
13 |
+
<circle class="cls-4" cx="493.71" cy="349.28" r="7.77"/>
|
14 |
+
<rect class="cls-5" x="378.65" y="-6.51" width="231.45" height="443.06"
|
15 |
+
transform="translate(709.39 -279.35) rotate(90)"/>
|
16 |
+
<path class="cls-6"
|
17 |
+
d="M395.41,154.11h181a20.71,20.71,0,0,1,20.72,20.73v69.08a20.71,20.71,0,0,1-20.72,20.72h-64.9l8.91,21.81-39.18-21.81H395.5a20.71,20.71,0,0,1-20.72-20.72V174.84a20.64,20.64,0,0,1,20.63-20.73Z"/>
|
18 |
+
<path class="cls-7"
|
19 |
+
d="M387.44,173a7.25,7.25,0,0,1,5.69-2.8q6.92-.54,7.86,6.51c2.8,18.89,5.88,34.88,9.13,48L429.91,187q2.71-5.14,6.78-5.42c4-.27,6.42,2.26,7.41,7.59a168.51,168.51,0,0,0,8.59,30.91q3.53-34.44,11.93-49.71a6.93,6.93,0,0,1,6-4,7.9,7.9,0,0,1,5.7,1.81,7.1,7.1,0,0,1,2.8,5.15,7.8,7.8,0,0,1-.91,4.34c-3.52,6.51-6.41,17.44-8.76,32.63-2.26,14.73-3.07,26.21-2.53,34.43a10.65,10.65,0,0,1-1.09,6A5.82,5.82,0,0,1,461,254c-2.35.18-4.79-.91-7.14-3.35-8.4-8.58-15.09-21.41-20-38.5q-8.81,17.35-13,26c-5.34,10.21-9.85,15.46-13.65,15.73-2.44.18-4.52-1.9-6.33-6.24Q394,229.89,386,179.06a7.7,7.7,0,0,1,1.45-6.06Zm194.22,14.19a20.79,20.79,0,0,0-14.46-10.48,23.24,23.24,0,0,0-4.88-.55c-8.68,0-15.73,4.52-21.24,13.56A47.89,47.89,0,0,0,534,215.21q0,10.44,4.34,17.89a20.79,20.79,0,0,0,14.46,10.48,23.17,23.17,0,0,0,4.88.55c8.77,0,15.81-4.52,21.24-13.56A48.43,48.43,0,0,0,586,205,33.36,33.36,0,0,0,581.66,187.19Zm-11.39,25c-1.26,6-3.52,10.4-6.87,13.38-2.62,2.35-5.06,3.34-7.32,2.89s-4-2.35-5.33-5.87a23.07,23.07,0,0,1-1.63-8.23,30.77,30.77,0,0,1,.64-6.59A29.2,29.2,0,0,1,554.55,197c3-4.43,6.14-6.23,9.4-5.6,2.16.45,4,2.35,5.33,5.87a23,23,0,0,1,1.62,8.23,31.71,31.71,0,0,1-.63,6.68Zm-45.19-25a21,21,0,0,0-14.46-10.48,23.1,23.1,0,0,0-4.88-.55c-8.67,0-15.72,4.52-21.24,13.56a47.89,47.89,0,0,0-7.05,25.49q0,10.44,4.34,17.89a20.83,20.83,0,0,0,14.46,10.48,23.24,23.24,0,0,0,4.88.55c8.77,0,15.82-4.52,21.24-13.56a48.43,48.43,0,0,0,7-25.58C529.42,197.94,528,192.07,525.08,187.19Zm-11.47,25c-1.27,6-3.53,10.4-6.87,13.38-2.62,2.35-5.06,3.34-7.32,2.89s-4-2.35-5.34-5.87a23,23,0,0,1-1.62-8.23,31.42,31.42,0,0,1,.63-6.59A29.2,29.2,0,0,1,497.88,197c3-4.43,6.15-6.23,9.4-5.6,2.17.45,4,2.35,5.33,5.87a22.85,22.85,0,0,1,1.63,8.23,26.7,26.7,0,0,1-.63,6.68Z"/>
|
20 |
+
<polyline class="cls-3"
|
21 |
+
points="1082.61 369.86 1070.82 416.13 1067.42 416.13 1062.51 427.63 1189.45 427.63 1185.46 416.13 1181.14 416.13 1169.34 369.86 1360.58 369.86 1360.58 87.29 891.37 87.29 891.37 369.86 1148.84 369.86"/>
|
22 |
+
<circle class="cls-4" cx="1125.98" cy="349.28" r="7.77"/>
|
23 |
+
<rect class="cls-5" x="1010.91" y="-6.51" width="231.45" height="443.06"
|
24 |
+
transform="translate(1341.66 -911.62) rotate(90)"/>
|
25 |
+
<g id="pf-logo-color-white-1-svg">
|
26 |
+
<polygon class="cls-8"
|
27 |
+
points="1067.23 166.57 1010.78 263.47 1061.75 263.47 1092.72 210.32 1067.23 166.57"/>
|
28 |
+
<polygon class="cls-9"
|
29 |
+
points="1143.69 210.32 1118.21 166.57 1092.72 210.32 1118.21 254.07 1143.69 210.32"/>
|
30 |
+
<polygon class="cls-10"
|
31 |
+
points="1169.18 166.57 1143.69 210.32 1174.66 263.47 1225.64 263.47 1169.18 166.57"/>
|
32 |
+
<polygon class="cls-11" points="1092.72 210.32 1061.75 263.47 1092.72 210.32 1092.72 210.32"/>
|
33 |
+
<polygon class="cls-11"
|
34 |
+
points="1118.21 254.07 1118.21 254.07 1092.72 210.32 1092.72 210.32 1118.21 254.07"/>
|
35 |
+
<polygon class="cls-11"
|
36 |
+
points="1092.72 210.32 1061.75 263.47 1112.73 263.47 1118.21 254.07 1092.72 210.32"/>
|
37 |
+
<polygon class="cls-12"
|
38 |
+
points="1118.21 254.07 1143.69 210.32 1143.69 210.32 1118.21 254.07 1118.21 254.07"/>
|
39 |
+
<polygon class="cls-12" points="1143.69 210.32 1174.66 263.47 1143.69 210.32 1143.69 210.32"/>
|
40 |
+
<polygon class="cls-12"
|
41 |
+
points="1118.21 254.07 1118.21 254.07 1118.21 254.07 1123.68 263.47 1174.66 263.47 1143.69 210.32 1118.21 254.07"/>
|
42 |
+
<polygon class="cls-12" points="1118.21 254.07 1112.73 263.47 1118.21 254.07 1118.21 254.07"/>
|
43 |
+
<polygon class="cls-12" points="1118.21 254.07 1112.73 263.47 1118.21 254.07 1118.21 254.07"/>
|
44 |
+
<polygon class="cls-13" points="1118.21 254.07 1123.68 263.47 1118.21 254.07 1118.21 254.07"/>
|
45 |
+
<polygon class="cls-13" points="1118.21 254.07 1123.68 263.47 1118.21 254.07 1118.21 254.07"/>
|
46 |
+
<polygon class="cls-13" points="1112.73 263.47 1123.68 263.47 1118.21 254.07 1112.73 263.47"/>
|
47 |
+
</g>
|
48 |
+
<line class="cls-14" x1="189.21" y1="318.47" x2="193.29" y2="383.94"/>
|
49 |
+
<line class="cls-14" x1="162.35" y1="333.34" x2="192.05" y2="364.06"/>
|
50 |
+
<line class="cls-14" x1="192.36" y1="369.04" x2="224.16" y2="299.56"/>
|
51 |
+
<line class="cls-14" x1="191.95" y1="371.29" x2="218.51" y2="351.84"/>
|
52 |
+
<ellipse class="cls-15" cx="240.1" cy="317.96" rx="20.13" ry="29.72"
|
53 |
+
transform="translate(-147.91 227.44) rotate(-39.81)"/>
|
54 |
+
<path class="cls-15"
|
55 |
+
d="M167.46,331.05c11.61,17.08-12.63,34.1-27.24,36.66-8.56,1.49-19.26-.41-19.72-10.8-.53-11.76,9.59-24.08,19.41-29.46a24.63,24.63,0,0,1,4.65-2C152.39,323,162.45,323.68,167.46,331.05Z"/>
|
56 |
+
<path class="cls-15"
|
57 |
+
d="M149,294.37a46.36,46.36,0,0,0,15.7,23c8.81,6.81,22.1,9.14,31.48,2.19,5.67-4.2,9.37-10.67,11.23-17.48,5.31-19.48-5.46-42.7-26.8-46-9.89-1.54-20.59,2.24-26.88,10C147,274.47,146.16,284.77,149,294.37Z"/>
|
58 |
+
<circle class="cls-16" cx="158.58" cy="336.64" r="2.77"/>
|
59 |
+
<circle class="cls-16" cx="229.25" cy="303.8" r="2.52"/>
|
60 |
+
<circle class="cls-16" cx="187.19" cy="307.57" r="3.07"/>
|
61 |
+
<polygon class="cls-8" points="229.69 442.79 163.22 442.79 153.72 386.53 239.18 386.53 229.69 442.79"/>
|
62 |
+
<polygon class="cls-8" points="242.92 388.08 149.99 388.08 146.27 371.63 246.64 371.63 242.92 388.08"/>
|
63 |
+
<line class="cls-17" x1="238.92" y1="389.01" x2="164.54" y2="389.01"/>
|
64 |
+
<path class="cls-15"
|
65 |
+
d="M238.12,356.33a19.56,19.56,0,0,1,1.64,16.34c-4.87,13.61-22.21,8.88-29.66.49a20.07,20.07,0,0,1-5.24-13,13.85,13.85,0,0,1,9.62-13.89C224,343,233.56,348.28,238.12,356.33Z"/>
|
66 |
+
<circle class="cls-16" cx="218.25" cy="357.26" r="1.66"/>
|
67 |
+
<circle class="cls-10" cx="801.57" cy="233.66" r="110.07" transform="translate(69.55 635.23) rotate(-45)"/>
|
68 |
+
<path class="cls-7"
|
69 |
+
d="M801.57,125.87A107.79,107.79,0,1,1,693.78,233.66,107.79,107.79,0,0,1,801.57,125.87m0-4.56A112.34,112.34,0,1,0,881,154.22a111.61,111.61,0,0,0-79.45-32.91Z"/>
|
70 |
+
<polygon class="cls-18"
|
71 |
+
points="839.16 181.27 826.43 205.35 839.16 229.44 823 229.44 810.26 205.35 823 181.27 839.16 181.27"/>
|
72 |
+
<line class="cls-19" x1="788.83" y1="205.35" x2="788.77" y2="205.35"/>
|
73 |
+
<line class="cls-20" x1="756.37" y1="205.35" x2="740.06" y2="205.35"/>
|
74 |
+
<line class="cls-19" x1="723.87" y1="205.35" x2="723.81" y2="205.35"/>
|
75 |
+
<line class="cls-21" x1="723.67" y1="262.05" x2="723.73" y2="262.05"/>
|
76 |
+
<line class="cls-21" x1="750.61" y1="262.05" x2="750.67" y2="262.05"/>
|
77 |
+
<line class="cls-19" x1="852.47" y1="205.35" x2="852.53" y2="205.35"/>
|
78 |
+
<line class="cls-19" x1="879.41" y1="205.35" x2="879.47" y2="205.35"/>
|
79 |
+
<polygon class="cls-7"
|
80 |
+
points="758.66 286.05 771.39 261.97 758.66 237.88 774.83 237.88 787.56 261.97 774.83 286.05 758.66 286.05"/>
|
81 |
+
<line class="cls-21" x1="876.2" y1="262.72" x2="876.15" y2="262.72"/>
|
82 |
+
<line class="cls-22" x1="840.15" y1="262.72" x2="822.03" y2="262.72"/>
|
83 |
+
<line class="cls-21" x1="804.04" y1="262.72" x2="803.98" y2="262.72"/>
|
84 |
+
</g>
|
85 |
+
</g>
|
86 |
+
</svg>
|
trunk/assets/images/loading.gif
ADDED
Binary file
|
trunk/assets/images/printful-logo-footer.png
ADDED
Binary file
|
trunk/assets/images/printful-menu-icon.png
ADDED
Binary file
|
trunk/assets/js/block-loader.js
ADDED
@@ -0,0 +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 |
+
})();
|
trunk/assets/js/connect.js
ADDED
@@ -0,0 +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 |
+
})();
|
trunk/assets/js/intercom.min.js
ADDED
@@ -0,0 +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}();
|
trunk/assets/js/product-customizer.js
ADDED
@@ -0,0 +1,207 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/** Define class */
|
2 |
+
var Printful_Product_Customizer;
|
3 |
+
|
4 |
+
(function () {
|
5 |
+
'use strict';
|
6 |
+
|
7 |
+
/**
|
8 |
+
*
|
9 |
+
* @type {{modal: null, baseUrl: string, onCustomizeClick: onCustomizeClick, closeModal: closeModal, createModal: (function(string): HTMLDivElement), listenForResponse: listenForResponse, onCustomDesignSave: onCustomDesignSave}}
|
10 |
+
*/
|
11 |
+
Printful_Product_Customizer = {
|
12 |
+
modal: null,
|
13 |
+
baseUrl: 'https://www.printful.com',
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Handle click event
|
17 |
+
* @param {string} site_url
|
18 |
+
*/
|
19 |
+
onCustomizeClick: function (site_url) {
|
20 |
+
var variation_id = jQuery('input[name="variation_id"]').val(); // get selected variation ID
|
21 |
+
|
22 |
+
if (!variation_id) {
|
23 |
+
// if variation does not exist, probably means single variant product
|
24 |
+
variation_id = jQuery('button[name="add-to-cart"]').val();
|
25 |
+
}
|
26 |
+
|
27 |
+
if (!variation_id) {
|
28 |
+
return;
|
29 |
+
}
|
30 |
+
|
31 |
+
var url = this.baseUrl + '/product-customizer/?website=' + site_url + '&variant=' + variation_id;
|
32 |
+
|
33 |
+
document.body.appendChild(this.createModal(url));
|
34 |
+
//start listening to post messaged
|
35 |
+
this.listenForResponse();
|
36 |
+
},
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Close the modal element
|
40 |
+
*/
|
41 |
+
closeModal: function () {
|
42 |
+
if (this.modal) {
|
43 |
+
this.modal.parentNode.removeChild(this.modal);
|
44 |
+
this.modal = null;
|
45 |
+
}
|
46 |
+
},
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Create modal content
|
50 |
+
* @param {string} url
|
51 |
+
* @returns {HTMLDivElement}
|
52 |
+
*/
|
53 |
+
createModal: function (url) {
|
54 |
+
// clear the old one just to be sure
|
55 |
+
this.closeModal();
|
56 |
+
|
57 |
+
// create iframe
|
58 |
+
var isMobileSafari = false;
|
59 |
+
var userAgent = (window.navigator && window.navigator.userAgent) ? window.navigator.userAgent : false;
|
60 |
+
|
61 |
+
if (userAgent && userAgent.match(/iPhone|iPad|iPod/i)) {
|
62 |
+
isMobileSafari = true;
|
63 |
+
}
|
64 |
+
|
65 |
+
var modal = document.createElement('div');
|
66 |
+
modal.className = 'pf-customize-modal';
|
67 |
+
modal.setAttribute('style', 'position: fixed; z-index: 2147483648; padding: 20px; top: 0; width: 100%; height: 100%; left: 0; background: rgba(0, 0 , 0, 0.6); box-sizing: border-box;');
|
68 |
+
|
69 |
+
|
70 |
+
var modalHeader = document.createElement('div');
|
71 |
+
modalHeader.setAttribute('style', 'padding: 15px; border-bottom: 1px solid #e5e5e5; overflow:hidden; position:absolute; top:0; left:0; width:100%; box-sizing: border-box;');
|
72 |
+
|
73 |
+
var closeBtn = document.createElement('button');
|
74 |
+
closeBtn.onclick = this.closeModal.bind(this);
|
75 |
+
closeBtn.setAttribute('style', 'height:30px; width:30px; cursor:pointer; border:0px; background:0 0; padding:0; -webkit-appearance:none; color:#000; float:right; background:none;');
|
76 |
+
var closeImg = document.createElement('img');
|
77 |
+
closeImg.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=';
|
78 |
+
closeImg.setAttribute('style', 'height:30px; width:30px;');
|
79 |
+
closeBtn.appendChild(closeImg);
|
80 |
+
modalHeader.appendChild(closeBtn);
|
81 |
+
|
82 |
+
var modalHeaderTitle = document.createElement('h4');
|
83 |
+
modalHeaderTitle.className = 'product-customizer__header-title';
|
84 |
+
modalHeaderTitle.setAttribute('style', 'float: left;font-weight:bold;font-size:23px;color:#222;line-height:30px;margin:0px;clear:none;');
|
85 |
+
modalHeaderTitle.innerText = window.pfGlobalCustomizer && window.pfGlobalCustomizer.modal_title ? window.pfGlobalCustomizer.modal_title : 'Create a personalized design';
|
86 |
+
modalHeader.appendChild(modalHeaderTitle);
|
87 |
+
|
88 |
+
var modalContent = document.createElement('div');
|
89 |
+
modalContent.setAttribute('style', 'background-color: #fff; width: 100%; height: 100%;overflow:hidden;position:relative');
|
90 |
+
modalContent.appendChild(modalHeader);
|
91 |
+
|
92 |
+
var styles = document.createElement('style');
|
93 |
+
styles.innerHTML = '@media screen and (max-width: 768px) { .product-customizer__header-title {font-size: 16px !important;} }';
|
94 |
+
modalContent.appendChild(styles);
|
95 |
+
|
96 |
+
modal.appendChild(modalContent);
|
97 |
+
|
98 |
+
var iframe = document.createElement('iframe');
|
99 |
+
iframe.src = url;
|
100 |
+
iframe.width = '100%';
|
101 |
+
iframe.height = '100%';
|
102 |
+
|
103 |
+
if (isMobileSafari) {
|
104 |
+
var iframeWrapper = document.createElement('div');
|
105 |
+
iframeWrapper.setAttribute('style', '-webkit-overflow-scrolling: touch; overflow: scroll; height: 100%; top: 61px; box-sizing: border-box; position: absolute; width: 100%; padding-bottom: 60px;');
|
106 |
+
iframe.setAttribute('style', 'border: 0; box-sizing: border-box;');
|
107 |
+
iframeWrapper.appendChild(iframe);
|
108 |
+
modalContent.appendChild(iframeWrapper);
|
109 |
+
} else {
|
110 |
+
iframe.setAttribute('style', 'border: 0; padding-top: 60px; box-sizing: border-box;');
|
111 |
+
modalContent.appendChild(iframe);
|
112 |
+
}
|
113 |
+
|
114 |
+
|
115 |
+
this.modal = modal;
|
116 |
+
|
117 |
+
return modal;
|
118 |
+
},
|
119 |
+
|
120 |
+
/**
|
121 |
+
* Listen for response from PF
|
122 |
+
*/
|
123 |
+
listenForResponse: function () {
|
124 |
+
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
|
125 |
+
var eventer = window[eventMethod];
|
126 |
+
var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";
|
127 |
+
eventer(messageEvent, function (e) {
|
128 |
+
if (e.origin !== this.baseUrl) {
|
129 |
+
return;
|
130 |
+
}
|
131 |
+
|
132 |
+
if (typeof e.data['action'] === 'undefined' || e.data['action'] !== 'PFProductCustomized') {
|
133 |
+
return;
|
134 |
+
}
|
135 |
+
|
136 |
+
this.onCustomDesignSave(e.data['hash']);
|
137 |
+
}.bind(this));
|
138 |
+
},
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Manage response from PF
|
142 |
+
* @param {string} hash
|
143 |
+
*/
|
144 |
+
onCustomDesignSave: function (hash) {
|
145 |
+
jQuery('#pfc_hash').val(hash);
|
146 |
+
this.closeModal();
|
147 |
+
|
148 |
+
jQuery('.single_add_to_cart_button').click();
|
149 |
+
}
|
150 |
+
};
|
151 |
+
})();
|
152 |
+
|
153 |
+
setInterval(function () {
|
154 |
+
/**
|
155 |
+
* @type {Array<HTMLImageElement>}
|
156 |
+
*/
|
157 |
+
var toCheck = [];
|
158 |
+
|
159 |
+
jQuery('img.pf-image-pending').each(function () {
|
160 |
+
jQuery(this).removeClass('pf-image-pending');
|
161 |
+
jQuery(this).addClass('pf-image-checking');
|
162 |
+
toCheck.push(this);
|
163 |
+
});
|
164 |
+
|
165 |
+
var hashes = toCheck.reduce(function(carry, image){
|
166 |
+
carry[image.getAttribute('data-hash')] = image;
|
167 |
+
|
168 |
+
return carry;
|
169 |
+
}, {});
|
170 |
+
|
171 |
+
// Resolve admin base URL
|
172 |
+
var adminURL = window.pfGlobalCustomizer && window.pfGlobalCustomizer.admin_url && window.pfGlobalCustomizer.admin_url.length
|
173 |
+
? window.pfGlobalCustomizer.admin_url
|
174 |
+
: '/wp-admin/';
|
175 |
+
|
176 |
+
// rtrim('/') + '/'
|
177 |
+
var hasSlash = adminURL.substring(adminURL.length - 1) === '/';
|
178 |
+
if (!hasSlash) {
|
179 |
+
adminURL += '/';
|
180 |
+
}
|
181 |
+
|
182 |
+
// if pending (loading) images exist, request image urls
|
183 |
+
if (Object.keys(hashes).length > 0) {
|
184 |
+
jQuery.ajax({
|
185 |
+
url: adminURL + 'admin-ajax.php',
|
186 |
+
type: 'GET',
|
187 |
+
data: {
|
188 |
+
action: 'printful_customized_thumb',
|
189 |
+
hashes: Object.keys(hashes)
|
190 |
+
},
|
191 |
+
success: function (response) {
|
192 |
+
var result = JSON.parse(response);
|
193 |
+
for (var hash in result){
|
194 |
+
var image = hashes[hash];
|
195 |
+
|
196 |
+
if (result[hash]) {
|
197 |
+
image.src = result[hash];
|
198 |
+
jQuery(image).removeClass('pf-image-checking');
|
199 |
+
} else {
|
200 |
+
jQuery(image).removeClass('pf-image-checking');
|
201 |
+
jQuery(image).addClass('pf-image-pending');
|
202 |
+
}
|
203 |
+
}
|
204 |
+
}
|
205 |
+
});
|
206 |
+
}
|
207 |
+
}, 2000);
|
trunk/assets/js/product-size-guide.js
ADDED
@@ -0,0 +1,468 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
* Key is unit identifier (e.g. 'inch') value is translated unit name (e.g. 'Inches')
|
25 |
+
* @var {{}}
|
26 |
+
*/
|
27 |
+
translatedUnitNames: null,
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Handle click event
|
31 |
+
*/
|
32 |
+
onSizeGuideClick: function () {
|
33 |
+
if (!window.pfGlobal || !window.pfGlobal.sg_data_raw) {
|
34 |
+
return;
|
35 |
+
}
|
36 |
+
this.sizeGuideData = JSON.parse(window.pfGlobal.sg_data_raw);
|
37 |
+
|
38 |
+
this.translatedUnitNames = {};
|
39 |
+
if (window.pfGlobal && window.pfGlobal.sg_unit_translations) {
|
40 |
+
this.translatedUnitNames = JSON.parse(window.pfGlobal.sg_unit_translations);
|
41 |
+
}
|
42 |
+
document.body.appendChild(this.createModal());
|
43 |
+
},
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Close the modal element
|
47 |
+
*/
|
48 |
+
closeModal: function () {
|
49 |
+
this.removeNode(this.sizeChartNode);
|
50 |
+
this.removeNode(this.modalContentNode);
|
51 |
+
this.removeNode(this.modalBodyNode);
|
52 |
+
this.removeNode(this.modal);
|
53 |
+
this.sizeChartNode = null;
|
54 |
+
this.modalContentNode = null;
|
55 |
+
this.modalBodyNode = null;
|
56 |
+
this.modal = null;
|
57 |
+
},
|
58 |
+
|
59 |
+
/**
|
60 |
+
* Create modal content
|
61 |
+
* @returns {HTMLDivElement}
|
62 |
+
*/
|
63 |
+
createModal: function () {
|
64 |
+
// Clear the old one just to be sure
|
65 |
+
this.closeModal();
|
66 |
+
this.modal = this.buildEl('div', 'pf-size-guide-modal-wrapper');
|
67 |
+
this.modalBodyNode = this.buildEl('div', 'pf-size-guide-modal');
|
68 |
+
this.modalBodyNode.style.color = this.getTextColor();
|
69 |
+
this.modalBodyNode.style.backgroundColor = this.getBackGroundColor();
|
70 |
+
this.modalBodyNode.appendChild(this.buildModalHeader());
|
71 |
+
|
72 |
+
// Render initial active tab
|
73 |
+
this.renderModalContent(Printful_Product_Size_Guide.TAB_TYPE_PERSON);
|
74 |
+
this.modal.appendChild(this.modalBodyNode);
|
75 |
+
|
76 |
+
return this.modal;
|
77 |
+
},
|
78 |
+
|
79 |
+
buildEl: function (tagName, className, attributes) {
|
80 |
+
attributes = attributes || {};
|
81 |
+
var el = document.createElement(tagName);
|
82 |
+
if (className) {
|
83 |
+
el.className = className;
|
84 |
+
}
|
85 |
+
|
86 |
+
if (attributes.id) {
|
87 |
+
el.setAttribute('id', attributes.id);
|
88 |
+
}
|
89 |
+
|
90 |
+
if (attributes.innerHTML) {
|
91 |
+
el.innerHTML = attributes.innerHTML;
|
92 |
+
}
|
93 |
+
|
94 |
+
if (attributes.src) {
|
95 |
+
el.setAttribute('src', attributes.src);
|
96 |
+
}
|
97 |
+
|
98 |
+
if (attributes.onclick) {
|
99 |
+
el.onclick = attributes.onclick;
|
100 |
+
}
|
101 |
+
|
102 |
+
if (['td', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p'].indexOf(tagName) > -1) {
|
103 |
+
el.style.backgroundColor = this.getBackGroundColor();
|
104 |
+
el.style.color = this.getTextColor();
|
105 |
+
}
|
106 |
+
|
107 |
+
return el;
|
108 |
+
},
|
109 |
+
|
110 |
+
removeNode: function (node) {
|
111 |
+
if (node && node.parentNode && node.parentNode.tagName) {
|
112 |
+
node.parentNode.removeChild(node);
|
113 |
+
}
|
114 |
+
},
|
115 |
+
|
116 |
+
renderModalContent: function (activeTabType) {
|
117 |
+
this.removeNode(this.modalContentNode);
|
118 |
+
this.modalContentNode = this.buildEl('div', 'pf-size-guide-modal__content');
|
119 |
+
|
120 |
+
var tabData = this.getSizeGuideDataForTab(activeTabType),
|
121 |
+
tabTitle = this.getTabTitle(activeTabType);
|
122 |
+
|
123 |
+
this.modalContentNode.appendChild(this.buildSizeGuideTabsNode(activeTabType));
|
124 |
+
this.modalContentNode.appendChild(this.buildTabContent(tabData, tabTitle));
|
125 |
+
|
126 |
+
this.modalBodyNode.appendChild(this.modalContentNode);
|
127 |
+
},
|
128 |
+
|
129 |
+
getSizeGuideDataForTab: function (tabType) {
|
130 |
+
if (tabType === Printful_Product_Size_Guide.TAB_TYPE_PERSON) {
|
131 |
+
return this.sizeGuideData.modelMeasurements;
|
132 |
+
}
|
133 |
+
|
134 |
+
if (tabType === Printful_Product_Size_Guide.TAB_TYPE_PRODUCT) {
|
135 |
+
return this.sizeGuideData.productMeasurements;
|
136 |
+
}
|
137 |
+
|
138 |
+
return {};
|
139 |
+
},
|
140 |
+
|
141 |
+
getTabTitle: function (tabType) {
|
142 |
+
if (tabType === Printful_Product_Size_Guide.TAB_TYPE_PERSON) {
|
143 |
+
return window.pfGlobal && window.pfGlobal.sg_tab_title_person ? window.pfGlobal.sg_tab_title_person : 'Measure yourself';
|
144 |
+
}
|
145 |
+
|
146 |
+
return window.pfGlobal && window.pfGlobal.sg_tab_title_product ? window.pfGlobal.sg_tab_title_product : 'Product measurements';
|
147 |
+
},
|
148 |
+
|
149 |
+
renderSizeChart: function (measurementData, selectedUnit) {
|
150 |
+
if (this.sizeChartNode) {
|
151 |
+
this.removeNode(this.sizeChartNode.firstChild);
|
152 |
+
} else {
|
153 |
+
this.sizeChartNode = this.buildEl('div', 'pf-size-guide-modal-size-chart');
|
154 |
+
}
|
155 |
+
|
156 |
+
this.sizeChartNode.appendChild(this.buildSizeChartBlock(measurementData, selectedUnit));
|
157 |
+
},
|
158 |
+
|
159 |
+
buildModalHeader: function () {
|
160 |
+
var title = window.pfGlobal && window.pfGlobal.sg_modal_title ? window.pfGlobal.sg_modal_title : 'Size guide',
|
161 |
+
wrapper = this.buildEl('div', 'pf-size-guide-modal__header');
|
162 |
+
|
163 |
+
wrapper.appendChild(this.buildEl('h4', 'pf-size-guide-modal__title', {innerHTML: title}));
|
164 |
+
|
165 |
+
var closeBtn = this.buildEl('button', 'pf-size-guide-modal__close'),
|
166 |
+
img = this.buildEl('img');
|
167 |
+
|
168 |
+
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=";
|
169 |
+
closeBtn.appendChild(img);
|
170 |
+
closeBtn.onclick = (this.closeModal).bind(this);
|
171 |
+
|
172 |
+
wrapper.appendChild(closeBtn);
|
173 |
+
wrapper.append(this.buildEl('div', 'pf-size-guide-modal-clear'));
|
174 |
+
|
175 |
+
return wrapper;
|
176 |
+
},
|
177 |
+
|
178 |
+
buildSizeGuideTabsNode: function (activeTabType) {
|
179 |
+
var tabs = this.buildEl('ul', 'pf-product-size-guide__tabs');
|
180 |
+
tabs.appendChild(this.buildSizeGuideTabNode(this.getTabTitle(Printful_Product_Size_Guide.TAB_TYPE_PERSON), Printful_Product_Size_Guide.TAB_TYPE_PERSON, activeTabType));
|
181 |
+
tabs.appendChild(this.buildSizeGuideTabNode(this.getTabTitle(Printful_Product_Size_Guide.TAB_TYPE_PRODUCT), Printful_Product_Size_Guide.TAB_TYPE_PRODUCT, activeTabType));
|
182 |
+
|
183 |
+
return tabs;
|
184 |
+
},
|
185 |
+
|
186 |
+
buildSizeGuideTabNode: function (title, type, activeType) {
|
187 |
+
var className = 'pf-product-size-guide__tab';
|
188 |
+
|
189 |
+
if (type === activeType) {
|
190 |
+
className += ' pf-product-size-guide__tab--active';
|
191 |
+
}
|
192 |
+
|
193 |
+
var tab = this.buildEl('li', className, {
|
194 |
+
innerHTML: title,
|
195 |
+
onclick: (this.renderModalContent).bind(this, type)
|
196 |
+
});
|
197 |
+
|
198 |
+
if (type === activeType) {
|
199 |
+
tab.style.backgroundColor = this.getTabBackGroundColor(true);
|
200 |
+
} else {
|
201 |
+
tab.style.backgroundColor = this.getTabBackGroundColor(false);
|
202 |
+
}
|
203 |
+
|
204 |
+
return tab;
|
205 |
+
},
|
206 |
+
|
207 |
+
buildTabContent: function (measurementData, title) {
|
208 |
+
measurementData = measurementData || {};
|
209 |
+
var node = this.buildEl('div');
|
210 |
+
node.appendChild(this.buildEl('h4', null, {innerHTML: title}));
|
211 |
+
if (measurementData.hasOwnProperty('description')) {
|
212 |
+
node.appendChild(this.buildEl('div', null, {innerHTML: measurementData.description}));
|
213 |
+
}
|
214 |
+
|
215 |
+
// Model description
|
216 |
+
node.appendChild(this.buildDescriptionBlock(measurementData));
|
217 |
+
|
218 |
+
// Size table
|
219 |
+
this.renderSizeChart(measurementData);
|
220 |
+
node.appendChild(this.sizeChartNode);
|
221 |
+
|
222 |
+
return node;
|
223 |
+
},
|
224 |
+
|
225 |
+
buildDescriptionBlock: function (measurementData) {
|
226 |
+
var node = this.buildEl('div', 'pf-size-guide-modal-measurements');
|
227 |
+
if (measurementData.hasOwnProperty('imageUrl') && measurementData.imageUrl) {
|
228 |
+
var measurementImgNode = this.buildEl('div', 'pf-size-guide-modal-measurements__image');
|
229 |
+
measurementImgNode.appendChild(this.buildEl('img', null, {src: measurementData.imageUrl}));
|
230 |
+
|
231 |
+
if (measurementData.hasOwnProperty('modelDescription')) {
|
232 |
+
measurementImgNode.appendChild(this.buildEl('div', null, {innerHTML: measurementData.modelDescription}));
|
233 |
+
}
|
234 |
+
node.appendChild(measurementImgNode);
|
235 |
+
}
|
236 |
+
|
237 |
+
if (measurementData.hasOwnProperty('imageDescription')) {
|
238 |
+
node.appendChild(this.buildEl('div', 'pf-size-guide-modal-measurements__description', {innerHTML: measurementData.imageDescription}));
|
239 |
+
}
|
240 |
+
node.append(this.buildEl('div', 'pf-size-guide-modal-clear'));
|
241 |
+
|
242 |
+
return node;
|
243 |
+
},
|
244 |
+
|
245 |
+
/**
|
246 |
+
* @param {{}} measurementData
|
247 |
+
* @param {string} [selectedUnit]
|
248 |
+
* @return {*}
|
249 |
+
*/
|
250 |
+
buildSizeChartBlock: function (measurementData, selectedUnit) {
|
251 |
+
var node = this.buildEl('div');
|
252 |
+
|
253 |
+
if (!measurementData.hasOwnProperty('sizeTableRows') || measurementData.sizeTableRows.length < 1) {
|
254 |
+
return node;
|
255 |
+
}
|
256 |
+
var sizeRows = measurementData.sizeTableRows,
|
257 |
+
availableUnits = this.getUniqueUnits(sizeRows);
|
258 |
+
|
259 |
+
// Selected or first if nothing selected
|
260 |
+
selectedUnit = selectedUnit || this.getDefaultUnit(availableUnits);
|
261 |
+
node.appendChild(this.buildSizeChartTabsNode(measurementData, availableUnits, selectedUnit));
|
262 |
+
var wrapper = this.buildEl('div');
|
263 |
+
wrapper.style.overflowX = 'auto';
|
264 |
+
wrapper.appendChild(this.buildSizeChartTable(this.getSortedChartRows(sizeRows, selectedUnit)));
|
265 |
+
node.appendChild(wrapper);
|
266 |
+
|
267 |
+
return node;
|
268 |
+
},
|
269 |
+
|
270 |
+
/**
|
271 |
+
* @param {{}} measurementData
|
272 |
+
* @param {Array<{key, title}>} availableUnits
|
273 |
+
* @param {string} selectedUnit
|
274 |
+
* @return {*}
|
275 |
+
*/
|
276 |
+
buildSizeChartTabsNode: function (measurementData, availableUnits, selectedUnit) {
|
277 |
+
availableUnits = availableUnits || [];
|
278 |
+
var tabsNode = this.buildEl('ul', 'pf-size-guide-modal-size-chart__tabs');
|
279 |
+
|
280 |
+
availableUnits.map((function (item) {
|
281 |
+
var className = 'pf-size-guide-modal-size-chart__tab';
|
282 |
+
if (item.key === selectedUnit) {
|
283 |
+
className += ' pf-size-guide-modal-size-chart__tab--active';
|
284 |
+
}
|
285 |
+
tabsNode.appendChild(this.buildEl('li', className, {
|
286 |
+
innerHTML: item.title,
|
287 |
+
onclick: (this.renderSizeChart).bind(this, measurementData, item.key)
|
288 |
+
}));
|
289 |
+
}).bind(this));
|
290 |
+
|
291 |
+
return tabsNode;
|
292 |
+
},
|
293 |
+
|
294 |
+
buildSizeChartTable: function (rows) {
|
295 |
+
var tableNode = this.buildEl('table', 'pf-size-guide-modal-size-chart__table'),
|
296 |
+
availableSizes = this.sizeGuideData.availableSizes;
|
297 |
+
|
298 |
+
var tableHeader = this.buildEl('thead'),
|
299 |
+
tableHeaderRow = this.buildEl('tr');
|
300 |
+
|
301 |
+
var tableHeaderRowText = window.pfGlobal && window.pfGlobal.sg_table_header_size ? window.pfGlobal.sg_table_header_size : 'Size';
|
302 |
+
|
303 |
+
tableHeaderRow.appendChild(this.buildEl('td', null, {innerHTML: tableHeaderRowText}));
|
304 |
+
rows.map((function (row) {
|
305 |
+
tableHeaderRow.appendChild(this.buildEl('td', null, {innerHTML: row.title}));
|
306 |
+
}).bind(this));
|
307 |
+
|
308 |
+
tableHeader.appendChild(tableHeaderRow);
|
309 |
+
tableNode.appendChild(tableHeader);
|
310 |
+
|
311 |
+
// Loop all available sizes and print out a row for each type
|
312 |
+
var tableBody = this.buildEl('tbody');
|
313 |
+
availableSizes.map((function (size) {
|
314 |
+
var tableBodyRow = this.buildEl('tr');
|
315 |
+
tableBodyRow.appendChild(this.buildEl('td', null, {innerHTML: size}));
|
316 |
+
rows.map((function (row) {
|
317 |
+
tableBodyRow.appendChild(this.buildEl('td', null, {innerHTML: row.sizes[size] || ''}));
|
318 |
+
}).bind(this));
|
319 |
+
|
320 |
+
tableBody.appendChild(tableBodyRow);
|
321 |
+
}).bind(this));
|
322 |
+
|
323 |
+
tableNode.appendChild(tableBody);
|
324 |
+
|
325 |
+
return tableNode;
|
326 |
+
},
|
327 |
+
|
328 |
+
getSortedChartRows: function (sizeTableRows, selectedUnit) {
|
329 |
+
return sizeTableRows
|
330 |
+
.filter(function (row) {
|
331 |
+
return row.unit === selectedUnit;
|
332 |
+
}).map((function (row) {
|
333 |
+
var sizes = {};
|
334 |
+
for (var k in row.sizes) {
|
335 |
+
if (!row.sizes.hasOwnProperty(k)) {
|
336 |
+
continue;
|
337 |
+
}
|
338 |
+
|
339 |
+
// Convert to fractions if needed, round and join
|
340 |
+
sizes[k] = row.sizes[k].map((function (size) {
|
341 |
+
if (row.unit === Printful_Product_Size_Guide.UNIT_INCH) {
|
342 |
+
return this.convertToFraction(size);
|
343 |
+
} else {
|
344 |
+
return size.toFixed(1);
|
345 |
+
}
|
346 |
+
}).bind(this)).join(' - ');
|
347 |
+
}
|
348 |
+
|
349 |
+
return {
|
350 |
+
title: row.title,
|
351 |
+
sizes: sizes
|
352 |
+
};
|
353 |
+
}).bind(this));
|
354 |
+
},
|
355 |
+
|
356 |
+
convertToFraction: function (value) {
|
357 |
+
var split = String(value).split('.'),
|
358 |
+
integer = parseInt(split[0], 10);
|
359 |
+
|
360 |
+
if (!split[1]) {
|
361 |
+
return value;
|
362 |
+
}
|
363 |
+
|
364 |
+
var decimal = parseFloat('0.' + split[1]),
|
365 |
+
fraction = 0;
|
366 |
+
|
367 |
+
for (var fra in Printful_Product_Size_Guide.FRACTION_MAP) {
|
368 |
+
var compareValue = Printful_Product_Size_Guide.FRACTION_MAP[fra];
|
369 |
+
|
370 |
+
if (decimal < compareValue) {
|
371 |
+
fraction = fra;
|
372 |
+
break;
|
373 |
+
}
|
374 |
+
}
|
375 |
+
|
376 |
+
if (fraction === 0 && integer) {
|
377 |
+
integer++;
|
378 |
+
fraction = '';
|
379 |
+
}
|
380 |
+
|
381 |
+
return (integer > 0 ? integer + ' ' : '') + fraction;
|
382 |
+
},
|
383 |
+
|
384 |
+
/**
|
385 |
+
* @param {Array<{unit, unitName}>}sizeTableRows
|
386 |
+
* @return {Array<{key, title}>}
|
387 |
+
*/
|
388 |
+
getUniqueUnits: function (sizeTableRows) {
|
389 |
+
var uniqueUnits = {};
|
390 |
+
sizeTableRows.map(function (row) {
|
391 |
+
// Gather unique units
|
392 |
+
if (!uniqueUnits.hasOwnProperty(row.unit)) {
|
393 |
+
uniqueUnits[row.unit] = row.unitName;
|
394 |
+
}
|
395 |
+
});
|
396 |
+
|
397 |
+
var units = [];
|
398 |
+
for (var i in uniqueUnits) {
|
399 |
+
if (uniqueUnits.hasOwnProperty(i)) {
|
400 |
+
units.push({
|
401 |
+
key: i,
|
402 |
+
title: this.getTranslatedUnitName(i, uniqueUnits[i])
|
403 |
+
});
|
404 |
+
}
|
405 |
+
}
|
406 |
+
|
407 |
+
return units;
|
408 |
+
},
|
409 |
+
|
410 |
+
/**
|
411 |
+
* @param {string} unit
|
412 |
+
* @param {string} defaultValue
|
413 |
+
* @return {null|string}
|
414 |
+
*/
|
415 |
+
getTranslatedUnitName: function(unit, defaultValue) {
|
416 |
+
if (this.translatedUnitNames[unit]) {
|
417 |
+
return this.translatedUnitNames[unit];
|
418 |
+
}
|
419 |
+
|
420 |
+
return defaultValue;
|
421 |
+
},
|
422 |
+
|
423 |
+
getBackGroundColor: function () {
|
424 |
+
return window.pfGlobal && window.pfGlobal.sg_modal_background_color ? window.pfGlobal.sg_modal_background_color : '#FFF';
|
425 |
+
},
|
426 |
+
|
427 |
+
getTextColor: function () {
|
428 |
+
return window.pfGlobal && window.pfGlobal.sg_modal_text_color ? window.pfGlobal.sg_modal_text_color : '#000';
|
429 |
+
},
|
430 |
+
|
431 |
+
getTabBackGroundColor: function (isActive) {
|
432 |
+
if (isActive) {
|
433 |
+
return window.pfGlobal && window.pfGlobal.sg_active_tab_background_color ? window.pfGlobal.sg_active_tab_background_color : '#FFF';
|
434 |
+
}
|
435 |
+
|
436 |
+
return window.pfGlobal && window.pfGlobal.sg_tab_background_color ? window.pfGlobal.sg_tab_background_color : '#EEE';
|
437 |
+
},
|
438 |
+
|
439 |
+
getDefaultUnit: function (availableUnits) {
|
440 |
+
availableUnits = availableUnits || [];
|
441 |
+
var uniqueUnitTypes = availableUnits.map(function (item) {
|
442 |
+
return item.key;
|
443 |
+
});
|
444 |
+
|
445 |
+
var defaultUnit = uniqueUnitTypes[0];
|
446 |
+
if (window.pfGlobal && window.pfGlobal.sg_primary_unit && uniqueUnitTypes.indexOf(window.pfGlobal.sg_primary_unit) > -1) {
|
447 |
+
return window.pfGlobal.sg_primary_unit;
|
448 |
+
}
|
449 |
+
|
450 |
+
return defaultUnit;
|
451 |
+
}
|
452 |
+
};
|
453 |
+
|
454 |
+
Printful_Product_Size_Guide.TAB_TYPE_PERSON = 'person';
|
455 |
+
Printful_Product_Size_Guide.TAB_TYPE_PRODUCT = 'product';
|
456 |
+
Printful_Product_Size_Guide.UNIT_INCH = 'inch';
|
457 |
+
|
458 |
+
Printful_Product_Size_Guide.FRACTION_MAP = {
|
459 |
+
' ': 0.0625, //don't add any fraction
|
460 |
+
'⅛': 0.187,
|
461 |
+
'¼': 0.3125,
|
462 |
+
'⅜': 0.4375,
|
463 |
+
'½': 0.5625,
|
464 |
+
'⅝': 0.6875,
|
465 |
+
'¾': 0.8125,
|
466 |
+
'⅞': 0.9375
|
467 |
+
};
|
468 |
+
})();
|
trunk/assets/js/settings.js
ADDED
@@ -0,0 +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 |
+
})();
|
trunk/i18n/languages/printful.pot
ADDED
@@ -0,0 +1,695 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#, fuzzy
|
2 |
+
msgid ""
|
3 |
+
msgstr ""
|
4 |
+
"Project-Id-Version: \n"
|
5 |
+
"POT-Creation-Date: 2020-09-07 18:56+0300\n"
|
6 |
+
"PO-Revision-Date: 2018-04-26 13:50+0300\n"
|
7 |
+
"Last-Translator: \n"
|
8 |
+
"Language-Team: \n"
|
9 |
+
"Language: en\n"
|
10 |
+
"MIME-Version: 1.0\n"
|
11 |
+
"Content-Type: text/plain; charset=UTF-8\n"
|
12 |
+
"Content-Transfer-Encoding: 8bit\n"
|
13 |
+
"X-Generator: Poedit 2.2.3\n"
|
14 |
+
"X-Poedit-Basepath: ../..\n"
|
15 |
+
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
16 |
+
"X-Poedit-KeywordsList: __;_e;_n;_x;_ex;_nx;esc_attr__;esc_attr_e;esc_attr_x;"
|
17 |
+
"esc_html__;esc_html_e;esc_html_x;_n_noop;_nx_noop;translate_nooped_plural\n"
|
18 |
+
"X-Poedit-SearchPath-0: .\n"
|
19 |
+
|
20 |
+
#: includes/class-printful-admin-dashboard.php:124
|
21 |
+
msgid "Loading your stats..."
|
22 |
+
msgstr ""
|
23 |
+
|
24 |
+
#: includes/class-printful-admin-dashboard.php:130
|
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 "
|
48 |
+
"products) that ship to %s. Before enabling, make sure you are registered for "
|
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"
|
163 |
+
" Uncheck the ones you want to disable. From your selection, our "
|
164 |
+
"algorithm will determine the fastest, most cost-effective, and most reliable "
|
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 |
+
|
180 |
+
#: includes/class-printful-admin-status.php:29
|
181 |
+
msgid "Connection to Printful API"
|
182 |
+
msgstr ""
|
183 |
+
|
184 |
+
#: includes/class-printful-admin-status.php:30
|
185 |
+
msgid "Is your store successfully connected to Printful API?"
|
186 |
+
msgstr ""
|
187 |
+
|
188 |
+
#: includes/class-printful-admin-status.php:34
|
189 |
+
msgid "Printful API key is set"
|
190 |
+
msgstr ""
|
191 |
+
|
192 |
+
#: includes/class-printful-admin-status.php:35
|
193 |
+
msgid ""
|
194 |
+
"Your store needs access to Printful API to use most of it's features like "
|
195 |
+
"shipping rates, tax rates and other settings."
|
196 |
+
msgstr ""
|
197 |
+
|
198 |
+
#: includes/class-printful-admin-status.php:39
|
199 |
+
msgid "WordPress Permalinks"
|
200 |
+
msgstr ""
|
201 |
+
|
202 |
+
#: includes/class-printful-admin-status.php:40
|
203 |
+
msgid ""
|
204 |
+
"WooCommerce API will not work unless your permalinks in Settings > "
|
205 |
+
"Permalinks are set up correctly. Make sure you that they are NOT set to "
|
206 |
+
"\"plain\"."
|
207 |
+
msgstr ""
|
208 |
+
|
209 |
+
#: includes/class-printful-admin-status.php:44
|
210 |
+
msgid "WordPress version"
|
211 |
+
msgstr ""
|
212 |
+
|
213 |
+
#: includes/class-printful-admin-status.php:45
|
214 |
+
msgid ""
|
215 |
+
"WordPress should always be updated to the latest version. Updates can be "
|
216 |
+
"installed from your WordPress admin dashboard."
|
217 |
+
msgstr ""
|
218 |
+
|
219 |
+
#: includes/class-printful-admin-status.php:49
|
220 |
+
msgid "WooCommerce Webhooks"
|
221 |
+
msgstr ""
|
222 |
+
|
223 |
+
#: includes/class-printful-admin-status.php:50
|
224 |
+
msgid ""
|
225 |
+
"Printful requires WooCommerce webhooks to be set up to quickly capture you "
|
226 |
+
"incoming orders, products updates etc."
|
227 |
+
msgstr ""
|
228 |
+
|
229 |
+
#: includes/class-printful-admin-status.php:54
|
230 |
+
msgid "WooCommerce API keys are set"
|
231 |
+
msgstr ""
|
232 |
+
|
233 |
+
#: includes/class-printful-admin-status.php:55
|
234 |
+
msgid ""
|
235 |
+
"Printful needs access to your WooCommerce API for the integration to work - "
|
236 |
+
"otherwise we can't sync your store, push or pull your products etc."
|
237 |
+
msgstr ""
|
238 |
+
|
239 |
+
#: includes/class-printful-admin-status.php:59
|
240 |
+
msgid "WooCommerce authentication URL access"
|
241 |
+
msgstr ""
|
242 |
+
|
243 |
+
#: includes/class-printful-admin-status.php:60
|
244 |
+
msgid ""
|
245 |
+
"Printful needs access to WooCommerce API authorize page. This sometimes may "
|
246 |
+
"get blocked due to hosts having unnecessarily intrusive security checks in "
|
247 |
+
"place that prevent WooCommerce API authentication from working (for example "
|
248 |
+
"mod_security rule #1234234). If this check fails, you will not be able "
|
249 |
+
"authorize Printful app."
|
250 |
+
msgstr ""
|
251 |
+
|
252 |
+
#: includes/class-printful-admin-status.php:64
|
253 |
+
msgid "WordPress remote requests"
|
254 |
+
msgstr ""
|
255 |
+
|
256 |
+
#: includes/class-printful-admin-status.php:65
|
257 |
+
msgid ""
|
258 |
+
"WordPress needs to be able to connect to Printful server to call webhooks. "
|
259 |
+
"If this check fails, contact your hosting support."
|
260 |
+
msgstr ""
|
261 |
+
|
262 |
+
#: includes/class-printful-admin-status.php:69
|
263 |
+
msgid "WordPress Site URL"
|
264 |
+
msgstr ""
|
265 |
+
|
266 |
+
#: includes/class-printful-admin-status.php:70
|
267 |
+
msgid ""
|
268 |
+
"If your currently setup WordPress site URL is redirected to another URL the "
|
269 |
+
"integration might not work correctly. Typically this happens with incorrect "
|
270 |
+
"http to https redirects. Go to Settings > General to fix this."
|
271 |
+
msgstr ""
|
272 |
+
|
273 |
+
#: includes/class-printful-admin-status.php:74
|
274 |
+
msgid "Recent store sync errors"
|
275 |
+
msgstr ""
|
276 |
+
|
277 |
+
#: includes/class-printful-admin-status.php:75
|
278 |
+
msgid ""
|
279 |
+
"Printful will connect to your store's API regularly and sync your latest "
|
280 |
+
"products, orders etc. If there have been any recent issues with sync, this "
|
281 |
+
"check will fail."
|
282 |
+
msgstr ""
|
283 |
+
|
284 |
+
#: includes/class-printful-admin-status.php:79
|
285 |
+
msgid "Write permissions"
|
286 |
+
msgstr ""
|
287 |
+
|
288 |
+
#: includes/class-printful-admin-status.php:80
|
289 |
+
msgid ""
|
290 |
+
"Make the uploads directory writable. This is required for mockup generator "
|
291 |
+
"product push to work correctly. Contact your hosting provider if you need "
|
292 |
+
"help with this."
|
293 |
+
msgstr ""
|
294 |
+
|
295 |
+
#: includes/class-printful-admin-status.php:84
|
296 |
+
msgid "PHP memory limit"
|
297 |
+
msgstr ""
|
298 |
+
|
299 |
+
#: includes/class-printful-admin-status.php:85
|
300 |
+
msgid ""
|
301 |
+
"Set PHP allocated memory limit to at least 128mb. Contact your hosting "
|
302 |
+
"provider if you need help with this."
|
303 |
+
msgstr ""
|
304 |
+
|
305 |
+
#: includes/class-printful-admin-status.php:89
|
306 |
+
msgid "PHP script time limit"
|
307 |
+
msgstr ""
|
308 |
+
|
309 |
+
#: includes/class-printful-admin-status.php:90
|
310 |
+
msgid ""
|
311 |
+
"Set PHP script execution time limit to at least 30 seconds. This is required "
|
312 |
+
"to successfully push products with many variants. Contact your hosting "
|
313 |
+
"provider if you need help with this."
|
314 |
+
msgstr ""
|
315 |
+
|
316 |
+
#: includes/class-printful-admin-status.php:94
|
317 |
+
msgid "W3 Total Cache DB Cache"
|
318 |
+
msgstr ""
|
319 |
+
|
320 |
+
#: includes/class-printful-admin-status.php:95
|
321 |
+
msgid ""
|
322 |
+
"If you are using W3 Total Cache, the database caching feature needs to be "
|
323 |
+
"disabled since it can cause issues with product push to store."
|
324 |
+
msgstr ""
|
325 |
+
|
326 |
+
#: includes/class-printful-admin-status.php:100
|
327 |
+
msgid "WP SpamShield"
|
328 |
+
msgstr ""
|
329 |
+
|
330 |
+
#: includes/class-printful-admin-status.php:101
|
331 |
+
msgid ""
|
332 |
+
"If you are using WP SpamShield, you might experience problems connecting to "
|
333 |
+
"Printful and pushing products."
|
334 |
+
msgstr ""
|
335 |
+
|
336 |
+
#: includes/class-printful-admin-status.php:106
|
337 |
+
msgid "Remove Print Aura plugin"
|
338 |
+
msgstr ""
|
339 |
+
|
340 |
+
#: includes/class-printful-admin-status.php:107
|
341 |
+
msgid "Print Aura plugin is known to cause issues so it needs to be removed."
|
342 |
+
msgstr ""
|
343 |
+
|
344 |
+
#: includes/class-printful-admin-status.php:148
|
345 |
+
msgid "Testing your store (this may take up to 30 seconds)..."
|
346 |
+
msgstr ""
|
347 |
+
|
348 |
+
#: includes/class-printful-admin-support.php:36
|
349 |
+
msgid "Building support report (this may take up to 30 seconds)..."
|
350 |
+
msgstr ""
|
351 |
+
|
352 |
+
#: includes/class-printful-admin-support.php:69
|
353 |
+
msgid "##### Printful Checklist #####\n"
|
354 |
+
msgstr ""
|
355 |
+
|
356 |
+
#: includes/class-printful-admin.php:75 includes/class-printful-admin.php:117
|
357 |
+
msgid "Dashboard"
|
358 |
+
msgstr ""
|
359 |
+
|
360 |
+
#: includes/class-printful-admin.php:111
|
361 |
+
msgid "Settings"
|
362 |
+
msgstr ""
|
363 |
+
|
364 |
+
#: includes/class-printful-admin.php:112 includes/templates/order-table.php:12
|
365 |
+
#: includes/templates/order-table.php:58 includes/templates/status-table.php:21
|
366 |
+
#: includes/templates/status-table.php:51
|
367 |
+
msgid "Status"
|
368 |
+
msgstr ""
|
369 |
+
|
370 |
+
#: includes/class-printful-admin.php:113
|
371 |
+
msgid "Support"
|
372 |
+
msgstr ""
|
373 |
+
|
374 |
+
#: includes/class-printful-admin.php:119 includes/templates/connect.php:29
|
375 |
+
msgid "Connect"
|
376 |
+
msgstr ""
|
377 |
+
|
378 |
+
#: includes/class-printful-admin.php:147
|
379 |
+
msgid " issue"
|
380 |
+
msgstr ""
|
381 |
+
|
382 |
+
#: includes/class-printful-rest-api-controller.php:36
|
383 |
+
msgid "Printful access key"
|
384 |
+
msgstr ""
|
385 |
+
|
386 |
+
#: includes/class-printful-rest-api-controller.php:41
|
387 |
+
msgid "Store Identifier"
|
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 |
+
|
395 |
+
#: includes/class-printful-rest-api-controller.php:61
|
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 |
+
|
427 |
+
#: includes/class-printful-shipping.php:51
|
428 |
+
msgid "Enable/Disable"
|
429 |
+
msgstr ""
|
430 |
+
|
431 |
+
#: includes/class-printful-shipping.php:53
|
432 |
+
msgid "Enable this shipping method"
|
433 |
+
msgstr ""
|
434 |
+
|
435 |
+
#: includes/class-printful-shipping.php:57
|
436 |
+
msgid "Disable Woocommerce rates"
|
437 |
+
msgstr ""
|
438 |
+
|
439 |
+
#: includes/class-printful-shipping.php:59
|
440 |
+
msgid "Disable standard Woocommerce rates for products fulfilled by Printful"
|
441 |
+
msgstr ""
|
442 |
+
|
443 |
+
#: includes/class-printful-shipping.php:63
|
444 |
+
msgid "Show Printful warnings"
|
445 |
+
msgstr ""
|
446 |
+
|
447 |
+
#: includes/class-printful-shipping.php:65
|
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-size-guide.php:53
|
469 |
+
msgid "Size"
|
470 |
+
msgstr ""
|
471 |
+
|
472 |
+
#: includes/class-printful-taxes.php:160
|
473 |
+
msgid "Sales Tax"
|
474 |
+
msgstr ""
|
475 |
+
|
476 |
+
#: includes/templates/connect.php:5
|
477 |
+
msgid "Connect to Printful"
|
478 |
+
msgstr ""
|
479 |
+
|
480 |
+
#: includes/templates/connect.php:12
|
481 |
+
msgid "To connect your store to Printful, fix the following errors:"
|
482 |
+
msgstr ""
|
483 |
+
|
484 |
+
#: includes/templates/connect.php:25
|
485 |
+
msgid ""
|
486 |
+
"You're almost done! Just 2 more steps to have your WooCommerce store "
|
487 |
+
"connected to Printful for automatic order fulfillment."
|
488 |
+
msgstr ""
|
489 |
+
|
490 |
+
#: includes/templates/error.php:1
|
491 |
+
msgid "Error:"
|
492 |
+
msgstr ""
|
493 |
+
|
494 |
+
#: includes/templates/order-table.php:9 includes/templates/order-table.php:55
|
495 |
+
msgid "Order"
|
496 |
+
msgstr ""
|
497 |
+
|
498 |
+
#: includes/templates/order-table.php:10 includes/templates/order-table.php:56
|
499 |
+
msgid "Date"
|
500 |
+
msgstr ""
|
501 |
+
|
502 |
+
#: includes/templates/order-table.php:11 includes/templates/order-table.php:57
|
503 |
+
msgid "From"
|
504 |
+
msgstr ""
|
505 |
+
|
506 |
+
#: includes/templates/order-table.php:13 includes/templates/order-table.php:59
|
507 |
+
msgid "Total"
|
508 |
+
msgstr ""
|
509 |
+
|
510 |
+
#: includes/templates/order-table.php:14 includes/templates/order-table.php:60
|
511 |
+
msgid "Actions"
|
512 |
+
msgstr ""
|
513 |
+
|
514 |
+
#: includes/templates/order-table.php:46
|
515 |
+
msgid "Open in Printful"
|
516 |
+
msgstr ""
|
517 |
+
|
518 |
+
#: includes/templates/order-table.php:67
|
519 |
+
msgid ""
|
520 |
+
"Once your store gets some Printful product orders, they will be shown here!"
|
521 |
+
msgstr ""
|
522 |
+
|
523 |
+
#: includes/templates/quick-links.php:6
|
524 |
+
msgid "Orders"
|
525 |
+
msgstr ""
|
526 |
+
|
527 |
+
#: includes/templates/quick-links.php:10
|
528 |
+
msgid "File library"
|
529 |
+
msgstr ""
|
530 |
+
|
531 |
+
#: includes/templates/quick-links.php:14
|
532 |
+
msgid "Stores"
|
533 |
+
msgstr ""
|
534 |
+
|
535 |
+
#: includes/templates/quick-links.php:18
|
536 |
+
msgid "Reports"
|
537 |
+
msgstr ""
|
538 |
+
|
539 |
+
#: includes/templates/quick-links.php:22
|
540 |
+
msgid "My Account"
|
541 |
+
msgstr ""
|
542 |
+
|
543 |
+
#: includes/templates/quick-links.php:26
|
544 |
+
msgid "Billing"
|
545 |
+
msgstr ""
|
546 |
+
|
547 |
+
#: includes/templates/quick-links.php:30
|
548 |
+
msgid "Notifications"
|
549 |
+
msgstr ""
|
550 |
+
|
551 |
+
#: includes/templates/setting-submit.php:7
|
552 |
+
msgid "Save changes"
|
553 |
+
msgstr ""
|
554 |
+
|
555 |
+
#: includes/templates/setting-submit.php:14
|
556 |
+
msgid "Saved successfully"
|
557 |
+
msgstr ""
|
558 |
+
|
559 |
+
#: includes/templates/shipping-notification.php:2
|
560 |
+
msgid "Printful Shipping"
|
561 |
+
msgstr ""
|
562 |
+
|
563 |
+
#: includes/templates/shipping-notification.php:3
|
564 |
+
msgid "To enable/disable Printful shipping for your store go to"
|
565 |
+
msgstr ""
|
566 |
+
|
567 |
+
#: includes/templates/shipping-notification.php:3
|
568 |
+
msgid "WooCommerce Shipping settings"
|
569 |
+
msgstr ""
|
570 |
+
|
571 |
+
#: includes/templates/stats.php:7 includes/templates/stats.php:20
|
572 |
+
#: includes/templates/stats.php:33
|
573 |
+
msgid "ORDER"
|
574 |
+
msgstr ""
|
575 |
+
|
576 |
+
#: includes/templates/stats.php:10
|
577 |
+
msgid "today"
|
578 |
+
msgstr ""
|
579 |
+
|
580 |
+
#: includes/templates/stats.php:23
|
581 |
+
msgid "last 7 days"
|
582 |
+
msgstr ""
|
583 |
+
|
584 |
+
#: includes/templates/stats.php:35 includes/templates/stats.php:42
|
585 |
+
msgid "last 28 days"
|
586 |
+
msgstr ""
|
587 |
+
|
588 |
+
#: includes/templates/stats.php:42
|
589 |
+
msgid "PROFIT"
|
590 |
+
msgstr ""
|
591 |
+
|
592 |
+
#: includes/templates/status-report.php:3
|
593 |
+
msgid "Copy the box content below and add it to your support message"
|
594 |
+
msgstr ""
|
595 |
+
|
596 |
+
#: includes/templates/status-report.php:5
|
597 |
+
msgid ""
|
598 |
+
"Note: this status report may not include an error log. Contact your hosting "
|
599 |
+
"provider if you need help with acquiring error logs."
|
600 |
+
msgstr ""
|
601 |
+
|
602 |
+
#: includes/templates/status-report.php:8
|
603 |
+
msgid "Copy"
|
604 |
+
msgstr ""
|
605 |
+
|
606 |
+
#: includes/templates/status-table.php:4
|
607 |
+
msgid ""
|
608 |
+
"Looks like the everything is set up correctly and Printful integration "
|
609 |
+
"should work as intended."
|
610 |
+
msgstr ""
|
611 |
+
|
612 |
+
#: includes/templates/status-table.php:10
|
613 |
+
msgid ""
|
614 |
+
"There are errors with your store setup that may cause the Printful "
|
615 |
+
"integration to not work as intended!"
|
616 |
+
msgstr ""
|
617 |
+
|
618 |
+
#: includes/templates/status-table.php:19
|
619 |
+
#: includes/templates/status-table.php:49
|
620 |
+
msgid "Name"
|
621 |
+
msgstr ""
|
622 |
+
|
623 |
+
#: includes/templates/status-table.php:20
|
624 |
+
#: includes/templates/status-table.php:50
|
625 |
+
msgid "Description"
|
626 |
+
msgstr ""
|
627 |
+
|
628 |
+
#: includes/templates/status-table.php:34
|
629 |
+
msgid "OK"
|
630 |
+
msgstr ""
|
631 |
+
|
632 |
+
#: includes/templates/status-table.php:36
|
633 |
+
msgid "WARNING"
|
634 |
+
msgstr ""
|
635 |
+
|
636 |
+
#: includes/templates/status-table.php:38
|
637 |
+
msgid "NOT CONNECTED"
|
638 |
+
msgstr ""
|
639 |
+
|
640 |
+
#: includes/templates/status-table.php:40
|
641 |
+
msgid "FAIL"
|
642 |
+
msgstr ""
|
643 |
+
|
644 |
+
#: includes/templates/status-table.php:56
|
645 |
+
msgid ""
|
646 |
+
"Warnings are issued when the test was unable to come to a definite "
|
647 |
+
"conclusion or if the result was passable, but not ideal."
|
648 |
+
msgstr ""
|
649 |
+
|
650 |
+
#: includes/templates/support-info.php:4
|
651 |
+
msgid "Need help? Get in touch!"
|
652 |
+
msgstr ""
|
653 |
+
|
654 |
+
#: includes/templates/support-info.php:5
|
655 |
+
msgid ""
|
656 |
+
"Email, call, text, or visit us. We've got support teams in the USA and "
|
657 |
+
"Europe to make sure we're there when you need us."
|
658 |
+
msgstr ""
|
659 |
+
|
660 |
+
#: includes/templates/support-info.php:5
|
661 |
+
msgid "We'd love to hear from you."
|
662 |
+
msgstr ""
|
663 |
+
|
664 |
+
#: includes/templates/support-info.php:7
|
665 |
+
msgid "Contact support"
|
666 |
+
msgstr ""
|
667 |
+
|
668 |
+
#: includes/templates/support-info.php:12
|
669 |
+
msgid "Read our FAQs"
|
670 |
+
msgstr ""
|
671 |
+
|
672 |
+
#: includes/templates/support-info.php:13
|
673 |
+
msgid "Getting started made easy – read the FAQs to jumpstart your business."
|
674 |
+
msgstr ""
|
675 |
+
|
676 |
+
#: includes/templates/support-info.php:13
|
677 |
+
msgid ""
|
678 |
+
"Whether you're a video tutorial fan or prefer the written answers – we've "
|
679 |
+
"got it covered!"
|
680 |
+
msgstr ""
|
681 |
+
|
682 |
+
#: includes/templates/support-info.php:15
|
683 |
+
msgid "See Printful FAQ"
|
684 |
+
msgstr ""
|
685 |
+
|
686 |
+
#: includes/templates/support-info.php:20
|
687 |
+
#: includes/templates/support-info.php:23
|
688 |
+
msgid "Integration help"
|
689 |
+
msgstr ""
|
690 |
+
|
691 |
+
#: includes/templates/support-info.php:21
|
692 |
+
msgid ""
|
693 |
+
"Are you experiencing technical issues? Solve them on your own by reading "
|
694 |
+
"these helpful guides and video tutorials."
|
695 |
+
msgstr ""
|
trunk/includes/class-printful-admin-dashboard.php
ADDED
@@ -0,0 +1,270 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
if ( ! defined( 'ABSPATH' ) ) exit;
|
3 |
+
|
4 |
+
class Printful_Admin_Dashboard {
|
5 |
+
|
6 |
+
const API_KEY_SEARCH_STRING = 'Printful';
|
7 |
+
|
8 |
+
public static $_instance;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* @return Printful_Admin_Dashboard
|
12 |
+
*/
|
13 |
+
public static function instance() {
|
14 |
+
|
15 |
+
if ( is_null( self::$_instance ) ) {
|
16 |
+
self::$_instance = new self();
|
17 |
+
}
|
18 |
+
|
19 |
+
return self::$_instance;
|
20 |
+
}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Printful_Admin_Dashboard constructor.
|
24 |
+
*/
|
25 |
+
function __construct() {
|
26 |
+
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Show the view
|
31 |
+
* @throws PrintfulException
|
32 |
+
*/
|
33 |
+
public static function view() {
|
34 |
+
|
35 |
+
$dashboard = self::instance();
|
36 |
+
$api_key = Printful_Integration::instance()->get_option( 'printful_key' );
|
37 |
+
$connect_status = Printful_Integration::instance()->is_connected();
|
38 |
+
|
39 |
+
if ( $connect_status ) {
|
40 |
+
$dashboard->render_dashboard();
|
41 |
+
} else if(!$connect_status && strlen($api_key) > 0) {
|
42 |
+
$dashboard->render_connect_error();
|
43 |
+
} else {
|
44 |
+
$dashboard->render_connect();
|
45 |
+
}
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Display the Printful connect page
|
50 |
+
*/
|
51 |
+
public function render_connect() {
|
52 |
+
|
53 |
+
$status = Printful_Admin_Status::instance();
|
54 |
+
$issues = array();
|
55 |
+
|
56 |
+
$permalinks_set = $status->run_single_test( 'check_permalinks' );
|
57 |
+
|
58 |
+
if ( $permalinks_set == Printful_Admin_Status::PF_STATUS_FAIL ) {
|
59 |
+
$message = 'WooCommerce API will not work unless your permalinks are set up correctly. Go to <a href="%s">Permalinks settings</a> and make sure that they are NOT set to "plain".';
|
60 |
+
$settings_url = admin_url( 'options-permalink.php' );
|
61 |
+
$issues[] = sprintf( $message, $settings_url );
|
62 |
+
}
|
63 |
+
|
64 |
+
if ( strpos( get_site_url(), 'localhost' ) ) {
|
65 |
+
$issues[] = 'You can\'t connect to Printful from localhost. Printful needs to be able reach your site to establish a connection.';
|
66 |
+
}
|
67 |
+
|
68 |
+
Printful_Admin::load_template( 'header', array( 'tabs' => Printful_Admin::get_tabs() ) );
|
69 |
+
|
70 |
+
Printful_Admin::load_template( 'connect', array(
|
71 |
+
'consumer_key' => $this->_get_consumer_key(),
|
72 |
+
'waiting_sync' => isset( $_GET['sync-in-progress'] ),
|
73 |
+
'consumer_key_error' => isset( $_GET['consumer-key-error'] ),
|
74 |
+
'issues' => $issues,
|
75 |
+
)
|
76 |
+
);
|
77 |
+
|
78 |
+
if ( isset( $_GET['sync-in-progress'] ) ) {
|
79 |
+
$emit_auth_response = 'Printful_Connect.send_return_message();';
|
80 |
+
Printful_Admin::load_template( 'inline-script', array( 'script' => $emit_auth_response ) );
|
81 |
+
}
|
82 |
+
|
83 |
+
Printful_Admin::load_template('footer');
|
84 |
+
}
|
85 |
+
|
86 |
+
/**
|
87 |
+
* Display the Printful connect error page
|
88 |
+
*/
|
89 |
+
public function render_connect_error() {
|
90 |
+
|
91 |
+
Printful_Admin::load_template( 'header', array( 'tabs' => Printful_Admin::get_tabs() ) );
|
92 |
+
|
93 |
+
$connect_error = Printful_Integration::instance()->get_connect_error();
|
94 |
+
if ( $connect_error ) {
|
95 |
+
Printful_Admin::load_template('error', array('error' => $connect_error));
|
96 |
+
}
|
97 |
+
|
98 |
+
Printful_Admin::load_template('footer');
|
99 |
+
}
|
100 |
+
|
101 |
+
/**
|
102 |
+
* Display the dashboard
|
103 |
+
*/
|
104 |
+
public function render_dashboard() {
|
105 |
+
|
106 |
+
Printful_Admin::load_template( 'header', array( 'tabs' => Printful_Admin::get_tabs() ) );
|
107 |
+
|
108 |
+
$stats = $this->_get_stats(true);
|
109 |
+
$orders = $this->_get_orders(true);
|
110 |
+
$error = false;
|
111 |
+
|
112 |
+
if ( is_wp_error( $stats ) ) {
|
113 |
+
$error = $stats;
|
114 |
+
}
|
115 |
+
if ( is_wp_error( $orders ) ) {
|
116 |
+
$error = $orders;
|
117 |
+
}
|
118 |
+
|
119 |
+
if ( ! $error ) {
|
120 |
+
|
121 |
+
if ( $stats ) {
|
122 |
+
Printful_Admin::load_template( 'stats', array( 'stats' => $stats ) );
|
123 |
+
} else {
|
124 |
+
Printful_Admin::load_template( 'ajax-loader', array( 'action' => 'get_printful_stats', 'message' => __( 'Loading your stats...', 'printful' ) ) );
|
125 |
+
}
|
126 |
+
|
127 |
+
if ($orders) {
|
128 |
+
Printful_Admin::load_template( 'order-table', array( 'orders' => $orders ) );
|
129 |
+
} else {
|
130 |
+
Printful_Admin::load_template( 'ajax-loader', array( 'action' => 'get_printful_orders', 'message' => __( 'Loading your orders...', 'printful' ) ) );
|
131 |
+
}
|
132 |
+
|
133 |
+
} else {
|
134 |
+
Printful_Admin::load_template( 'error', array( 'error' => $error->get_error_message('printful') ) );
|
135 |
+
}
|
136 |
+
|
137 |
+
Printful_Admin::load_template( 'quick-links' );
|
138 |
+
|
139 |
+
if ( isset( $_GET['sync-in-progress'] ) ) {
|
140 |
+
$emit_auth_response = 'Printful_Connect.send_return_message();';
|
141 |
+
Printful_Admin::load_template( 'inline-script', array( 'script' => $emit_auth_response ) );
|
142 |
+
}
|
143 |
+
|
144 |
+
Printful_Admin::load_template( 'footer' );
|
145 |
+
}
|
146 |
+
|
147 |
+
/**
|
148 |
+
* Ajax response for stats block
|
149 |
+
*/
|
150 |
+
public static function render_stats_ajax() {
|
151 |
+
|
152 |
+
$stats = self::instance()->_get_stats();
|
153 |
+
|
154 |
+
if ( ! empty( $stats ) && ! is_wp_error( $stats ) ) {
|
155 |
+
Printful_Admin::load_template( 'stats', array( 'stats' => $stats ) );
|
156 |
+
} else {
|
157 |
+
Printful_Admin::load_template( 'error', array( 'error' => $stats->get_error_message( 'printful' ) ) );
|
158 |
+
}
|
159 |
+
|
160 |
+
exit;
|
161 |
+
}
|
162 |
+
|
163 |
+
/**
|
164 |
+
* Ajax response for stats block
|
165 |
+
*/
|
166 |
+
public static function render_orders_ajax() {
|
167 |
+
|
168 |
+
$orders = self::instance()->_get_orders();
|
169 |
+
|
170 |
+
if ( ! empty( $orders ) && is_wp_error( $orders ) ) {
|
171 |
+
Printful_Admin::load_template( 'error', array( 'error' => $orders->get_error_message('printful') ) );
|
172 |
+
} else {
|
173 |
+
Printful_Admin::load_template( 'order-table', array( 'orders' => $orders ) );
|
174 |
+
}
|
175 |
+
|
176 |
+
exit;
|
177 |
+
}
|
178 |
+
|
179 |
+
/**
|
180 |
+
* Get store statistics from API
|
181 |
+
* @param bool $only_cached_results
|
182 |
+
* @return mixed
|
183 |
+
*/
|
184 |
+
private function _get_stats($only_cached_results = false) {
|
185 |
+
|
186 |
+
$stats = get_transient( 'printful_stats' );
|
187 |
+
if ( $only_cached_results || $stats ) {
|
188 |
+
return $stats;
|
189 |
+
}
|
190 |
+
|
191 |
+
try {
|
192 |
+
$stats = Printful_Integration::instance()->get_client()->get( 'store/statistics' );
|
193 |
+
if ( ! empty( $stats['store_statistics'] ) ) {
|
194 |
+
$stats = $stats['store_statistics'];
|
195 |
+
}
|
196 |
+
set_transient( 'printful_stats', $stats, MINUTE_IN_SECONDS * 5 ); //cache for 5 minute
|
197 |
+
} catch (PrintfulApiException $e) {
|
198 |
+
return new WP_Error('printful', 'Could not connect to Printful API. Please try again later!');
|
199 |
+
} catch (PrintfulException $e) {
|
200 |
+
return new WP_Error('printful', 'Could not connect to Printful API. Please try again later!');
|
201 |
+
}
|
202 |
+
|
203 |
+
return $stats;
|
204 |
+
}
|
205 |
+
|
206 |
+
/**
|
207 |
+
* Get Printful orders from the API
|
208 |
+
* @param bool $only_cached_results
|
209 |
+
* @return mixed
|
210 |
+
*/
|
211 |
+
private function _get_orders($only_cached_results = false) {
|
212 |
+
|
213 |
+
$orders = get_transient( 'printful_orders' );
|
214 |
+
|
215 |
+
if ( $only_cached_results || $orders ) {
|
216 |
+
return $orders;
|
217 |
+
}
|
218 |
+
|
219 |
+
try {
|
220 |
+
$order_data = Printful_Integration::instance()->get_client()->get( 'orders' );
|
221 |
+
|
222 |
+
if ( ! empty( $order_data ) ) {
|
223 |
+
|
224 |
+
foreach ( $order_data as $key => $order ) {
|
225 |
+
|
226 |
+
if($order['status'] == 'pending') {
|
227 |
+
$order_data[$key]['status'] = 'Waiting for fulfillment';
|
228 |
+
}
|
229 |
+
}
|
230 |
+
}
|
231 |
+
|
232 |
+
$orders = array( 'count' => count( $order_data ), 'results' => $order_data );
|
233 |
+
set_transient( 'printful_orders', $orders, MINUTE_IN_SECONDS * 5 ); //cache for 5 minute
|
234 |
+
} catch (PrintfulApiException $e) {
|
235 |
+
return new WP_Error('printful', 'Could not connect to Printful API. Please try again later!');
|
236 |
+
} catch (PrintfulException $e) {
|
237 |
+
return new WP_Error('printful', 'Could not connect to Printful API. Please try again later!');
|
238 |
+
}
|
239 |
+
|
240 |
+
return $orders;
|
241 |
+
}
|
242 |
+
|
243 |
+
/**
|
244 |
+
* Get the last used consumer key fragment and use it for validating the address
|
245 |
+
* @return null|string
|
246 |
+
*/
|
247 |
+
private function _get_consumer_key() {
|
248 |
+
|
249 |
+
global $wpdb;
|
250 |
+
|
251 |
+
// Get the API key
|
252 |
+
$printfulKey = '%' . esc_sql( $wpdb->esc_like( wc_clean( self::API_KEY_SEARCH_STRING ) ) ) . '%';
|
253 |
+
$consumer_key = $wpdb->get_var(
|
254 |
+
$wpdb->prepare(
|
255 |
+
"SELECT truncated_key FROM {$wpdb->prefix}woocommerce_api_keys WHERE description LIKE %s ORDER BY key_id DESC LIMIT 1",
|
256 |
+
$printfulKey
|
257 |
+
)
|
258 |
+
);
|
259 |
+
|
260 |
+
//if not found by description, it was probably manually created. try the last used key instead
|
261 |
+
if ( ! $consumer_key ) {
|
262 |
+
$consumer_key = $wpdb->get_var(
|
263 |
+
"SELECT truncated_key FROM {$wpdb->prefix}woocommerce_api_keys ORDER BY key_id DESC LIMIT 1"
|
264 |
+
);
|
265 |
+
}
|
266 |
+
|
267 |
+
return $consumer_key;
|
268 |
+
}
|
269 |
+
|
270 |
+
}
|
trunk/includes/class-printful-admin-settings.php
ADDED
@@ -0,0 +1,466 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
if ( ! defined( 'ABSPATH' ) ) exit;
|
3 |
+
|
4 |
+
class Printful_Admin_Settings {
|
5 |
+
|
6 |
+
public static $_instance;
|
7 |
+
const CARRIER_TYPE_STANDARD = 'standard';
|
8 |
+
const CARRIER_TYPE_EXPEDITED = 'expedited';
|
9 |
+
const CARRIER_TYPE_DOMESTIC = 'domestic';
|
10 |
+
const CARRIER_TYPE_INTERNATIONAL = 'international';
|
11 |
+
const CARRIER_REGION_US = 'US';
|
12 |
+
const CARRIER_REGION_EU = 'LV';
|
13 |
+
const DEFAULT_PERSONALIZE_BUTTON_TEXT = 'Personalize Design';
|
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 |
+
*/
|
30 |
+
public static function getIntegrationFields()
|
31 |
+
{
|
32 |
+
$sales_tax_link = sprintf(
|
33 |
+
'<a href="%s" target="_blank">%s</a>',
|
34 |
+
esc_url( 'https://www.printful.com/faq/taxes-and-billing/sales-tax/371-which-states-does-printful-charge-sales-tax-in-' ),
|
35 |
+
esc_html__( 'states where Printful applies sales tax', 'printful' )
|
36 |
+
);
|
37 |
+
|
38 |
+
return array(
|
39 |
+
'printful_key' => array(
|
40 |
+
'title' => __( 'Printful store API key', 'printful' ),
|
41 |
+
'type' => 'text',
|
42 |
+
'desc_tip' => true,
|
43 |
+
'description' => __( 'Your store\'s Printful API key. Create it in the Prinful dashboard', 'printful' ),
|
44 |
+
'default' => false,
|
45 |
+
),
|
46 |
+
'calculate_tax' => array(
|
47 |
+
'title' => __( 'Calculate sales tax', 'printful' ),
|
48 |
+
'type' => 'checkbox',
|
49 |
+
'label' => sprintf(
|
50 |
+
__('Calculated for all products listed on your store (including non-Printful products) that ship to %s. Before enabling, make sure you are registered for a seller permit in all these states.'),
|
51 |
+
$sales_tax_link
|
52 |
+
),
|
53 |
+
'default' => 'no',
|
54 |
+
),
|
55 |
+
'disable_ssl' => array(
|
56 |
+
'title' => __( 'Disable SSL', 'printful' ),
|
57 |
+
'type' => 'checkbox',
|
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',
|
82 |
+
'description' => __( 'Personalization popup title text', 'printful' ),
|
83 |
+
'default' => self::DEFAULT_PERSONALIZE_MODAL_TITLE,
|
84 |
+
),
|
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 |
+
*/
|
157 |
+
public static function instance() {
|
158 |
+
|
159 |
+
if ( is_null( self::$_instance ) ) {
|
160 |
+
self::$_instance = new self();
|
161 |
+
}
|
162 |
+
|
163 |
+
return self::$_instance;
|
164 |
+
}
|
165 |
+
|
166 |
+
/**
|
167 |
+
* Setup the view
|
168 |
+
*/
|
169 |
+
public static function view() {
|
170 |
+
|
171 |
+
$settings = self::instance();
|
172 |
+
$settings->render();
|
173 |
+
}
|
174 |
+
|
175 |
+
/**
|
176 |
+
* Display the view
|
177 |
+
*/
|
178 |
+
public function render() {
|
179 |
+
|
180 |
+
Printful_Admin::load_template( 'header', array( 'tabs' => Printful_Admin::get_tabs() ) );
|
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
|
203 |
+
Printful_Admin::load_template( 'ajax-loader', array( 'action' => 'get_printful_carriers', 'message' => 'Loading your carriers...' ) );
|
204 |
+
|
205 |
+
Printful_Admin::load_template( 'setting-submit', array( 'nonce' => wp_create_nonce( 'printful_settings' ), 'disabled' => true ) );
|
206 |
+
|
207 |
+
echo '</form>';
|
208 |
+
|
209 |
+
Printful_Admin::load_template( 'footer' );
|
210 |
+
}
|
211 |
+
|
212 |
+
/**
|
213 |
+
* Display the ajax content for carrier settings
|
214 |
+
* @throws PrintfulException
|
215 |
+
*/
|
216 |
+
public static function render_carriers_ajax() {
|
217 |
+
|
218 |
+
$carrier_settings = self::instance()->setup_carrier_fields();
|
219 |
+
Printful_Admin::load_template( 'setting-group', $carrier_settings );
|
220 |
+
$enable_submit = 'Printful_Settings.enable_submit_btn();';
|
221 |
+
Printful_Admin::load_template( 'inline-script', array( 'script' => $enable_submit ) );
|
222 |
+
|
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 |
+
/**
|
251 |
+
* @internal param $carrier_settings
|
252 |
+
* @throws PrintfulException
|
253 |
+
*/
|
254 |
+
public function setup_carrier_fields() {
|
255 |
+
$carrier_settings = array(
|
256 |
+
'title' => __( 'Shipping Methods', 'printful' ),
|
257 |
+
'description' => __( 'Here you can choose the shipping methods you want Printful to use for shipping your orders.
|
258 |
+
Uncheck the ones you want to disable. From your selection, our algorithm will determine the fastest, most cost-effective, and most reliable method for each order.', 'printful' ),
|
259 |
+
'settings' => array(),
|
260 |
+
);
|
261 |
+
|
262 |
+
if ( ! Printful_Integration::instance()->is_connected() ) {
|
263 |
+
$carrier_settings['description'] = __( 'You need to be connected to Printful API to edit carrier settings!', 'printful' );
|
264 |
+
|
265 |
+
return $carrier_settings;
|
266 |
+
}
|
267 |
+
|
268 |
+
$carrier_regions = Printful_Carriers::instance()->carriers;
|
269 |
+
|
270 |
+
if ( empty( $carrier_regions ) ) {
|
271 |
+
return false;
|
272 |
+
}
|
273 |
+
|
274 |
+
$carrier_settings[ 'settings' ] = $this->prepare_form_data( $carrier_regions );
|
275 |
+
|
276 |
+
return $carrier_settings;
|
277 |
+
}
|
278 |
+
|
279 |
+
/**
|
280 |
+
* Prepare carrier data for posting to Printful API
|
281 |
+
* @return array|bool
|
282 |
+
*/
|
283 |
+
public function prepare_carriers() {
|
284 |
+
|
285 |
+
$carrier_regions = Printful_Carriers::instance()->carriers;
|
286 |
+
|
287 |
+
if ( empty( $carrier_regions ) ) {
|
288 |
+
return false;
|
289 |
+
}
|
290 |
+
|
291 |
+
$us_carriers[ self::CARRIER_REGION_US ] = ( ( ! empty( $_POST[ self::CARRIER_REGION_US ] ) && wp_verify_nonce( $_POST['_wpnonce'], 'printful_settings' ) ) ? $_POST[ self::CARRIER_REGION_US ] : array() );
|
292 |
+
$eu_carriers[ self::CARRIER_REGION_EU ] = ( ( ! empty( $_POST[ self::CARRIER_REGION_EU ] ) && wp_verify_nonce( $_POST['_wpnonce'], 'printful_settings' ) ) ? $_POST[ self::CARRIER_REGION_EU ] : array() );
|
293 |
+
|
294 |
+
$parsed_carriers = $this->parse_region_carriers( $us_carriers, $eu_carriers );
|
295 |
+
$us_carriers = $parsed_carriers['us_carriers'];
|
296 |
+
$eu_carriers = $parsed_carriers['eu_carriers'];
|
297 |
+
|
298 |
+
$saved_carriers = array_merge( $us_carriers, $eu_carriers );
|
299 |
+
$request_body = array();
|
300 |
+
|
301 |
+
foreach ( $carrier_regions as $region => $carrier_region ) {
|
302 |
+
foreach ( $carrier_region as $carrier_type => $carrier_methods ) {
|
303 |
+
foreach ( $carrier_methods as $carrier_method => $carrier_data ) {
|
304 |
+
$is_active = false;
|
305 |
+
|
306 |
+
if ( in_array( $carrier_method, $saved_carriers[ $region ][ $carrier_type ] ) ) {
|
307 |
+
$is_active = true;
|
308 |
+
}
|
309 |
+
|
310 |
+
// `isDisabled` means that user is not allowed to change `isActive` state
|
311 |
+
if ($carrier_data['isDisabled']) {
|
312 |
+
$is_active = $carrier_data['isActive'];
|
313 |
+
}
|
314 |
+
|
315 |
+
$request_body[ $region ][ $carrier_type ][ $carrier_method ] = array(
|
316 |
+
'isActive' => $is_active
|
317 |
+
);
|
318 |
+
}
|
319 |
+
}
|
320 |
+
}
|
321 |
+
|
322 |
+
return $request_body;
|
323 |
+
}
|
324 |
+
|
325 |
+
/**
|
326 |
+
* Ajax endpoint for saving the settings
|
327 |
+
* @throws PrintfulException
|
328 |
+
*/
|
329 |
+
public static function save_printful_settings() {
|
330 |
+
|
331 |
+
if ( ! empty( $_POST ) ) {
|
332 |
+
|
333 |
+
check_admin_referer( 'printful_settings' );
|
334 |
+
$error_msg = null;
|
335 |
+
|
336 |
+
//save carriers first, so API key change does not affect this
|
337 |
+
if ( Printful_Integration::instance()->is_connected(true) ) {
|
338 |
+
|
339 |
+
//save remote carrier settings
|
340 |
+
$request_body = Printful_Admin_Settings::instance()->prepare_carriers();
|
341 |
+
$result = Printful_Carriers::instance()->post_carriers( $request_body );
|
342 |
+
|
343 |
+
if ( ! $result ) {
|
344 |
+
$error_msg = 'Error: failed to save carriers';
|
345 |
+
} else if (isset($result['error'])) {
|
346 |
+
$error_msg = $result['error'];
|
347 |
+
}
|
348 |
+
}
|
349 |
+
|
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 ] ) ) {
|
357 |
+
$options[ $key ] = 'yes';
|
358 |
+
} else {
|
359 |
+
$options[ $key ] = 'no';
|
360 |
+
}
|
361 |
+
} else {
|
362 |
+
if ( isset( $_POST[ $key ] ) ) {
|
363 |
+
$options[ $key ] = $_POST[ $key ];
|
364 |
+
}
|
365 |
+
}
|
366 |
+
}
|
367 |
+
|
368 |
+
//save integration settings
|
369 |
+
Printful_Integration::instance()->update_settings( $options );
|
370 |
+
|
371 |
+
if ( $error_msg ) {
|
372 |
+
die( $error_msg );
|
373 |
+
}
|
374 |
+
|
375 |
+
die('OK');
|
376 |
+
}
|
377 |
+
}
|
378 |
+
|
379 |
+
/**
|
380 |
+
* @param array $carrier_regions
|
381 |
+
* @return array|bool
|
382 |
+
*/
|
383 |
+
private function prepare_form_data( $carrier_regions )
|
384 |
+
{
|
385 |
+
$carrier_settings = array();
|
386 |
+
|
387 |
+
foreach ( $carrier_regions as $region => $carrier_types ) {
|
388 |
+
foreach ($carrier_types as $carrier_type => $carrier_data) {
|
389 |
+
foreach ($carrier_data as $key => $carrier) {
|
390 |
+
$carrier_item = array(
|
391 |
+
'label' => $carrier[ 'label' ] . ' <i>' . $carrier[ 'subtitle' ] . '</i>',
|
392 |
+
'value' => ( $carrier[ 'isActive' ] == true ? 'yes' : 'no' ),
|
393 |
+
'isDisabled' => $carrier[ 'isDisabled' ]
|
394 |
+
);
|
395 |
+
|
396 |
+
$carrier_regions[ $region ][ $carrier_type ][ $carrier['key'] ] = $carrier_item;
|
397 |
+
}
|
398 |
+
}
|
399 |
+
}
|
400 |
+
|
401 |
+
$item_array = $this->format_carrier_items( $carrier_regions );
|
402 |
+
|
403 |
+
$carrier_settings[ self::CARRIER_REGION_US ] = array(
|
404 |
+
'title' => __( 'International from USA', 'printful' ),
|
405 |
+
'type' => 'checkbox-group',
|
406 |
+
'items' => $item_array[ self::CARRIER_REGION_US ]
|
407 |
+
);
|
408 |
+
|
409 |
+
$carrier_settings[ self::CARRIER_REGION_EU ] = array(
|
410 |
+
'title' => __( 'International from EU', 'printful' ),
|
411 |
+
'type' => 'checkbox-group',
|
412 |
+
'items' => $item_array[ self::CARRIER_REGION_EU ]
|
413 |
+
);
|
414 |
+
|
415 |
+
return $carrier_settings;
|
416 |
+
}
|
417 |
+
|
418 |
+
/**
|
419 |
+
* Prepare carrier item data for form
|
420 |
+
* @param array $carrier_regions
|
421 |
+
* @return array
|
422 |
+
*/
|
423 |
+
private function format_carrier_items( $carrier_regions )
|
424 |
+
{
|
425 |
+
$item_array = array();
|
426 |
+
|
427 |
+
foreach ( $carrier_regions as $region => $carrier_types ) {
|
428 |
+
foreach ( $carrier_types as $carrier_type => $carrier_data ) {
|
429 |
+
$item_array[ $region ][ $carrier_type ] = array(
|
430 |
+
'subtitle' => ucfirst($carrier_type) . ' shipping',
|
431 |
+
'carriers' => $carrier_data
|
432 |
+
);
|
433 |
+
}
|
434 |
+
}
|
435 |
+
|
436 |
+
return array(
|
437 |
+
self::CARRIER_REGION_US => $item_array[ self::CARRIER_REGION_US ],
|
438 |
+
self::CARRIER_REGION_EU => $item_array[ self::CARRIER_REGION_EU ]
|
439 |
+
);
|
440 |
+
}
|
441 |
+
|
442 |
+
/**
|
443 |
+
* @param array $us_carriers
|
444 |
+
* @param array $eu_carriers
|
445 |
+
* @return array
|
446 |
+
*/
|
447 |
+
private function parse_region_carriers( $us_carriers, $eu_carriers )
|
448 |
+
{
|
449 |
+
foreach ( $us_carriers[ self::CARRIER_REGION_US ] as $carrier_type => $carrier_methods ) {
|
450 |
+
foreach ( $carrier_methods as $key => $carrier_method ) {
|
451 |
+
$us_carriers[ self::CARRIER_REGION_US ][ $carrier_type ][ $key ] = str_replace( self::CARRIER_REGION_US . '_' . $carrier_type . '_', '', $carrier_method );
|
452 |
+
}
|
453 |
+
}
|
454 |
+
|
455 |
+
foreach ( $eu_carriers[ self::CARRIER_REGION_EU ] as $carrier_type => $carrier_methods ) {
|
456 |
+
foreach ( $carrier_methods as $key => $carrier_method ) {
|
457 |
+
$eu_carriers[ self::CARRIER_REGION_EU ][ $carrier_type ][ $key ] = str_replace( self::CARRIER_REGION_EU . '_' . $carrier_type.'_', '', $carrier_method );
|
458 |
+
}
|
459 |
+
}
|
460 |
+
|
461 |
+
return array(
|
462 |
+
'eu_carriers' => $eu_carriers,
|
463 |
+
'us_carriers' => $us_carriers
|
464 |
+
);
|
465 |
+
}
|
466 |
+
}
|
trunk/includes/class-printful-admin-status.php
ADDED
@@ -0,0 +1,597 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
if ( ! defined( 'ABSPATH' ) ) exit;
|
3 |
+
|
4 |
+
class Printful_Admin_Status {
|
5 |
+
|
6 |
+
const PF_STATUS_OK = 1;
|
7 |
+
const PF_STATUS_WARNING = 0;
|
8 |
+
const PF_STATUS_FAIL = -1;
|
9 |
+
const PF_STATUS_NOT_CONNECTED = 2;
|
10 |
+
|
11 |
+
const API_KEY_SEARCH_STRING = 'Printful';
|
12 |
+
const PF_WEBHOOK_NAME = 'Printful Integration';
|
13 |
+
const PF_REMOTE_REQUEST_URL = 'hook/woocommerce?store=1';
|
14 |
+
const PF_REMOTE_REQUEST_TOPIC = 'woo.plugin.test';
|
15 |
+
const PF_STATUS_ISSUE_COUNT = 'printful_status_issue_count';
|
16 |
+
const PF_CACHED_CHECKLIST = 'printful_cached_checklist';
|
17 |
+
const STATUS_CONNECTED_DISPLAYABLE_METHODS = array(
|
18 |
+
'check_PF_webhooks',
|
19 |
+
'check_PF_sync_errors',
|
20 |
+
'check_PF_API_key',
|
21 |
+
'check_WC_API_access'
|
22 |
+
);
|
23 |
+
|
24 |
+
public static $_instance;
|
25 |
+
|
26 |
+
public static function getChecklistItems() {
|
27 |
+
return array(
|
28 |
+
array(
|
29 |
+
'name' => __( 'Connection to Printful API', 'printful' ),
|
30 |
+
'description' => __( 'Is your store successfully connected to Printful API?', 'printful' ),
|
31 |
+
'method' => 'check_PF_API_connect',
|
32 |
+
),
|
33 |
+
array(
|
34 |
+
'name' => __( 'Printful API key is set', 'printful' ),
|
35 |
+
'description' => __( 'Your store needs access to Printful API to use most of it\'s features like shipping rates, tax rates and other settings.', 'printful' ),
|
36 |
+
'method' => 'check_PF_API_key',
|
37 |
+
),
|
38 |
+
array(
|
39 |
+
'name' => __( 'WordPress Permalinks', 'printful' ),
|
40 |
+
'description' => __( 'WooCommerce API will not work unless your permalinks in Settings > Permalinks are set up correctly. Make sure you that they are NOT set to "plain".', 'printful' ),
|
41 |
+
'method' => 'check_permalinks',
|
42 |
+
),
|
43 |
+
array(
|
44 |
+
'name' => __( 'WordPress version', 'printful' ),
|
45 |
+
'description' => __( 'WordPress should always be updated to the latest version. Updates can be installed from your WordPress admin dashboard.', 'printful' ),
|
46 |
+
'method' => 'check_WP_version',
|
47 |
+
),
|
48 |
+
array(
|
49 |
+
'name' => __( 'WooCommerce Webhooks', 'printful' ),
|
50 |
+
'description' => __( 'Printful requires WooCommerce webhooks to be set up to quickly capture you incoming orders, products updates etc.', 'printful' ),
|
51 |
+
'method' => 'check_PF_webhooks',
|
52 |
+
),
|
53 |
+
array(
|
54 |
+
'name' => __( 'WooCommerce API keys are set', 'printful' ),
|
55 |
+
'description' => __( 'Printful needs access to your WooCommerce API for the integration to work - otherwise we can\'t sync your store, push or pull your products etc.', 'printful' ),
|
56 |
+
'method' => 'check_WC_API_access',
|
57 |
+
),
|
58 |
+
array(
|
59 |
+
'name' => __( 'WooCommerce authentication URL access', 'printful' ),
|
60 |
+
'description' => __( 'Printful needs access to WooCommerce API authorize page. This sometimes may get blocked due to hosts having unnecessarily intrusive security checks in place that prevent WooCommerce API authentication from working (for example mod_security rule #1234234). If this check fails, you will not be able authorize Printful app.', 'printful' ),
|
61 |
+
'method' => 'check_WC_auth_url_access',
|
62 |
+
),
|
63 |
+
array(
|
64 |
+
'name' => __( 'WordPress remote requests', 'printful' ),
|
65 |
+
'description' => __( 'WordPress needs to be able to connect to Printful server to call webhooks. If this check fails, contact your hosting support.', 'printful' ),
|
66 |
+
'method' => 'check_remote_requests',
|
67 |
+
),
|
68 |
+
array(
|
69 |
+
'name' => __( 'WordPress Site URL', 'printful' ),
|
70 |
+
'description' => __( 'If your currently setup WordPress site URL is redirected to another URL the integration might not work correctly. Typically this happens with incorrect http to https redirects. Go to Settings > General to fix this.' , 'printful' ),
|
71 |
+
'method' => 'check_site_url_redirect',
|
72 |
+
),
|
73 |
+
array(
|
74 |
+
'name' => __( 'Recent store sync errors', 'printful' ),
|
75 |
+
'description' => __( 'Printful will connect to your store\'s API regularly and sync your latest products, orders etc. If there have been any recent issues with sync, this check will fail.', 'printful' ),
|
76 |
+
'method' => 'check_PF_sync_errors',
|
77 |
+
),
|
78 |
+
array(
|
79 |
+
'name' => __( 'Write permissions', 'printful' ),
|
80 |
+
'description' => __( 'Make the uploads directory writable. This is required for mockup generator product push to work correctly. Contact your hosting provider if you need help with this.', 'printful' ),
|
81 |
+
'method' => 'check_uploads_write',
|
82 |
+
),
|
83 |
+
array(
|
84 |
+
'name' => __( 'PHP memory limit', 'printful' ),
|
85 |
+
'description' => __( 'Set PHP allocated memory limit to at least 128mb. Contact your hosting provider if you need help with this.', 'printful' ),
|
86 |
+
'method' => 'check_PHP_memory_limit',
|
87 |
+
),
|
88 |
+
array(
|
89 |
+
'name' => __( 'PHP script time limit', 'printful' ),
|
90 |
+
'description' => __( 'Set PHP script execution time limit to at least 30 seconds. This is required to successfully push products with many variants. Contact your hosting provider if you need help with this.', 'printful' ),
|
91 |
+
'method' => 'check_PHP_time_limit',
|
92 |
+
),
|
93 |
+
array(
|
94 |
+
'name' => __( 'W3 Total Cache DB Cache', 'printful' ),
|
95 |
+
'description' => __( 'If you are using W3 Total Cache, the database caching feature needs to be disabled since it can cause issues with product push to store.', 'printful' ),
|
96 |
+
'method' => 'check_W3_db_cache',
|
97 |
+
'silent' => true,
|
98 |
+
),
|
99 |
+
array(
|
100 |
+
'name' => __( 'WP SpamShield', 'printful' ),
|
101 |
+
'description' => __( 'If you are using WP SpamShield, you might experience problems connecting to Printful and pushing products.', 'printful' ),
|
102 |
+
'method' => 'check_wp_spamshield',
|
103 |
+
'silent' => true,
|
104 |
+
),
|
105 |
+
array(
|
106 |
+
'name' => __( 'Remove Print Aura plugin', 'printful' ),
|
107 |
+
'description' => __( 'Print Aura plugin is known to cause issues so it needs to be removed.', 'printful' ),
|
108 |
+
'method' => 'check_printaura_plugin',
|
109 |
+
'silent' => true,
|
110 |
+
),
|
111 |
+
);
|
112 |
+
}
|
113 |
+
|
114 |
+
/**
|
115 |
+
* @return Printful_Admin_Status
|
116 |
+
*/
|
117 |
+
public static function instance() {
|
118 |
+
if ( ! function_exists( 'get_plugins' ) ) {
|
119 |
+
include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
|
120 |
+
}
|
121 |
+
|
122 |
+
if ( is_null( self::$_instance ) ) {
|
123 |
+
self::$_instance = new self();
|
124 |
+
}
|
125 |
+
|
126 |
+
return self::$_instance;
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* Setup the view variables
|
131 |
+
*/
|
132 |
+
public static function view() {
|
133 |
+
|
134 |
+
$status = self::instance();
|
135 |
+
$status->render();
|
136 |
+
}
|
137 |
+
|
138 |
+
/**
|
139 |
+
* Render the view
|
140 |
+
*/
|
141 |
+
public function render() {
|
142 |
+
Printful_Admin::load_template( 'header', array( 'tabs' => Printful_Admin::get_tabs() ) );
|
143 |
+
|
144 |
+
$checklist = self::get_checklist( true );
|
145 |
+
if ( $checklist ) {
|
146 |
+
Printful_Admin::load_template( 'status-table', array( 'checklist' => $checklist ) );
|
147 |
+
} else {
|
148 |
+
Printful_Admin::load_template( 'ajax-loader', array( 'action' => 'get_printful_status_checklist', 'message' => __( 'Testing your store (this may take up to 30 seconds)...', 'printful' ) ) );
|
149 |
+
}
|
150 |
+
|
151 |
+
Printful_Admin::load_template( 'footer' );
|
152 |
+
}
|
153 |
+
|
154 |
+
/**
|
155 |
+
* Build the content for status page
|
156 |
+
*/
|
157 |
+
public static function render_status_table_ajax() {
|
158 |
+
|
159 |
+
$checklist = self::get_checklist();
|
160 |
+
Printful_Admin::load_template( 'status-table', array( 'checklist' => $checklist ) );
|
161 |
+
|
162 |
+
exit;
|
163 |
+
}
|
164 |
+
|
165 |
+
/**
|
166 |
+
* Run the tests
|
167 |
+
* @param bool $only_cached_results
|
168 |
+
* @return array
|
169 |
+
* @throws PrintfulException
|
170 |
+
*/
|
171 |
+
public static function get_checklist( $only_cached_results = false ) {
|
172 |
+
|
173 |
+
$status = self::instance();
|
174 |
+
|
175 |
+
$list = get_transient( Printful_Admin_Status::PF_CACHED_CHECKLIST );
|
176 |
+
|
177 |
+
if ( $only_cached_results || $list ) {
|
178 |
+
return $list;
|
179 |
+
}
|
180 |
+
|
181 |
+
$list = array();
|
182 |
+
$list['overall_status'] = true;
|
183 |
+
$issueCount = 0;
|
184 |
+
|
185 |
+
foreach ( self::getChecklistItems() as $item ) {
|
186 |
+
$list_item = array();
|
187 |
+
$list_item['name'] = $item['name'];
|
188 |
+
$list_item['description'] = $item['description'];
|
189 |
+
|
190 |
+
if ( method_exists( $status, $item['method'] ) ) {
|
191 |
+
$list_item['status'] = $status->{$item['method']}();
|
192 |
+
|
193 |
+
// Do not display status for methods that are depending on connection status to Printful
|
194 |
+
if ( ! Printful_Integration::instance()->is_connected(true) && in_array( $item['method'], self::STATUS_CONNECTED_DISPLAYABLE_METHODS ) ) {
|
195 |
+
continue;
|
196 |
+
}
|
197 |
+
|
198 |
+
if ( $status->should_result_be_visible( $list_item['status'], $item ) ) {
|
199 |
+
$list['items'][] = $list_item;
|
200 |
+
}
|
201 |
+
|
202 |
+
if ( $list_item['status'] == self::PF_STATUS_FAIL || $list_item['status'] == self::PF_STATUS_NOT_CONNECTED ) {
|
203 |
+
$list['overall_status'] = false;
|
204 |
+
$issueCount ++;
|
205 |
+
}
|
206 |
+
}
|
207 |
+
}
|
208 |
+
|
209 |
+
set_transient( Printful_Admin_Status::PF_CACHED_CHECKLIST, $list, MINUTE_IN_SECONDS );
|
210 |
+
set_transient( Printful_Admin_Status::PF_STATUS_ISSUE_COUNT, $issueCount, HOUR_IN_SECONDS );
|
211 |
+
|
212 |
+
return $list;
|
213 |
+
}
|
214 |
+
|
215 |
+
/**
|
216 |
+
* Execute only one test
|
217 |
+
* @param $method
|
218 |
+
* @return mixed
|
219 |
+
*/
|
220 |
+
public function run_single_test( $method ) {
|
221 |
+
if ( method_exists( $this, $method ) ) {
|
222 |
+
return $this->{$method}();
|
223 |
+
}
|
224 |
+
return false;
|
225 |
+
}
|
226 |
+
|
227 |
+
/**
|
228 |
+
* @param $status
|
229 |
+
* @param bool $item
|
230 |
+
*
|
231 |
+
* @return int
|
232 |
+
*/
|
233 |
+
private function should_result_be_visible( $status, $item = false ) {
|
234 |
+
|
235 |
+
if ( ! isset( $item['silent'] ) || ( $item['silent'] === true && $status === self::PF_STATUS_FAIL ) ) { //silent items are only shown on FAIL
|
236 |
+
return true;
|
237 |
+
}
|
238 |
+
|
239 |
+
return false;
|
240 |
+
}
|
241 |
+
|
242 |
+
/**
|
243 |
+
* Function for checking if thumbnails are resized
|
244 |
+
*/
|
245 |
+
private function check_uploads_write() {
|
246 |
+
|
247 |
+
$upload_dir = wp_upload_dir();
|
248 |
+
if ( is_writable( $upload_dir['basedir'] ) ) {
|
249 |
+
return self::PF_STATUS_OK;
|
250 |
+
}
|
251 |
+
|
252 |
+
return self::PF_STATUS_FAIL;
|
253 |
+
}
|
254 |
+
|
255 |
+
/**
|
256 |
+
* @return int
|
257 |
+
*/
|
258 |
+
private function check_PHP_memory_limit() {
|
259 |
+
|
260 |
+
$memory_limit = ini_get( 'memory_limit' );
|
261 |
+
|
262 |
+
if ( preg_match( '/^(\d+)(.)$/', $memory_limit, $matches ) ) {
|
263 |
+
if ( $matches[2] == 'M' ) {
|
264 |
+
$memory_limit = $matches[1] * 1024 * 1024; // nnnM -> nnn MB
|
265 |
+
} else if ( $matches[2] == 'K' ) {
|
266 |
+
$memory_limit = $matches[1] * 1024; // nnnK -> nnn KB
|
267 |
+
}
|
268 |
+
}
|
269 |
+
|
270 |
+
$ok = ( $memory_limit >= 128 * 1024 * 1024 ); // at least 128M?
|
271 |
+
|
272 |
+
if ( $ok ) {
|
273 |
+
return self::PF_STATUS_OK;
|
274 |
+
}
|
275 |
+
|
276 |
+
return self::PF_STATUS_FAIL;
|
277 |
+
}
|
278 |
+
|
279 |
+
/**
|
280 |
+
* @return int
|
281 |
+
*/
|
282 |
+
private function check_WP_version() {
|
283 |
+
|
284 |
+
$current = get_bloginfo( 'version' );
|
285 |
+
|
286 |
+
try {
|
287 |
+
$url = 'https://api.wordpress.org/core/version-check/1.7/';
|
288 |
+
$response = wp_remote_get( $url );
|
289 |
+
|
290 |
+
if ( ! is_wp_error( $response ) ) {
|
291 |
+
$json = $response['body'];
|
292 |
+
$obj = json_decode( $json );
|
293 |
+
}
|
294 |
+
|
295 |
+
if ( empty( $obj ) ) {
|
296 |
+
return self::PF_STATUS_FAIL;
|
297 |
+
}
|
298 |
+
|
299 |
+
$version = $obj->offers[0];
|
300 |
+
$latest = $version->version;
|
301 |
+
|
302 |
+
} catch ( Exception $e ) {
|
303 |
+
return self::PF_STATUS_FAIL;
|
304 |
+
}
|
305 |
+
|
306 |
+
if ( ! $latest ) {
|
307 |
+
return self::PF_STATUS_FAIL;
|
308 |
+
}
|
309 |
+
|
310 |
+
if ( version_compare( $current, $latest, '>=' ) ) {
|
311 |
+
return self::PF_STATUS_OK;
|
312 |
+
}
|
313 |
+
|
314 |
+
return self::PF_STATUS_FAIL;
|
315 |
+
}
|
316 |
+
|
317 |
+
/**
|
318 |
+
* @return int
|
319 |
+
*/
|
320 |
+
private function check_PF_webhooks() {
|
321 |
+
global $wpdb;
|
322 |
+
|
323 |
+
// Get the webhooks
|
324 |
+
// In version 3.3 the webhooks are stored in separate table, before that they were stored in posts table
|
325 |
+
if ( version_compare( WC()->version, '3.3', '<' ) ) {
|
326 |
+
|
327 |
+
// Query args
|
328 |
+
$args = array(
|
329 |
+
'post_type' => 'shop_webhook',
|
330 |
+
'nopaging' => true,
|
331 |
+
'ignore_sticky_posts' => true,
|
332 |
+
's' => self::PF_WEBHOOK_NAME,
|
333 |
+
'post_status' => 'published',
|
334 |
+
);
|
335 |
+
|
336 |
+
$webhook_results = new WP_Query( $args );
|
337 |
+
$webhooks = $webhook_results->posts;
|
338 |
+
$count = count( $webhooks ) > 0;
|
339 |
+
} else {
|
340 |
+
$count = $wpdb->get_var(
|
341 |
+
$wpdb->prepare( "SELECT COUNT(*) as webhook_count FROM {$wpdb->prefix}wc_webhooks WHERE name = %s",
|
342 |
+
self::PF_WEBHOOK_NAME
|
343 |
+
) );
|
344 |
+
}
|
345 |
+
|
346 |
+
if ( $count > 0 ) {
|
347 |
+
return self::PF_STATUS_OK;
|
348 |
+
}
|
349 |
+
|
350 |
+
return self::PF_STATUS_FAIL;
|
351 |
+
}
|
352 |
+
|
353 |
+
/**
|
354 |
+
* @return int
|
355 |
+
*/
|
356 |
+
private function check_WC_API_access() {
|
357 |
+
|
358 |
+
global $wpdb;
|
359 |
+
|
360 |
+
//if any keys are set
|
361 |
+
$count = $wpdb->get_var(
|
362 |
+
"SELECT COUNT(*) as key_count FROM {$wpdb->prefix}woocommerce_api_keys"
|
363 |
+
);
|
364 |
+
|
365 |
+
if ( $count == 0 ) {
|
366 |
+
return self::PF_STATUS_FAIL;
|
367 |
+
}
|
368 |
+
|
369 |
+
// Get the API key with matching description
|
370 |
+
$printfulKey = esc_sql( $wpdb->esc_like( wc_clean( self::API_KEY_SEARCH_STRING ) ) );
|
371 |
+
$queryArg = '%'. $wpdb->esc_like( $printfulKey ) . '%';
|
372 |
+
|
373 |
+
$key = $wpdb->get_row(
|
374 |
+
$wpdb->prepare(
|
375 |
+
"SELECT * FROM {$wpdb->prefix}woocommerce_api_keys WHERE description LIKE %s ORDER BY last_access LIMIT 1",
|
376 |
+
$queryArg
|
377 |
+
)
|
378 |
+
);
|
379 |
+
|
380 |
+
if ( ! empty( $key ) && $key->permissions == 'read_write' ) {
|
381 |
+
return self::PF_STATUS_OK;
|
382 |
+
}
|
383 |
+
|
384 |
+
return self::PF_STATUS_WARNING;
|
385 |
+
}
|
386 |
+
|
387 |
+
/**
|
388 |
+
* @return int
|
389 |
+
*/
|
390 |
+
private function check_PF_API_key() {
|
391 |
+
|
392 |
+
$option = get_option( 'woocommerce_printful_settings', array() );
|
393 |
+
if ( ! empty( $option['printful_key'] ) && strlen( $option['printful_key'] ) == 36 ) {
|
394 |
+
return self::PF_STATUS_OK;
|
395 |
+
}
|
396 |
+
|
397 |
+
return self::PF_STATUS_FAIL;
|
398 |
+
}
|
399 |
+
|
400 |
+
/**
|
401 |
+
* @return int
|
402 |
+
* @throws PrintfulException
|
403 |
+
*/
|
404 |
+
private function check_PF_API_connect() {
|
405 |
+
|
406 |
+
if ( Printful_Integration::instance()->is_connected(true) ) {
|
407 |
+
return self::PF_STATUS_OK;
|
408 |
+
}
|
409 |
+
|
410 |
+
return self::PF_STATUS_NOT_CONNECTED;
|
411 |
+
}
|
412 |
+
|
413 |
+
/**
|
414 |
+
* @return int
|
415 |
+
*/
|
416 |
+
private function check_PHP_time_limit() {
|
417 |
+
$time_limit = ini_get( 'max_execution_time' );
|
418 |
+
|
419 |
+
if ( !$time_limit || $time_limit >= 30 ) {
|
420 |
+
return self::PF_STATUS_OK;
|
421 |
+
}
|
422 |
+
|
423 |
+
return self::PF_STATUS_FAIL;
|
424 |
+
}
|
425 |
+
|
426 |
+
/**
|
427 |
+
* @return int
|
428 |
+
*/
|
429 |
+
private function check_PF_sync_errors() {
|
430 |
+
|
431 |
+
$sync_log = get_option( Printful_Request_log::PF_OPTION_INCOMING_API_REQUEST_LOG, array() );
|
432 |
+
if ( empty( $sync_log ) ) {
|
433 |
+
return self::PF_STATUS_OK; //no results means no errors
|
434 |
+
}
|
435 |
+
|
436 |
+
$sync_log = array_reverse( $sync_log );
|
437 |
+
$sync_log = array_slice( $sync_log, 0, 6 ); //we only care about last to syncs
|
438 |
+
|
439 |
+
foreach ( $sync_log as $sl ) {
|
440 |
+
if ( ! empty( $sl['result'] ) && $sl['result'] == 'ERROR' ) {
|
441 |
+
return self::PF_STATUS_FAIL;
|
442 |
+
}
|
443 |
+
}
|
444 |
+
|
445 |
+
return self::PF_STATUS_OK;
|
446 |
+
}
|
447 |
+
|
448 |
+
/**
|
449 |
+
* @return int
|
450 |
+
*/
|
451 |
+
private function check_W3_db_cache() {
|
452 |
+
|
453 |
+
if ( ! is_plugin_active( 'w3-total-cache/w3-total-cache.php' ) ) {
|
454 |
+
return self::PF_STATUS_OK;
|
455 |
+
}
|
456 |
+
|
457 |
+
$w3tc_config_file = get_home_path() . 'wp-content/w3tc-config/master.php';
|
458 |
+
if ( file_exists( $w3tc_config_file ) && is_readable( $w3tc_config_file ) ) {
|
459 |
+
$content = @file_get_contents( $w3tc_config_file );
|
460 |
+
$config = @json_decode( substr( $content, 14 ), true );
|
461 |
+
|
462 |
+
if ( is_array( $config ) && ! empty( $config['dbcache.enabled'] ) ) {
|
463 |
+
return ! $config['dbcache.enabled'];
|
464 |
+
}
|
465 |
+
}
|
466 |
+
|
467 |
+
return self::PF_STATUS_OK;
|
468 |
+
}
|
469 |
+
|
470 |
+
/**
|
471 |
+
* @return int
|
472 |
+
*/
|
473 |
+
private function check_permalinks() {
|
474 |
+
|
475 |
+
$permalinks = get_option( 'permalink_structure', false );
|
476 |
+
|
477 |
+
if ( $permalinks && strlen( $permalinks ) > 0 ) {
|
478 |
+
return self::PF_STATUS_OK;
|
479 |
+
}
|
480 |
+
|
481 |
+
return self::PF_STATUS_FAIL;
|
482 |
+
}
|
483 |
+
|
484 |
+
/**
|
485 |
+
* @return int
|
486 |
+
*/
|
487 |
+
private function check_printaura_plugin() {
|
488 |
+
|
489 |
+
if ( ! is_plugin_active( 'printaura-woocommerce-api/printaura-woocommerce-api.php' ) ) {
|
490 |
+
return self::PF_STATUS_OK;
|
491 |
+
}
|
492 |
+
|
493 |
+
return self::PF_STATUS_FAIL;
|
494 |
+
}
|
495 |
+
|
496 |
+
/**
|
497 |
+
* @return int
|
498 |
+
*/
|
499 |
+
private function check_wp_spamshield() {
|
500 |
+
|
501 |
+
if ( ! is_plugin_active( 'wp-spamshield/wp-spamshield.php' ) ) {
|
502 |
+
return self::PF_STATUS_OK;
|
503 |
+
}
|
504 |
+
|
505 |
+
return self::PF_STATUS_FAIL;
|
506 |
+
}
|
507 |
+
|
508 |
+
/**
|
509 |
+
* @return int
|
510 |
+
*/
|
511 |
+
private function check_remote_requests() {
|
512 |
+
|
513 |
+
// Setup request args.
|
514 |
+
$http_args = array(
|
515 |
+
'method' => 'POST',
|
516 |
+
'timeout' => MINUTE_IN_SECONDS,
|
517 |
+
'redirection' => 0,
|
518 |
+
'httpversion' => '1.0',
|
519 |
+
'blocking' => true,
|
520 |
+
'user-agent' => sprintf( 'WooCommerce/%s Hookshot (WordPress/%s)', WC_VERSION, $GLOBALS['wp_version'] ),
|
521 |
+
'body' => trim( json_encode( array( 'test' => true ) ) ),
|
522 |
+
'headers' => array( 'Content-Type' => 'application/json' ),
|
523 |
+
'cookies' => array(),
|
524 |
+
);
|
525 |
+
|
526 |
+
// Add custom headers.
|
527 |
+
$http_args['headers']['X-WC-Webhook-Source'] = home_url( '/' ); // Since 2.6.0.
|
528 |
+
$http_args['headers']['X-WC-Webhook-Topic'] = self::PF_REMOTE_REQUEST_TOPIC;
|
529 |
+
|
530 |
+
// Webhook away!
|
531 |
+
$response = wp_safe_remote_request( Printful_Base::get_printful_api_host() . self::PF_REMOTE_REQUEST_URL, $http_args );
|
532 |
+
|
533 |
+
if ( is_wp_error( $response ) ) {
|
534 |
+
return self::PF_STATUS_FAIL;
|
535 |
+
}
|
536 |
+
|
537 |
+
return self::PF_STATUS_OK;
|
538 |
+
}
|
539 |
+
|
540 |
+
/**
|
541 |
+
* @return int
|
542 |
+
*/
|
543 |
+
private function check_WC_auth_url_access() {
|
544 |
+
$url = home_url( '/' ) . 'wc-auth/v1/authorize?app_name=Printful&scope=read_write&user_id=1&return_url=https%3A%2F%2Fwww.printful.com%2Fdashboard%2Fwoocommerce%2Freturn&callback_url=https%3A%2F%2Fapi.printful.com%2Fhook%2Fwoocommerce-auth-callback';
|
545 |
+
$http_args = array(
|
546 |
+
'timeout' => 60,
|
547 |
+
'method' => 'GET',
|
548 |
+
'user-agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',
|
549 |
+
);
|
550 |
+
|
551 |
+
$response = wp_safe_remote_get( $url, $http_args );
|
552 |
+
|
553 |
+
if ( is_wp_error( $response ) ) {
|
554 |
+
return self::PF_STATUS_FAIL;
|
555 |
+
}
|
556 |
+
|
557 |
+
$code = $response['response']['code'];
|
558 |
+
|
559 |
+
if ( $code == 200 ) {
|
560 |
+
return self::PF_STATUS_OK;
|
561 |
+
}
|
562 |
+
|
563 |
+
return self::PF_STATUS_FAIL;
|
564 |
+
}
|
565 |
+
|
566 |
+
/**
|
567 |
+
* @return int
|
568 |
+
*/
|
569 |
+
private function check_site_url_redirect()
|
570 |
+
{
|
571 |
+
|
572 |
+
$regular_site_url_req = wp_remote_head( get_option( 'siteurl' ) . '/wp-json/', array('redirection' => 0));
|
573 |
+
|
574 |
+
//need to not trigger issues for subfolder wordpress setups
|
575 |
+
$slashed_site_url_req = wp_remote_head( trailingslashit(get_option( 'siteurl' )), array('redirection' => 0));
|
576 |
+
|
577 |
+
if (is_wp_error($regular_site_url_req) || is_wp_error($slashed_site_url_req)) {
|
578 |
+
return self::PF_STATUS_FAIL;
|
579 |
+
}
|
580 |
+
|
581 |
+
/** @var WP_HTTP_Requests_Response $response */
|
582 |
+
$regular_response = $regular_site_url_req['http_response'];
|
583 |
+
|
584 |
+
/** @var WP_HTTP_Requests_Response $slashed_response */
|
585 |
+
$slashed_response = $slashed_site_url_req['http_response'];
|
586 |
+
|
587 |
+
if ($regular_response->get_status() == 200 || $slashed_response->get_status() == 200) {
|
588 |
+
return self::PF_STATUS_OK;
|
589 |
+
}
|
590 |
+
|
591 |
+
if (in_array($regular_response->get_status(), array(301, 302, 303, 307))) {
|
592 |
+
return self::PF_STATUS_FAIL;
|
593 |
+
}
|
594 |
+
|
595 |
+
return self::PF_STATUS_WARNING;
|
596 |
+
}
|
597 |
+
}
|
trunk/includes/class-printful-admin-support.php
ADDED
@@ -0,0 +1,238 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
if ( ! defined( 'ABSPATH' ) ) exit;
|
3 |
+
|
4 |
+
class Printful_Admin_Support {
|
5 |
+
|
6 |
+
public static $_instance;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* @return Printful_Admin_Support
|
10 |
+
*/
|
11 |
+
public static function instance() {
|
12 |
+
|
13 |
+
if ( is_null( self::$_instance ) ) {
|
14 |
+
self::$_instance = new self();
|
15 |
+
}
|
16 |
+
|
17 |
+
return self::$_instance;
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Setup the view
|
22 |
+
*/
|
23 |
+
public static function view() {
|
24 |
+
|
25 |
+
$support = self::instance();
|
26 |
+
$support->render();
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Display support report
|
31 |
+
*/
|
32 |
+
public function render() {
|
33 |
+
|
34 |
+
Printful_Admin::load_template( 'header', array( 'tabs' => Printful_Admin::get_tabs() ) );
|
35 |
+
|
36 |
+
Printful_Admin::load_template( 'ajax-loader', array( 'action' => 'get_printful_status_report', 'message' => __( 'Building support report (this may take up to 30 seconds)...', 'printful' ) ) );
|
37 |
+
|
38 |
+
Printful_Admin::load_template( 'support-info' );
|
39 |
+
|
40 |
+
Printful_Admin::load_template( 'footer' );
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Build the content for status page
|
45 |
+
*/
|
46 |
+
public static function render_status_report_ajax() {
|
47 |
+
|
48 |
+
$status_report = self::instance()->generate_report();
|
49 |
+
Printful_Admin::load_template( 'status-report', array( 'status_report' => $status_report ) );
|
50 |
+
|
51 |
+
exit;
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Create system status report
|
56 |
+
* @return string
|
57 |
+
* @throws PrintfulException
|
58 |
+
*/
|
59 |
+
public function generate_report() {
|
60 |
+
|
61 |
+
if ( ! class_exists( 'WC_REST_System_Status_Controller' ) ) {
|
62 |
+
return false;
|
63 |
+
}
|
64 |
+
|
65 |
+
$system_status = new WC_REST_System_Status_Controller; //make use of the woocommerce system status report
|
66 |
+
|
67 |
+
ob_start();
|
68 |
+
|
69 |
+
echo __( "##### Printful Checklist #####\n", 'printful' );
|
70 |
+
$checklist = Printful_Admin_Status::get_checklist();
|
71 |
+
foreach ( $checklist['items'] as $item ) {
|
72 |
+
$status = 'OK';
|
73 |
+
if($item['status'] == Printful_Admin_Status::PF_STATUS_WARNING) {
|
74 |
+
$status = 'WARNING';
|
75 |
+
} else if($item['status'] == Printful_Admin_Status::PF_STATUS_FAIL) {
|
76 |
+
$status = 'FAIL';
|
77 |
+
} else if ($item['status'] == Printful_Admin_Status::PF_STATUS_NOT_CONNECTED) {
|
78 |
+
$status = 'NOT CONNECTED';
|
79 |
+
}
|
80 |
+
echo "* ";
|
81 |
+
echo esc_html( str_pad( esc_html( $item['name'] ), 30 ) ) . '=> ' . esc_html( $status ) . "\n";
|
82 |
+
}
|
83 |
+
|
84 |
+
echo "\n\n##### Printful Last Sync's #####\n";
|
85 |
+
$syncReport = $this->get_sync_report();
|
86 |
+
if ( ! empty( $syncReport ) ) {
|
87 |
+
echo esc_html( str_pad( 'Date', 30 ) );
|
88 |
+
echo esc_html( str_pad( 'Request', 30 ) );
|
89 |
+
echo esc_html( str_pad( 'Message', 30 ) );
|
90 |
+
echo "\n";
|
91 |
+
|
92 |
+
foreach ( $syncReport as $sr ) {
|
93 |
+
echo "* ";
|
94 |
+
echo esc_html( str_pad( $sr['date'] . ';', 30 ) );
|
95 |
+
echo esc_html( str_pad( $sr['path'] . ';', 30 ) );
|
96 |
+
echo esc_html( str_pad( $sr['message'] . ';', 30 ) );
|
97 |
+
echo "\n";
|
98 |
+
}
|
99 |
+
}
|
100 |
+
|
101 |
+
echo "\n\n##### Environment #####\n";
|
102 |
+
$this->output_report_block( $system_status->get_environment_info() );
|
103 |
+
|
104 |
+
echo "\n\n##### Database #####\n";
|
105 |
+
$this->output_report_block( $system_status->get_database_info() );
|
106 |
+
|
107 |
+
echo "\n\n##### Active Plugins #####\n";
|
108 |
+
foreach ( $system_status->get_active_plugins() as $plugin ) {
|
109 |
+
if ( ! empty( $plugin['name'] ) ) {
|
110 |
+
echo "* ";
|
111 |
+
echo esc_html( $plugin['name'] ) . " (" . esc_html( $plugin['version'] ) . ")\n";
|
112 |
+
}
|
113 |
+
}
|
114 |
+
|
115 |
+
echo "\n\n##### Theme #####\n";
|
116 |
+
$this->output_report_block( $system_status->get_theme_info() );
|
117 |
+
|
118 |
+
echo "\n\n##### WooCommerce settings #####\n";
|
119 |
+
$this->output_report_block( $system_status->get_settings() );
|
120 |
+
|
121 |
+
if (
|
122 |
+
( defined( 'WP_DEBUG' ) && WP_DEBUG == true )
|
123 |
+
&&
|
124 |
+
( defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG == true )
|
125 |
+
) {
|
126 |
+
echo "\n\n##### Wordpress Error log (last 50 entries) #####\n";
|
127 |
+
$contents = $this->get_error_log_contents();
|
128 |
+
if ( $contents ) {
|
129 |
+
print_r( $contents );
|
130 |
+
}
|
131 |
+
}
|
132 |
+
|
133 |
+
$report = ob_get_contents();
|
134 |
+
ob_end_clean();
|
135 |
+
|
136 |
+
return $report;
|
137 |
+
}
|
138 |
+
|
139 |
+
/**
|
140 |
+
* Get last 50 lines of error log
|
141 |
+
* @return bool|string
|
142 |
+
*/
|
143 |
+
public function get_error_log_contents() {
|
144 |
+
|
145 |
+
if ( ! function_exists( 'fopen' ) ) {
|
146 |
+
return false;
|
147 |
+
}
|
148 |
+
|
149 |
+
return $this->file_tail( WP_CONTENT_DIR . '/debug.log', 50 );
|
150 |
+
}
|
151 |
+
|
152 |
+
/**
|
153 |
+
* source: https://gist.github.com/lorenzos/1711e81a9162320fde20
|
154 |
+
* @param $filepath
|
155 |
+
* @param int $lines
|
156 |
+
* @param bool $adaptive
|
157 |
+
*
|
158 |
+
* @return bool|string
|
159 |
+
*/
|
160 |
+
function file_tail( $filepath, $lines = 1, $adaptive = true ) {
|
161 |
+
|
162 |
+
$f = @fopen( $filepath, "rb" );
|
163 |
+
if ( $f === false ) {
|
164 |
+
return false;
|
165 |
+
}
|
166 |
+
|
167 |
+
// Sets buffer size, according to the number of lines to retrieve.
|
168 |
+
if ( ! $adaptive ) {
|
169 |
+
$buffer = 4096;
|
170 |
+
} else {
|
171 |
+
$buffer = ( $lines < 2 ? 64 : ( $lines < 10 ? 512 : 4096 ) );
|
172 |
+
}
|
173 |
+
|
174 |
+
// Jump to last character
|
175 |
+
fseek( $f, - 1, SEEK_END );
|
176 |
+
if ( fread( $f, 1 ) != "\n" ) {
|
177 |
+
$lines -= 1;
|
178 |
+
}
|
179 |
+
|
180 |
+
$output = '';
|
181 |
+
$chunk = '';
|
182 |
+
while ( ftell( $f ) > 0 && $lines >= 0 ) {
|
183 |
+
// Figure out how far back we should jump
|
184 |
+
$seek = min( ftell( $f ), $buffer );
|
185 |
+
// Do the jump (backwards, relative to where we are)
|
186 |
+
fseek( $f, - $seek, SEEK_CUR );
|
187 |
+
$output = ( $chunk = fread( $f, $seek ) ) . $output;
|
188 |
+
fseek( $f, - mb_strlen( $chunk, '8bit' ), SEEK_CUR );
|
189 |
+
$lines -= substr_count( $chunk, "\n" );
|
190 |
+
}
|
191 |
+
while ( $lines ++ < 0 ) {
|
192 |
+
$output = substr( $output, strpos( $output, "\n" ) + 1 );
|
193 |
+
}
|
194 |
+
fclose( $f );
|
195 |
+
|
196 |
+
return trim( $output );
|
197 |
+
}
|
198 |
+
|
199 |
+
|
200 |
+
/**
|
201 |
+
* Displays the data
|
202 |
+
* @param $data
|
203 |
+
*/
|
204 |
+
public function output_report_block( $data ) {
|
205 |
+
|
206 |
+
foreach ( $data as $key => $item ) {
|
207 |
+
if ( is_string( $item ) ) {
|
208 |
+
echo "* ";
|
209 |
+
echo esc_html( str_pad($key, 30) ) . "=> " . esc_html($item) . "\n";
|
210 |
+
}
|
211 |
+
}
|
212 |
+
}
|
213 |
+
|
214 |
+
/**
|
215 |
+
* Returns log of last incoming API requests from Printful
|
216 |
+
* @return array
|
217 |
+
*/
|
218 |
+
public function get_sync_report() {
|
219 |
+
|
220 |
+
$report = array();
|
221 |
+
$request_log = get_option( Printful_Request_log::PF_OPTION_INCOMING_API_REQUEST_LOG, array() );
|
222 |
+
$request_log = array_reverse( $request_log );
|
223 |
+
|
224 |
+
if ( empty( $request_log ) ) {
|
225 |
+
return $report;
|
226 |
+
}
|
227 |
+
|
228 |
+
foreach ( $request_log as $log ) {
|
229 |
+
$report[] = array(
|
230 |
+
'date' => $log['date'],
|
231 |
+
'path' => $log['request'],
|
232 |
+
'message' => $log['result'],
|
233 |
+
);
|
234 |
+
}
|
235 |
+
|
236 |
+
return $report;
|
237 |
+
}
|
238 |
+
}
|
trunk/includes/class-printful-admin.php
ADDED
@@ -0,0 +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 |
+
}
|
trunk/includes/class-printful-carriers.php
ADDED
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
if ( ! defined( 'ABSPATH' ) ) exit;
|
3 |
+
|
4 |
+
class Printful_Carriers {
|
5 |
+
|
6 |
+
public $carriers;
|
7 |
+
public static $_instance;
|
8 |
+
|
9 |
+
public static function instance() {
|
10 |
+
|
11 |
+
if ( is_null( self::$_instance ) ) {
|
12 |
+
self::$_instance = new self();
|
13 |
+
}
|
14 |
+
|
15 |
+
return self::$_instance;
|
16 |
+
}
|
17 |
+
|
18 |
+
public function __construct() {
|
19 |
+
|
20 |
+
$this->carriers = $this->get_carriers();
|
21 |
+
}
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Get carrier data
|
25 |
+
* @return mixed
|
26 |
+
*/
|
27 |
+
public function get_carriers() {
|
28 |
+
$carriers = get_transient( 'printful_carriers' );
|
29 |
+
if ( ! $carriers ) {
|
30 |
+
$carriers = $this->refresh_carriers();
|
31 |
+
}
|
32 |
+
|
33 |
+
return $carriers;
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Refresh carrier data from Printful
|
38 |
+
* @return mixed
|
39 |
+
*/
|
40 |
+
public function refresh_carriers() {
|
41 |
+
|
42 |
+
try {
|
43 |
+
$carriers = Printful_Integration::instance()->get_client()->get( 'store/get-shipping-methods' );
|
44 |
+
$this->update_carrier_cache( $carriers );
|
45 |
+
} catch (PrintfulApiException $e) {
|
46 |
+
$carriers = array();
|
47 |
+
} catch (PrintfulException $e) {
|
48 |
+
$carriers = array();
|
49 |
+
}
|
50 |
+
|
51 |
+
return $carriers;
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Update carrier transient
|
56 |
+
* @param $carriers
|
57 |
+
*/
|
58 |
+
public function update_carrier_cache($carriers) {
|
59 |
+
|
60 |
+
set_transient( 'printful_carriers', $carriers, MINUTE_IN_SECONDS * 5); //5mins
|
61 |
+
}
|
62 |
+
|
63 |
+
/**
|
64 |
+
* Post carrier settings to printful
|
65 |
+
* @param $data
|
66 |
+
* @return mixed
|
67 |
+
*/
|
68 |
+
public function post_carriers( $data ) {
|
69 |
+
|
70 |
+
if ( empty( $data ) ) {
|
71 |
+
return false;
|
72 |
+
}
|
73 |
+
|
74 |
+
$shipping = new self;
|
75 |
+
try {
|
76 |
+
$carriers = Printful_Integration::instance()->get_client()->patch( 'store/update-shipping-methods', $data );
|
77 |
+
|
78 |
+
if ( empty( $carriers['error'] ) ) {
|
79 |
+
$shipping->update_carrier_cache( $carriers );
|
80 |
+
}
|
81 |
+
} catch ( PrintfulApiException $e ) {
|
82 |
+
$carriers = false;
|
83 |
+
} catch ( PrintfulException $e ) {
|
84 |
+
$carriers = false;
|
85 |
+
}
|
86 |
+
|
87 |
+
return $carriers;
|
88 |
+
}
|
89 |
+
|
90 |
+
}
|
trunk/includes/class-printful-client.php
ADDED
@@ -0,0 +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 {}
|
trunk/includes/class-printful-customizer.php
ADDED
@@ -0,0 +1,168 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
if ( ! defined( 'ABSPATH' ) ) exit;
|
3 |
+
|
4 |
+
class Printful_Customizer {
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Initialize the values, hooks and actions
|
8 |
+
*/
|
9 |
+
public static function init()
|
10 |
+
{
|
11 |
+
$customizer = new self;
|
12 |
+
|
13 |
+
$customizer->hook_actions();
|
14 |
+
}
|
15 |
+
|
16 |
+
public function hook_actions()
|
17 |
+
{
|
18 |
+
// Save customized product as independent cart item data
|
19 |
+
add_filter( 'woocommerce_add_cart_item_data', array( $this, 'save_customizer_hash_data_to_cart' ), 10, 1 );
|
20 |
+
add_filter( 'woocommerce_cart_item_thumbnail', array( $this, 'change_woocommerce_cart_item_thumbnail' ), 20, 2 );
|
21 |
+
// add property to order
|
22 |
+
add_action('woocommerce_new_order_item', array( $this, 'save_customizer_hash_order_meta' ), 10, 2 );
|
23 |
+
|
24 |
+
// hide hash from meta data provided to user
|
25 |
+
add_filter( 'woocommerce_order_item_get_formatted_meta_data', array( $this, 'hide_hash_from_meta_data'), 10, 1 );
|
26 |
+
add_action( 'wp_ajax_printful_customized_thumb', array( $this, 'printful_customized_thumb' ) );
|
27 |
+
add_action( 'wp_ajax_nopriv_printful_customized_thumb', array( $this, 'printful_customized_thumb' ) );
|
28 |
+
|
29 |
+
add_action( 'wp_enqueue_scripts', array( $this, 'load_customizer_scripts' ));
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Load customizer scripts for cart and product page
|
34 |
+
*/
|
35 |
+
public function load_customizer_scripts() {
|
36 |
+
global $post;
|
37 |
+
|
38 |
+
$is_customized_product = $post && get_post_meta( $post->ID, 'pf_customizable', true );
|
39 |
+
if ($is_customized_product || is_cart()) {
|
40 |
+
$customizer_script_handle = 'printful-product-customizer';
|
41 |
+
wp_enqueue_script( $customizer_script_handle, plugins_url( '../assets/js/product-customizer.js', __FILE__ ) );
|
42 |
+
wp_localize_script( $customizer_script_handle, 'pfGlobalCustomizer', array(
|
43 |
+
'admin_url' => esc_url(admin_url()),
|
44 |
+
'modal_title' => Printful_Integration::instance()
|
45 |
+
->get_option( 'pfc_modal_title', Printful_Admin_Settings::DEFAULT_PERSONALIZE_MODAL_TITLE )
|
46 |
+
));
|
47 |
+
}
|
48 |
+
}
|
49 |
+
|
50 |
+
/**
|
51 |
+
* @param $cart_item_data
|
52 |
+
* @return mixed
|
53 |
+
*/
|
54 |
+
public function save_customizer_hash_data_to_cart( $cart_item_data ) {
|
55 |
+
if ( isset( $_POST['pfc_hash'] ) && ! empty( $_POST['pfc_hash'] ) ) {
|
56 |
+
// Set the custom data in the cart item
|
57 |
+
$cart_item_data['pfc_hash'] = (string) sanitize_text_field( $_POST['pfc_hash'] );
|
58 |
+
// set pending actual image property, so it is not reloaded each time
|
59 |
+
$cart_item_data['customizer_image_generated'] = false;
|
60 |
+
|
61 |
+
// Make each item as a unique separated cart item
|
62 |
+
$cart_item_data['unique_key'] = md5( microtime().rand() );
|
63 |
+
}
|
64 |
+
|
65 |
+
return $cart_item_data;
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* @param $thumbnail
|
70 |
+
* @param $cart_item
|
71 |
+
* @return string
|
72 |
+
*/
|
73 |
+
public function change_woocommerce_cart_item_thumbnail( $thumbnail, $cart_item )
|
74 |
+
{
|
75 |
+
if ( $cart_item
|
76 |
+
&& !empty( $cart_item['pfc_hash'] )
|
77 |
+
&& empty( $cart_item['customizer_image_generated'] )
|
78 |
+
) {
|
79 |
+
$class = 'attachment-shop_thumbnail wp-post-image'; // Default cart thumbnail class.
|
80 |
+
$src = esc_url( Printful_Base::get_asset_url() . 'images/loading.gif' );
|
81 |
+
// Construct img tag.
|
82 |
+
return "<img src='{$src}' class='{$class} pf-image-pending' data-hash='{$cart_item['pfc_hash']}' />";
|
83 |
+
} else if ($cart_item
|
84 |
+
&& !empty( $cart_item['pfc_hash'] )
|
85 |
+
&& !empty( $cart_item['customizer_image_generated'] )
|
86 |
+
&& empty( $cart_item['customizer_image_loaded'] )
|
87 |
+
&& !empty( $cart_item['customizer_image'] )
|
88 |
+
) {
|
89 |
+
$class = 'attachment-shop_thumbnail wp-post-image'; // Default cart thumbnail class.
|
90 |
+
// Construct img tag.
|
91 |
+
return "<img src='{$cart_item['customizer_image']}' class='{$class}' data-hash='{$cart_item['pfc_hash']}' />";
|
92 |
+
}
|
93 |
+
|
94 |
+
return $thumbnail;
|
95 |
+
}
|
96 |
+
|
97 |
+
/**
|
98 |
+
* @param $itemId
|
99 |
+
* @param $item
|
100 |
+
*
|
101 |
+
* @throws Exception
|
102 |
+
*/
|
103 |
+
public function save_customizer_hash_order_meta( $itemId, $item )
|
104 |
+
{
|
105 |
+
if (empty($item->legacy_values)) {
|
106 |
+
return;
|
107 |
+
}
|
108 |
+
|
109 |
+
if ( isset( $item->legacy_values['pfc_hash'] ) ) {
|
110 |
+
wc_add_order_item_meta( $itemId, 'pfc_hash', $item->legacy_values['pfc_hash'] );
|
111 |
+
}
|
112 |
+
}
|
113 |
+
|
114 |
+
/**
|
115 |
+
* @throws PrintfulException
|
116 |
+
* @throws Exception
|
117 |
+
*/
|
118 |
+
public static function printful_customized_thumb()
|
119 |
+
{
|
120 |
+
if ( empty( $_GET['hashes'] ) ) {
|
121 |
+
return;
|
122 |
+
}
|
123 |
+
|
124 |
+
$client = Printful_Integration::instance()->get_client();
|
125 |
+
$hashes = implode(',', $_GET['hashes']);
|
126 |
+
$response = $client->get('woocommerce/get-hash-images', array(
|
127 |
+
'hashes' => $hashes
|
128 |
+
));
|
129 |
+
|
130 |
+
// global $woocommerce;
|
131 |
+
$items = WC()->cart->get_cart();
|
132 |
+
|
133 |
+
foreach ($items as $key => $item) {
|
134 |
+
if ( isset( $item['pfc_hash'] ) ) {
|
135 |
+
foreach ($response as $hash => $image_url) {
|
136 |
+
if ($hash !== $item['pfc_hash']) {
|
137 |
+
continue;
|
138 |
+
}
|
139 |
+
|
140 |
+
WC()->cart->cart_contents[$key]['customizer_image_generated'] = true;
|
141 |
+
WC()->cart->cart_contents[$key]['customizer_image'] = $image_url;
|
142 |
+
WC()->cart->cart_contents[$key]['customizer_image_loaded'] = false;
|
143 |
+
}
|
144 |
+
}
|
145 |
+
}
|
146 |
+
|
147 |
+
WC()->cart->set_session();
|
148 |
+
|
149 |
+
echo json_encode($response);
|
150 |
+
exit;
|
151 |
+
}
|
152 |
+
|
153 |
+
/**
|
154 |
+
* Hide pfc_hash from UI
|
155 |
+
* @param array $formatted_meta
|
156 |
+
* @return mixed
|
157 |
+
*/
|
158 |
+
public function hide_hash_from_meta_data( $formatted_meta )
|
159 |
+
{
|
160 |
+
foreach( $formatted_meta as $key => $meta ) {
|
161 |
+
if ( in_array( $meta->key, array( 'pfc_hash' ) ) ) {
|
162 |
+
unset( $formatted_meta[$key] );
|
163 |
+
}
|
164 |
+
}
|
165 |
+
|
166 |
+
return $formatted_meta;
|
167 |
+
}
|
168 |
+
}
|
trunk/includes/class-printful-integration.php
ADDED
@@ -0,0 +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 |
+
}
|
trunk/includes/class-printful-request-log.php
ADDED
@@ -0,0 +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 |
+
}
|
trunk/includes/class-printful-rest-api-controller.php
ADDED
@@ -0,0 +1,263 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* API class
|
5 |
+
*/
|
6 |
+
class Printful_REST_API_Controller extends WC_REST_Controller
|
7 |
+
{
|
8 |
+
/**
|
9 |
+
* Endpoint namespace.
|
10 |
+
*
|
11 |
+
* @var string
|
12 |
+
*/
|
13 |
+
protected $namespace = 'wc/v2';
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Route base.
|
17 |
+
*
|
18 |
+
* @var string
|
19 |
+
*/
|
20 |
+
protected $rest_base = 'printful';
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Register the REST API routes.
|
24 |
+
*/
|
25 |
+
public function register_routes() {
|
26 |
+
register_rest_route( $this->namespace, '/' . $this->rest_base . '/access', array(
|
27 |
+
array(
|
28 |
+
'methods' => WP_REST_Server::EDITABLE,
|
29 |
+
'callback' => array( $this, 'set_printful_access' ),
|
30 |
+
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
31 |
+
'show_in_index' => false,
|
32 |
+
'args' => array(
|
33 |
+
'accessKey' => array(
|
34 |
+
'required' => false,
|
35 |
+
'type' => 'string',
|
36 |
+
'description' => __( 'Printful access key', 'printful' ),
|
37 |
+
),
|
38 |
+
'storeId' => array(
|
39 |
+
'required' => false,
|
40 |
+
'type' => 'integer',
|
41 |
+
'description' => __( 'Store Identifier', 'printful' ),
|
42 |
+
),
|
43 |
+
),
|
44 |
+
)
|
45 |
+
) );
|
46 |
+
|
47 |
+
register_rest_route( $this->namespace, '/' . $this->rest_base . '/products/(?P<product_id>\d+)/size-chart', array(
|
48 |
+
array(
|
49 |
+
'methods' => WP_REST_Server::EDITABLE,
|
50 |
+
'callback' => array( $this, 'post_size_guide' ),
|
51 |
+
'permission_callback' => array( $this, 'update_item_permissions_check' ),
|
52 |
+
'show_in_index' => false,
|
53 |
+
'args' => array(
|
54 |
+
'product_id' => array(
|
55 |
+
'description' => __( 'Unique identifier for the resource.', 'printful' ),
|
56 |
+
'type' => 'integer',
|
57 |
+
),
|
58 |
+
'size_chart' => array(
|
59 |
+
'required' => true,
|
60 |
+
'type' => 'string',
|
61 |
+
'description' => __( 'Printful size guide', 'printful' ),
|
62 |
+
)
|
63 |
+
)
|
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,
|
90 |
+
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
91 |
+
'callback' => array( $this, 'get_version' ),
|
92 |
+
'show_in_index' => false,
|
93 |
+
)
|
94 |
+
) );
|
95 |
+
|
96 |
+
register_rest_route( $this->namespace, '/' . $this->rest_base . '/store_data', array(
|
97 |
+
array(
|
98 |
+
'methods' => WP_REST_Server::READABLE,
|
99 |
+
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
100 |
+
'callback' => array( $this, 'get_store_data' ),
|
101 |
+
'show_in_index' => true,
|
102 |
+
)
|
103 |
+
) );
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* @param WP_REST_Request $request
|
108 |
+
* @return array
|
109 |
+
*/
|
110 |
+
public static function set_printful_access( $request )
|
111 |
+
{
|
112 |
+
$error = false;
|
113 |
+
|
114 |
+
$options = get_option( 'woocommerce_printful_settings', array() );
|
115 |
+
|
116 |
+
$api_key = $request->get_param('accessKey');
|
117 |
+
$store_id = $request->get_param('storeId');
|
118 |
+
$store_id = intval( $store_id );
|
119 |
+
|
120 |
+
if ( ! is_string( $api_key ) || strlen( $api_key ) == 0 || $store_id == 0 ) {
|
121 |
+
$error = 'Failed to update access data';
|
122 |
+
}
|
123 |
+
|
124 |
+
$options['printful_key'] = $api_key;
|
125 |
+
$options['printful_store_id'] = $store_id;
|
126 |
+
|
127 |
+
Printful_Integration::instance()->update_settings( $options );
|
128 |
+
|
129 |
+
return array(
|
130 |
+
'error' => $error,
|
131 |
+
);
|
132 |
+
}
|
133 |
+
|
134 |
+
/**
|
135 |
+
* Submit size guide
|
136 |
+
* @param array $data
|
137 |
+
* @return array|WP_Error
|
138 |
+
*/
|
139 |
+
public static function post_size_guide( $data )
|
140 |
+
{
|
141 |
+
if ( empty( $data['size_chart'] ) ) {
|
142 |
+
return new WP_Error( 'printful_api_size_chart_empty', __( 'No size chart was provided', 'printful' ), array('status' => 400));
|
143 |
+
}
|
144 |
+
|
145 |
+
//product id is valid
|
146 |
+
$product_id = intval( $data['product_id'] );
|
147 |
+
if ( $product_id < 1 ) {
|
148 |
+
return new WP_Error( 'printful_api_product_not_found', __( 'The product ID is invalid', 'printful' ), array('status' => 400));
|
149 |
+
}
|
150 |
+
|
151 |
+
//product exists
|
152 |
+
/** @var WC_Product $product */
|
153 |
+
$product = wc_get_product( $product_id );
|
154 |
+
if ( empty( $product )) {
|
155 |
+
return new WP_Error( 'printful_api_product_not_found', __( 'The product is not found', 'printful' ), array('status' => 400));
|
156 |
+
}
|
157 |
+
|
158 |
+
//how about permissions?
|
159 |
+
$post_type = get_post_type_object( 'product' );
|
160 |
+
if ( ! current_user_can( $post_type->cap->edit_post, $product->get_id() ) ) {
|
161 |
+
return new WP_Error( 'printful_api_user_cannot_edit_product_size_chart', __( 'You do not have permission to edit the size chart', 'printful' ), array('status' => 401));
|
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,
|
178 |
+
'size_chart' => $data['size_chart'],
|
179 |
+
);
|
180 |
+
}
|
181 |
+
|
182 |
+
/**
|
183 |
+
* Allow remotely get plugin version for debug purposes
|
184 |
+
*/
|
185 |
+
public static function get_version() {
|
186 |
+
$error = false;
|
187 |
+
|
188 |
+
try {
|
189 |
+
$client = Printful_Integration::instance()->get_client();
|
190 |
+
$store_data = $client->get( 'store' );
|
191 |
+
} catch ( Exception $exception ) {
|
192 |
+
$error = $exception->getMessage();
|
193 |
+
}
|
194 |
+
|
195 |
+
$checklist = Printful_Admin_Status::get_checklist();
|
196 |
+
$checklist['overall_status'] = ( $checklist['overall_status'] ? 'OK' : 'FAIL' );
|
197 |
+
|
198 |
+
foreach ( $checklist['items'] as $checklist_key => $checklist_item ) {
|
199 |
+
|
200 |
+
if ( $checklist_item['status'] == Printful_Admin_Status::PF_STATUS_OK ) {
|
201 |
+
$checklist_item['status'] = 'OK';
|
202 |
+
} elseif ( $checklist_item['status'] == Printful_Admin_Status::PF_STATUS_WARNING ) {
|
203 |
+
$checklist_item['status'] = 'WARNING';
|
204 |
+
} elseif ( $checklist_item['status'] == Printful_Admin_Status::PF_STATUS_NOT_CONNECTED ) {
|
205 |
+
$checklist_item['status'] = 'NOT CONNECTED';
|
206 |
+
} else {
|
207 |
+
$checklist_item['status'] = 'FAIL';
|
208 |
+
}
|
209 |
+
|
210 |
+
$checklist['items'][ $checklist_key ] = $checklist_item;
|
211 |
+
}
|
212 |
+
|
213 |
+
return array(
|
214 |
+
'version' => Printful_Base::VERSION,
|
215 |
+
'store_id' => ! empty( $store_data['id'] ) ? $store_data['id'] : false,
|
216 |
+
'error' => $error,
|
217 |
+
'status_checklist' => $checklist,
|
218 |
+
);
|
219 |
+
}
|
220 |
+
|
221 |
+
/**
|
222 |
+
* Get necessary store data
|
223 |
+
* @return array
|
224 |
+
*/
|
225 |
+
public static function get_store_data() {
|
226 |
+
return array(
|
227 |
+
'website' => get_site_url(),
|
228 |
+
'version' => WC()->version,
|
229 |
+
'name' => get_bloginfo( 'title', 'display' )
|
230 |
+
);
|
231 |
+
}
|
232 |
+
|
233 |
+
/**
|
234 |
+
* Check whether a given request has permission to read printful endpoints.
|
235 |
+
*
|
236 |
+
* @param WP_REST_Request $request Full details about the request.
|
237 |
+
* @return WP_Error|boolean
|
238 |
+
*/
|
239 |
+
public function get_items_permissions_check( $request ) {
|
240 |
+
if ( ! wc_rest_check_user_permissions( 'read' ) ) {
|
241 |
+
return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
242 |
+
}
|
243 |
+
|
244 |
+
return true;
|
245 |
+
}
|
246 |
+
|
247 |
+
/**
|
248 |
+
* Check if a given request has access to update a product.
|
249 |
+
*
|
250 |
+
* @param WP_REST_Request $request Full details about the request.
|
251 |
+
* @return WP_Error|boolean
|
252 |
+
*/
|
253 |
+
public function update_item_permissions_check( $request ) {
|
254 |
+
$params = $request->get_url_params();
|
255 |
+
$product = wc_get_product( (int) $params['product_id'] );
|
256 |
+
|
257 |
+
if ( empty( $product ) && ! wc_rest_check_post_permissions( 'product', 'edit', $product->get_id() ) ) {
|
258 |
+
return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Sorry, you are not allowed to edit this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
259 |
+
}
|
260 |
+
|
261 |
+
return true;
|
262 |
+
}
|
263 |
+
}
|
trunk/includes/class-printful-shipping.php
ADDED
@@ -0,0 +1,337 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
if ( ! defined( 'ABSPATH' ) ) exit;
|
3 |
+
|
4 |
+
class Printful_Shipping extends WC_Shipping_Method
|
5 |
+
{
|
6 |
+
public $show_warnings = false;
|
7 |
+
public $calculate_tax = false;
|
8 |
+
public $override_defaults = true;
|
9 |
+
private $last_error = false;
|
10 |
+
|
11 |
+
const PRINTFUL_SHIPPING = 'printful_shipping';
|
12 |
+
|
13 |
+
//Store whether currently processed package contains Printful products (for WC<2.6)
|
14 |
+
private $printful_package = true;
|
15 |
+
|
16 |
+
public static function init() {
|
17 |
+
new self;
|
18 |
+
}
|
19 |
+
|
20 |
+
public function __construct() {
|
21 |
+
|
22 |
+
$this->id = 'printful_shipping';
|
23 |
+
$this->method_title = $this->title = 'Printful Shipping';
|
24 |
+
$this->method_description = 'Calculate live shipping rates based on actual Printful shipping costs.';
|
25 |
+
|
26 |
+
$this->init_form_fields();
|
27 |
+
$this->init_settings();
|
28 |
+
|
29 |
+
add_action( 'woocommerce_update_options_shipping_' . $this->id, array( &$this, 'process_admin_options' ) );
|
30 |
+
|
31 |
+
$this->enabled = $this->get_option( 'enabled' );
|
32 |
+
$this->show_warnings = $this->get_option( 'show_warnings' ) == 'yes';
|
33 |
+
$this->override_defaults = $this->get_option( 'override_defaults' ) == 'yes';
|
34 |
+
|
35 |
+
//Initialize shipping methods for specific package (or no package)
|
36 |
+
add_filter( 'woocommerce_load_shipping_methods', array( $this, 'woocommerce_load_shipping_methods' ), 10000 );
|
37 |
+
|
38 |
+
//Remove other shipping methods for Printful package on WC < 2.6
|
39 |
+
add_filter( 'woocommerce_shipping_methods', array( $this, 'woocommerce_shipping_methods' ), 10000 );
|
40 |
+
|
41 |
+
add_filter( 'woocommerce_cart_shipping_packages', array( $this, 'woocommerce_cart_shipping_packages' ), 10000 );
|
42 |
+
}
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Init fields for Printful Shipping form
|
46 |
+
*/
|
47 |
+
public function init_form_fields() {
|
48 |
+
|
49 |
+
$this->form_fields = array(
|
50 |
+
'enabled' => array(
|
51 |
+
'title' => __( 'Enable/Disable', 'woocommerce' ),
|
52 |
+
'type' => 'checkbox',
|
53 |
+
'label' => __( 'Enable this shipping method', 'woocommerce' ),
|
54 |
+
'default' => 'no',
|
55 |
+
),
|
56 |
+
'override_defaults' => array(
|
57 |
+
'title' => __( 'Disable Woocommerce rates', 'woocommerce' ),
|
58 |
+
'type' => 'checkbox',
|
59 |
+
'label' => __( 'Disable standard Woocommerce rates for products fulfilled by Printful', 'woocommerce' ),
|
60 |
+
'default' => 'yes',
|
61 |
+
),
|
62 |
+
'show_warnings' => array(
|
63 |
+
'title' => __( 'Show Printful warnings', 'woocommerce' ),
|
64 |
+
'type' => 'checkbox',
|
65 |
+
'label' => __( 'Display Printful status messages if rate API request fails', 'woocommerce' ),
|
66 |
+
'default' => 'yes',
|
67 |
+
),
|
68 |
+
);
|
69 |
+
|
70 |
+
if ( !Printful_Integration::instance()->get_option('printful_key') ) {
|
71 |
+
$this->form_fields['info'] = array(
|
72 |
+
'type' => 'title',
|
73 |
+
'description' => 'Please add Printful API key to the
|
74 |
+
<a href="' . admin_url( 'admin.php?page=printful-dashboard&tab=settings' ) . '">Printful Integration settings section</a>
|
75 |
+
to enable rate calculation.',
|
76 |
+
);
|
77 |
+
}
|
78 |
+
}
|
79 |
+
|
80 |
+
/**
|
81 |
+
* Enable only Printful shipping method for Printful packages
|
82 |
+
* @param array $package
|
83 |
+
*/
|
84 |
+
public function woocommerce_load_shipping_methods( $package = array() ) {
|
85 |
+
|
86 |
+
$this->printful_package = false;
|
87 |
+
|
88 |
+
if ( $package && ! empty( $package['printful'] ) ) {
|
89 |
+
if ( $this->enabled == 'yes' ) {
|
90 |
+
$this->printful_package = true;
|
91 |
+
if ( $this->override_defaults ) {
|
92 |
+
//Remove default methods if we process Printful package
|
93 |
+
WC()->shipping()->unregister_shipping_methods();
|
94 |
+
}
|
95 |
+
WC()->shipping()->register_shipping_method( $this );
|
96 |
+
}
|
97 |
+
} else if ( ! $package ) {
|
98 |
+
//Show Printful tab on Shipping rate settings
|
99 |
+
WC()->shipping()->register_shipping_method( $this );
|
100 |
+
}
|
101 |
+
}
|
102 |
+
|
103 |
+
/**
|
104 |
+
* Remove non-Printful methods for Printful packages on WC < 2.6
|
105 |
+
* @param $methods
|
106 |
+
*
|
107 |
+
* @return array
|
108 |
+
*/
|
109 |
+
public function woocommerce_shipping_methods( $methods ) {
|
110 |
+
|
111 |
+
if ( $this->override_defaults && $this->printful_package && version_compare( WC()->version, '2.6', '<' ) ) {
|
112 |
+
//For WC < 2.6 woocommerce_shipping_methods is executed after woocommerce_load_shipping_methods
|
113 |
+
//So we need to clean up unnecessary methods from there
|
114 |
+
return array();
|
115 |
+
}
|
116 |
+
|
117 |
+
return $methods;
|
118 |
+
}
|
119 |
+
|
120 |
+
/**
|
121 |
+
* Split Printul products to a separate package if there are any
|
122 |
+
* @param array $packages
|
123 |
+
*
|
124 |
+
* @return array
|
125 |
+
*/
|
126 |
+
public function woocommerce_cart_shipping_packages( $packages = array() ) {
|
127 |
+
//Printful rates are turned off, do not split products
|
128 |
+
if ( $this->enabled !== 'yes' ) {
|
129 |
+
return $packages;
|
130 |
+
}
|
131 |
+
|
132 |
+
$return_packages = array();
|
133 |
+
|
134 |
+
foreach ( $packages as $package ) {
|
135 |
+
$ids = array();
|
136 |
+
foreach ( $package['contents'] as $key => $item ) {
|
137 |
+
$ids[ $key ] = $item['variation_id'] ? $item['variation_id'] : $item['product_id'];
|
138 |
+
}
|
139 |
+
|
140 |
+
$printful_ids = array();
|
141 |
+
if ( $ids ) {
|
142 |
+
asort( $ids );
|
143 |
+
$values = implode( ',', array_unique( $ids ) );
|
144 |
+
$key = 'printful_productids_' . md5( $values );
|
145 |
+
$printful_ids = get_transient( $key );
|
146 |
+
if ( ! is_array( $printful_ids ) ) {
|
147 |
+
$printful_ids = array();
|
148 |
+
try {
|
149 |
+
$client = Printful_Integration::instance()->get_client();
|
150 |
+
$status = $client->get( 'sync/variants', array(
|
151 |
+
'external_ids' => $values,
|
152 |
+
) );
|
153 |
+
if ( ! empty( $status['sync_variants'] ) ) {
|
154 |
+
foreach ( $status['sync_variants'] as $variant ) {
|
155 |
+
if ( $variant['synced'] ) {
|
156 |
+
$printful_ids[] = $variant['external_id'];
|
157 |
+
}
|
158 |
+
}
|
159 |
+
}
|
160 |
+
set_transient( $key, $printful_ids, 1800 );
|
161 |
+
} catch ( PrintfulException $e ) {
|
162 |
+
$this->set_error( $e );
|
163 |
+
|
164 |
+
//Failed to get Printful status, return default packages
|
165 |
+
return $packages;
|
166 |
+
}
|
167 |
+
}
|
168 |
+
}
|
169 |
+
$new_contents = array(
|
170 |
+
'printful' => array(),
|
171 |
+
'virtual' => array(),
|
172 |
+
'woocommerce' => array(),
|
173 |
+
);
|
174 |
+
|
175 |
+
foreach ( $ids as $key => $external_id ) {
|
176 |
+
$item = $package['contents'][ $key ];
|
177 |
+
if ( in_array( $external_id, $printful_ids ) ) {
|
178 |
+
$new_contents['printful'][ $key ] = $item;
|
179 |
+
} else if ( $item['data']->is_virtual() || $item['data']->is_downloadable() ) {
|
180 |
+
$new_contents['virtual'][ $key ] = $item;
|
181 |
+
} else {
|
182 |
+
$new_contents['woocommerce'][ $key ] = $item;
|
183 |
+
}
|
184 |
+
}
|
185 |
+
|
186 |
+
//Put virtual products together with any other package
|
187 |
+
if ( $new_contents['virtual'] ) {
|
188 |
+
if ( $new_contents['printful'] && ! $new_contents['woocommerce'] ) {
|
189 |
+
$new_contents['printful'] += $new_contents['virtual'];
|
190 |
+
} else {
|
191 |
+
$new_contents['woocommerce'] += $new_contents['virtual'];
|
192 |
+
}
|
193 |
+
unset ( $new_contents['virtual'] );
|
194 |
+
}
|
195 |
+
|
196 |
+
foreach ( $new_contents as $key => $contents ) {
|
197 |
+
if ( $contents ) {
|
198 |
+
$new_package = $package;
|
199 |
+
$new_package['contents_cost'] = 0;
|
200 |
+
$new_package['contents'] = $contents;
|
201 |
+
foreach ( $contents as $item ) {
|
202 |
+
if ( $item['data']->needs_shipping() ) {
|
203 |
+
if ( isset( $item['line_total'] ) ) {
|
204 |
+
$new_package['contents_cost'] += $item['line_total'];
|
205 |
+
}
|
206 |
+
}
|
207 |
+
}
|
208 |
+
if ( $key == 'printful' ) {
|
209 |
+
$new_package['printful'] = true;
|
210 |
+
}
|
211 |
+
$return_packages[] = $new_package;
|
212 |
+
}
|
213 |
+
}
|
214 |
+
}
|
215 |
+
|
216 |
+
return $return_packages;
|
217 |
+
}
|
218 |
+
|
219 |
+
/**
|
220 |
+
* @param array $package
|
221 |
+
*
|
222 |
+
* @return bool
|
223 |
+
*/
|
224 |
+
public function calculate_shipping( $package = array() ) {
|
225 |
+
$request = array(
|
226 |
+
'recipient' => array(
|
227 |
+
'address1' => $package['destination']['address'],
|
228 |
+
'address2' => $package['destination']['address_2'],
|
229 |
+
'city' => $package['destination']['city'],
|
230 |
+
'state_code' => $package['destination']['state'],
|
231 |
+
'country_code' => $package['destination']['country'],
|
232 |
+
'zip' => $package['destination']['postcode'],
|
233 |
+
),
|
234 |
+
'items' => array(),
|
235 |
+
'currency' => get_woocommerce_currency(),
|
236 |
+
'locale' => get_locale()
|
237 |
+
);
|
238 |
+
|
239 |
+
|
240 |
+
if ( $request['recipient']['country_code'] == 'US' &&
|
241 |
+
( empty( $request['recipient']['state_code'] ) )
|
242 |
+
) {
|
243 |
+
return false;
|
244 |
+
}
|
245 |
+
|
246 |
+
foreach ( $package['contents'] as $item ) {
|
247 |
+
if ( ! empty( $item['data'] ) && ( $item['data']->is_virtual() || $item['data']->is_downloadable() ) ) {
|
248 |
+
continue;
|
249 |
+
}
|
250 |
+
$request['items'] [] = array(
|
251 |
+
'external_variant_id' => $item['variation_id'] ? $item['variation_id'] : $item['product_id'],
|
252 |
+
'quantity' => $item['quantity'],
|
253 |
+
'value' => $item['line_total'] / $item['quantity'],
|
254 |
+
);
|
255 |
+
}
|
256 |
+
|
257 |
+
if ( ! $request['items'] ) {
|
258 |
+
return false;
|
259 |
+
}
|
260 |
+
|
261 |
+
try {
|
262 |
+
$client = Printful_Integration::instance()->get_client();
|
263 |
+
} catch ( PrintfulException $e ) {
|
264 |
+
$this->set_error( $e );
|
265 |
+
|
266 |
+
return false;
|
267 |
+
}
|
268 |
+
|
269 |
+
try {
|
270 |
+
$key = 'printful_rates_' . md5( json_encode( $request ) );
|
271 |
+
$response = get_transient( $key );
|
272 |
+
if ( $response === false ) {
|
273 |
+
$response = $client->post( 'shipping/rates', $request, array(
|
274 |
+
'expedited' => true,
|
275 |
+
'is_billing_phone_number_mandatory' => $this->isBillingPhoneNumberRequired(),
|
276 |
+
) );
|
277 |
+
//Cache locally, since WC < 2.6 had problems with caching rates form multiple packages internally
|
278 |
+
set_transient( $key, $response, 1800 );
|
279 |
+
}
|
280 |
+
|
281 |
+
foreach ( $response as $rate ) {
|
282 |
+
$rateData = array(
|
283 |
+
'id' => $this->id . '_' . $rate['id'],
|
284 |
+
'label' => $rate['name'],
|
285 |
+
'cost' => $rate['rate'],
|
286 |
+
'calc_tax' => 'per_order',
|
287 |
+
);
|
288 |
+
|
289 |
+
// Before 3.4.0 rate could be passed as ID, after it's set as method_id which refers to class ID
|
290 |
+
if ( version_compare( WC()->version, '3.4.0', '>=' ) ) {
|
291 |
+
$this->id = self::PRINTFUL_SHIPPING . '_' . $rate['id'];
|
292 |
+
}
|
293 |
+
|
294 |
+
$this->add_rate( $rateData );
|
295 |
+
// Reset class ID after adding rate so ID name does not stack as huge string in foreach
|
296 |
+
$this->id = self::PRINTFUL_SHIPPING;
|
297 |
+
}
|
298 |
+
} catch ( PrintfulException $e ) {
|
299 |
+
$this->set_error( $e );
|
300 |
+
return false;
|
301 |
+
}
|
302 |
+
|
303 |
+
return false;
|
304 |
+
}
|
305 |
+
|
306 |
+
/**
|
307 |
+
* @param $error
|
308 |
+
*/
|
309 |
+
private function set_error( $error ) {
|
310 |
+
if ( $this->show_warnings ) {
|
311 |
+
$this->last_error = $error;
|
312 |
+
add_filter( 'woocommerce_cart_no_shipping_available_html', array( $this, 'show_error' ) );
|
313 |
+
add_filter( 'woocommerce_no_shipping_available_html', array( $this, 'show_error' ) );
|
314 |
+
}
|
315 |
+
}
|
316 |
+
|
317 |
+
/**
|
318 |
+
* @param $data
|
319 |
+
*
|
320 |
+
* @return string
|
321 |
+
*/
|
322 |
+
public function show_error( $data ) {
|
323 |
+
$error = $this->last_error;
|
324 |
+
$message = $error->getMessage();
|
325 |
+
|
326 |
+
if ( $error instanceof PrintfulApiException && $error->getCode() == 401 ) {
|
327 |
+
$message = 'Invalid API key';
|
328 |
+
}
|
329 |
+
|
330 |
+
return '<p>ERROR: ' . htmlspecialchars( $message ) . '</p>';
|
331 |
+
}
|
332 |
+
|
333 |
+
private function isBillingPhoneNumberRequired()
|
334 |
+
{
|
335 |
+
return get_option('woocommerce_checkout_phone_field', 'required') === 'required';
|
336 |
+
}
|
337 |
+
}
|
trunk/includes/class-printful-size-chart-tab.php
ADDED
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
if (!empty($_POST['pf_size_chart'])) {
|
128 |
+
update_post_meta($post_id, 'pf_size_chart', htmlspecialchars($_POST['pf_size_chart']));
|
129 |
+
}
|
130 |
+
}
|
131 |
+
}
|
trunk/includes/class-printful-size-guide.php
ADDED
@@ -0,0 +1,221 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
|
4 |
+
class Printful_Size_Guide {
|
5 |
+
/**
|
6 |
+
* Bump this everytime you make changes to size guide CSS file
|
7 |
+
*/
|
8 |
+
const CSS_VERSION = '1';
|
9 |
+
|
10 |
+
public static function init() {
|
11 |
+
$sizeGuide = new self();
|
12 |
+
|
13 |
+
$sizeGuide->hook_actions();
|
14 |
+
}
|
15 |
+
|
16 |
+
public function hook_actions() {
|
17 |
+
add_action( 'wp_enqueue_scripts', array( $this, 'load_size_guide_scripts' ) );
|
18 |
+
}
|
19 |
+
|
20 |
+
public function load_size_guide_scripts() {
|
21 |
+
global $post;
|
22 |
+
|
23 |
+
$sizeGuideData = self::get_size_guide_for_product( $post );
|
24 |
+
if ( ! $sizeGuideData ) {
|
25 |
+
return;
|
26 |
+
}
|
27 |
+
|
28 |
+
$handle = 'printful-product-size-guide';
|
29 |
+
wp_enqueue_script( $handle, plugins_url( '../assets/js/product-size-guide.js', __FILE__ ) );
|
30 |
+
wp_localize_script(
|
31 |
+
$handle,
|
32 |
+
'pfGlobal',
|
33 |
+
array(
|
34 |
+
'sg_modal_title' => Printful_Integration::instance()
|
35 |
+
->get_option( 'pfsg_modal_title', Printful_Admin_Settings::DEFAULT_SIZE_GUIDE_MODAL_TITLE ),
|
36 |
+
'sg_modal_text_color' => Printful_Integration::instance()
|
37 |
+
->get_option( 'pfsg_modal_text_color', Printful_Admin_Settings::DEFAULT_SIZE_GUIDE_MODAL_TEXT_COLOR ),
|
38 |
+
'sg_modal_background_color' => Printful_Integration::instance()
|
39 |
+
->get_option(
|
40 |
+
'pfsg_modal_background_color',
|
41 |
+
Printful_Admin_Settings::DEFAULT_SIZE_GUIDE_MODAL_BACKGROUND_COLOR
|
42 |
+
),
|
43 |
+
'sg_tab_background_color' => Printful_Integration::instance()
|
44 |
+
->get_option(
|
45 |
+
'pfsg_tab_background_color',
|
46 |
+
Printful_Admin_Settings::DEFAULT_SIZE_GUIDE_TAB_BACKGROUND_COLOR
|
47 |
+
),
|
48 |
+
'sg_active_tab_background_color' => Printful_Integration::instance()
|
49 |
+
->get_option(
|
50 |
+
'pfsg_active_tab_background_color',
|
51 |
+
Printful_Admin_Settings::DEFAULT_SIZE_GUIDE_ACTIVE_TAB_BACKGROUND_COLOR
|
52 |
+
),
|
53 |
+
'sg_primary_unit' => Printful_Integration::instance()
|
54 |
+
->get_option( 'pfsg_primary_unit', Printful_Admin_Settings::DEFAULT_SIZE_GUIDE_UNIT ),
|
55 |
+
'sg_data_raw' => json_encode( $sizeGuideData ),
|
56 |
+
'sg_tab_title_person' => __( 'Measure yourself', 'printful' ),
|
57 |
+
'sg_tab_title_product' => __( 'Product measurements', 'printful' ),
|
58 |
+
'sg_table_header_size' => __( 'Size', 'printful' ),
|
59 |
+
'sg_unit_translations' => json_encode( [
|
60 |
+
'inch' => __( 'Inches', 'printful' ),
|
61 |
+
'centimeter' => __( 'Centimeters', 'printful' ),
|
62 |
+
] )
|
63 |
+
)
|
64 |
+
);
|
65 |
+
wp_register_style( $handle, plugins_url( '../assets/css/size-guide.css', __FILE__ ), [], self::CSS_VERSION );
|
66 |
+
wp_enqueue_style( $handle );
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* @param WP_Post $product
|
71 |
+
*
|
72 |
+
* @return array|null
|
73 |
+
*/
|
74 |
+
public static function get_size_guide_for_product( $product = null ) {
|
75 |
+
if ( ! $product || ! get_post_meta( $product->ID, 'pf_advanced_size_chart', true ) ) {
|
76 |
+
return null;
|
77 |
+
}
|
78 |
+
|
79 |
+
// Size guide data should be a valid JSON
|
80 |
+
$size_guide_data = json_decode( get_post_meta( $product->ID, 'pf_advanced_size_chart', true ), true );
|
81 |
+
|
82 |
+
if ( ! self::is_size_guide_valid( $size_guide_data ) ) {
|
83 |
+
return null;
|
84 |
+
}
|
85 |
+
|
86 |
+
// URLs to size guide images
|
87 |
+
if ( isset( $size_guide_data['modelMeasurements']['imageId'] ) ) {
|
88 |
+
$size_guide_data['modelMeasurements']['imageUrl'] = self::get_attached_image_src( $size_guide_data['modelMeasurements']['imageId'] );
|
89 |
+
}
|
90 |
+
|
91 |
+
if ( isset( $size_guide_data['productMeasurements']['imageId'] ) ) {
|
92 |
+
$size_guide_data['productMeasurements']['imageUrl'] = self::get_attached_image_src( $size_guide_data['productMeasurements']['imageId'] );
|
93 |
+
}
|
94 |
+
|
95 |
+
return $size_guide_data;
|
96 |
+
}
|
97 |
+
|
98 |
+
/**
|
99 |
+
* @param array $size_guide
|
100 |
+
*
|
101 |
+
* @return bool
|
102 |
+
*/
|
103 |
+
public static function is_size_guide_valid( $size_guide = null ) {
|
104 |
+
if ( empty( $size_guide ) || ! is_array( $size_guide ) ) {
|
105 |
+
return false;
|
106 |
+
}
|
107 |
+
|
108 |
+
// Size guide should have at least model or product measurement data
|
109 |
+
if ( ( empty( $size_guide['modelMeasurements'] ) && empty( $size_guide['productMeasurements'] ) )
|
110 |
+
|| empty( $size_guide['availableSizes'] ) ) {
|
111 |
+
return false;
|
112 |
+
}
|
113 |
+
|
114 |
+
// Validate size guide rows
|
115 |
+
$rows = [];
|
116 |
+
if ( ! empty( $size_guide['modelMeasurements']['sizeTableRows'] ) ) {
|
117 |
+
$rows += $size_guide['modelMeasurements']['sizeTableRows'];
|
118 |
+
}
|
119 |
+
|
120 |
+
if ( ! empty( $size_guide['productMeasurements']['sizeTableRows'] ) ) {
|
121 |
+
$rows += $size_guide['productMeasurements']['sizeTableRows'];
|
122 |
+
}
|
123 |
+
|
124 |
+
foreach ( $rows as $row ) {
|
125 |
+
if ( empty( $row['unit'] ) || empty( $row['sizes'] ) ) {
|
126 |
+
return false;
|
127 |
+
}
|
128 |
+
}
|
129 |
+
|
130 |
+
return true;
|
131 |
+
}
|
132 |
+
|
133 |
+
/**
|
134 |
+
* @param int $attachment_id
|
135 |
+
*
|
136 |
+
* @return string|null
|
137 |
+
*/
|
138 |
+
public static function get_attached_image_src( $attachment_id ) {
|
139 |
+
$attachment = wp_get_attachment_image_src( $attachment_id, 'full' );
|
140 |
+
|
141 |
+
if ( ! is_array( $attachment ) ) {
|
142 |
+
return null;
|
143 |
+
}
|
144 |
+
|
145 |
+
return current( $attachment );
|
146 |
+
}
|
147 |
+
|
148 |
+
/**
|
149 |
+
* Creates file from given url and stores as post attachment
|
150 |
+
*
|
151 |
+
* @param string $url
|
152 |
+
* @param string $file_name
|
153 |
+
*
|
154 |
+
* @return int|WP_Error
|
155 |
+
*/
|
156 |
+
public static function save_image( $url, $file_name ) {
|
157 |
+
if ( ! $url ) {
|
158 |
+
return null;
|
159 |
+
}
|
160 |
+
|
161 |
+
require_once ABSPATH . 'wp-admin/includes/file.php';
|
162 |
+
|
163 |
+
// Download size guide img to temp file
|
164 |
+
$temp_file_name = download_url( $url, 20 );
|
165 |
+
if ( is_wp_error( $temp_file_name ) ) {
|
166 |
+
return $temp_file_name;
|
167 |
+
}
|
168 |
+
|
169 |
+
// Validate image readability
|
170 |
+
require_once ABSPATH . 'wp-admin/includes/image.php';
|
171 |
+
if ( ! file_is_displayable_image( $temp_file_name ) ) {
|
172 |
+
@unlink( $temp_file_name );
|
173 |
+
|
174 |
+
return new WP_Error( 'pf_size_guide_img', 'Size guide attachment is not a valid image' );
|
175 |
+
}
|
176 |
+
|
177 |
+
$file_data = array(
|
178 |
+
'name' => $file_name,
|
179 |
+
'tmp_name' => $temp_file_name,
|
180 |
+
);
|
181 |
+
|
182 |
+
// Handle side load and attach to a post
|
183 |
+
require_once ABSPATH . 'wp-admin/includes/media.php';
|
184 |
+
$id = media_handle_sideload( $file_data );
|
185 |
+
|
186 |
+
@unlink( $temp_file_name );
|
187 |
+
|
188 |
+
if ( is_wp_error( $id ) ) {
|
189 |
+
return $id;
|
190 |
+
}
|
191 |
+
|
192 |
+
return $id;
|
193 |
+
}
|
194 |
+
|
195 |
+
/**
|
196 |
+
* @param array $size_guide_data
|
197 |
+
* @param int $product_id
|
198 |
+
*
|
199 |
+
* @return array
|
200 |
+
*/
|
201 |
+
public static function process_size_guide_for_storage( $size_guide_data, $product_id ) {
|
202 |
+
$modelImageUrl = ! empty( $size_guide_data['modelMeasurements']['imageUrl'] )
|
203 |
+
? $size_guide_data['modelMeasurements']['imageUrl'] : null;
|
204 |
+
|
205 |
+
$productImageUrl = ! empty( $size_guide_data['productMeasurements']['imageUrl'] )
|
206 |
+
? $size_guide_data['productMeasurements']['imageUrl'] : null;
|
207 |
+
|
208 |
+
// Remove original URLs
|
209 |
+
unset( $size_guide_data['modelMeasurements']['imageUrl'] );
|
210 |
+
unset( $size_guide_data['productMeasurements']['imageUrl'] );
|
211 |
+
|
212 |
+
$modelImageId = self::save_image( $modelImageUrl, $product_id . '_model_size_guide.png' );
|
213 |
+
$productImageId = self::save_image( $productImageUrl, $product_id . '_product_size_guide.png' );
|
214 |
+
|
215 |
+
// Link size guide image attachments to size guide
|
216 |
+
$size_guide_data['modelMeasurements']['imageId'] = ! is_wp_error( $modelImageId ) ? $modelImageId : null;
|
217 |
+
$size_guide_data['productMeasurements']['imageId'] = ! is_wp_error( $productImageId ) ? $productImageId : null;
|
218 |
+
|
219 |
+
return $size_guide_data;
|
220 |
+
}
|
221 |
+
}
|
trunk/includes/class-printful-taxes.php
ADDED
@@ -0,0 +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 |
+
}
|
trunk/includes/class-printful-template.php
ADDED
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
if ( ! defined( 'ABSPATH' ) ) exit;
|
3 |
+
|
4 |
+
class Printful_Template {
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Initialize the values, hooks and actions
|
8 |
+
*/
|
9 |
+
public static function init()
|
10 |
+
{
|
11 |
+
$template = new self;
|
12 |
+
|
13 |
+
$template->hook_templates();
|
14 |
+
}
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Hook custom modifications in template files
|
18 |
+
*/
|
19 |
+
public function hook_templates()
|
20 |
+
{
|
21 |
+
// hook templates, 29 indicates position, right before variation selection
|
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 |
+
/**
|
30 |
+
* Hook callback for personalization button within product page
|
31 |
+
*/
|
32 |
+
public static function printful_template_customize_button()
|
33 |
+
{
|
34 |
+
global $post;
|
35 |
+
|
36 |
+
if ( $post && get_post_meta( $post->ID, 'pf_customizable', true ) ) {
|
37 |
+
// load template for personalization button
|
38 |
+
Printful_Admin::load_template( 'personalize-button', array(
|
39 |
+
'site_url' => get_site_url(),
|
40 |
+
'pfc_button_color' => Printful_Integration::instance()->get_option( 'pfc_button_color' ) ?: Printful_Admin_Settings::DEFAULT_PERSONALIZE_BUTTON_COLOR,
|
41 |
+
'pfc_button_text' => Printful_Integration::instance()->get_option( 'pfc_button_text' ) ?: Printful_Admin_Settings::DEFAULT_PERSONALIZE_BUTTON_TEXT,
|
42 |
+
'pfc_modal_title' => Printful_Integration::instance()->get_option( 'pfc_modal_title' ) ?: Printful_Admin_Settings::DEFAULT_PERSONALIZE_MODAL_TITLE,
|
43 |
+
) );
|
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 |
+
}
|
trunk/includes/templates/ajax-loader.php
ADDED
@@ -0,0 +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>
|
trunk/includes/templates/connect.php
ADDED
@@ -0,0 +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>
|
trunk/includes/templates/customizer-hidden-input.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<input type="hidden" id="pfc_hash" name="pfc_hash" value="">
|
trunk/includes/templates/error.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<p class="printful-error"><b><?php esc_html_e('Error:', 'printful'); ?></b> <?php echo wp_kses_post($error); ?></p>
|
trunk/includes/templates/footer.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
</div>
|
trunk/includes/templates/header.php
ADDED
@@ -0,0 +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>
|
trunk/includes/templates/inline-script.php
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
<script type="text/javascript">
|
2 |
+
<?php echo esc_js( $script ); ?>
|
3 |
+
</script>
|
trunk/includes/templates/order-table.php
ADDED
@@ -0,0 +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; ?>
|
trunk/includes/templates/personalize-button.php
ADDED
@@ -0,0 +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>
|
trunk/includes/templates/quick-links.php
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<h2>Quick links</h2>
|
2 |
+
|
3 |
+
<div class="printful-quick-links">
|
4 |
+
<a href="<?php echo esc_url(Printful_Base::get_printful_host()); ?>dashboard/default/orders" target="_blank" class="printful-quick-links-item">
|
5 |
+
<span class="dashicons dashicons-cart"></span>
|
6 |
+
<h4><?php esc_html_e('Orders', 'printful'); ?></h4>
|
7 |
+
</a>
|
8 |
+
<a href="<?php echo esc_url(Printful_Base::get_printful_host()); ?>dashboard/library" target="_blank" class="printful-quick-links-item">
|
9 |
+
<span class="dashicons dashicons-images-alt"></span>
|
10 |
+
<h4><?php esc_html_e('File library', 'printful'); ?></h4>
|
11 |
+
</a>
|
12 |
+
<a href="<?php echo esc_url(Printful_Base::get_printful_host()); ?>dashboard/store" target="_blank" class="printful-quick-links-item">
|
13 |
+
<span class="dashicons dashicons-store"></span>
|
14 |
+
<h4><?php esc_html_e('Stores', 'printful'); ?></h4>
|
15 |
+
</a>
|
16 |
+
<a href="<?php echo esc_url(Printful_Base::get_printful_host()); ?>dashboard/reports" target="_blank" class="printful-quick-links-item">
|
17 |
+
<span class="dashicons dashicons-chart-area"></span>
|
18 |
+
<h4><?php esc_html_e('Reports', 'printful'); ?></h4>
|
19 |
+
</a>
|
20 |
+
<a href="<?php echo esc_url(Printful_Base::get_printful_host()); ?>dashboard/settings/account-settings" target="_blank" class="printful-quick-links-item">
|
21 |
+
<span class="dashicons dashicons-admin-users"></span>
|
22 |
+
<h4><?php esc_html_e('My Account', 'printful'); ?></h4>
|
23 |
+
</a>
|
24 |
+
<a href="<?php echo esc_url(Printful_Base::get_printful_host()); ?>dashboard/billing" target="_blank" class="printful-quick-links-item">
|
25 |
+
<span class="dashicons dashicons-portfolio"></span>
|
26 |
+
<h4><?php esc_html_e('Billing', 'printful'); ?></h4>
|
27 |
+
</a>
|
28 |
+
<a href="<?php echo esc_url(Printful_Base::get_printful_host()); ?>dashboard/notifications" target="_blank" class="printful-quick-links-item">
|
29 |
+
<span class="dashicons dashicons-megaphone"></span>
|
30 |
+
<h4><?php esc_html_e('Notifications', 'printful'); ?></h4>
|
31 |
+
</a>
|
32 |
+
</div>
|
trunk/includes/templates/setting-group.php
ADDED
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @var string $title
|
4 |
+
* @var string $description
|
5 |
+
* @var string $carrier_version
|
6 |
+
* @var array $settings
|
7 |
+
*/
|
8 |
+
?>
|
9 |
+
<div class="printful-setting-group">
|
10 |
+
|
11 |
+
<h2><?php echo esc_html($title); ?></h2>
|
12 |
+
<p><?php _e( $description, 'printful' ); ?></p>
|
13 |
+
|
14 |
+
<?php if ( !empty( $settings ) ) : ?>
|
15 |
+
|
16 |
+
<table class="form-table">
|
17 |
+
<tbody>
|
18 |
+
<?php foreach($settings as $key => $setting) : ?>
|
19 |
+
|
20 |
+
<?php
|
21 |
+
if($setting['type'] == 'title') {
|
22 |
+
continue;
|
23 |
+
}
|
24 |
+
?>
|
25 |
+
|
26 |
+
<tr valign="top">
|
27 |
+
|
28 |
+
<th scope="row" class="titledesc">
|
29 |
+
<label for="<?php echo esc_attr($key); ?>"><?php echo esc_html($setting['title']); ?></label>
|
30 |
+
</th>
|
31 |
+
|
32 |
+
<td class="forminp">
|
33 |
+
<fieldset>
|
34 |
+
<legend class="screen-reader-text"><span><?php echo esc_html($setting['title']); ?></span></legend>
|
35 |
+
|
36 |
+
<?php if ( $setting['type'] == 'text' ) : ?>
|
37 |
+
|
38 |
+
<input class="input-text regular-input" type="text" name="<?php echo esc_attr($key); ?>" id="<?php echo esc_attr($key); ?>" value="<?php echo esc_html($setting['value'] ?: $setting['default']); ?>" placeholder="">
|
39 |
+
|
40 |
+
<?php elseif ($setting['type'] == 'checkbox') : ?>
|
41 |
+
|
42 |
+
<label for="<?php echo esc_attr($key); ?>">
|
43 |
+
<input class="" type="checkbox" name="<?php echo esc_attr($key); ?>" id="<?php echo esc_attr($key); ?>" value="1" <?php if($setting['value'] == 'yes') { echo 'checked="checked"'; } ?>>
|
44 |
+
<?php _e( $setting['label'] , 'printful' ); ?>
|
45 |
+
</label>
|
46 |
+
<br>
|
47 |
+
|
48 |
+
<?php elseif ($setting['type'] == 'checkbox-group') : ?>
|
49 |
+
|
50 |
+
<?php foreach ( $setting['items'] as $checkbox_key => $item ) : ?>
|
51 |
+
|
52 |
+
<label class="carrier-type"><?php echo $item['subtitle']; ?></label>
|
53 |
+
<?php foreach ( $item['carriers'] as $carrier_name => $carrier ): ?>
|
54 |
+
|
55 |
+
<label for="<?php echo esc_attr( $key ) . '_' . esc_attr( $checkbox_key ).'_'.esc_attr( $carrier_name ); ?>" class="checkbox-group-item">
|
56 |
+
<input class="" type="checkbox" name="<?php echo esc_attr( $key ) . '[' . esc_attr( $checkbox_key ) . '][]'; ?>"
|
57 |
+
id="<?php echo esc_attr( $key ) . '_' .esc_attr( $checkbox_key ) . '_' . esc_attr( $carrier_name ); ?>"
|
58 |
+
value="<?php echo esc_attr( $key ) . '_' . esc_attr( $checkbox_key ) . '_' . esc_attr( $carrier_name ); ?>"
|
59 |
+
<?php if ( $carrier['value'] == 'yes' ) {
|
60 |
+
echo 'checked="checked"';
|
61 |
+
} ?>
|
62 |
+
<?php if ( $carrier['isDisabled'] ) {
|
63 |
+
echo 'disabled="disabled"';
|
64 |
+
} ?>
|
65 |
+
>
|
66 |
+
<?php echo wp_kses_post( $carrier['label'] ); ?>
|
67 |
+
</label>
|
68 |
+
<br>
|
69 |
+
|
70 |
+
<?php endforeach; ?>
|
71 |
+
|
72 |
+
<?php endforeach; ?>
|
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>
|
91 |
+
</td>
|
92 |
+
</tr>
|
93 |
+
|
94 |
+
<?php endforeach; ?>
|
95 |
+
</tbody>
|
96 |
+
</table>
|
97 |
+
|
98 |
+
<?php endif; ?>
|
99 |
+
|
100 |
+
</div>
|
101 |
+
<script>
|
102 |
+
jQuery(document).ready(function($){
|
103 |
+
$('.pf-color-picker').wpColorPicker();
|
104 |
+
});
|
105 |
+
</script>
|
trunk/includes/templates/setting-submit.php
ADDED
@@ -0,0 +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>
|
trunk/includes/templates/shipping-notification.php
ADDED
@@ -0,0 +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§ion=printful_shipping' ) ); ?>"><?php esc_html_e('WooCommerce Shipping settings', 'printful'); ?></a>.</p>
|
4 |
+
</div>
|
trunk/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>
|
trunk/includes/templates/stats.php
ADDED
@@ -0,0 +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>
|
trunk/includes/templates/status-report.php
ADDED
@@ -0,0 +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>
|
trunk/includes/templates/status-table.php
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( $checklist['overall_status'] ) {
|
2 |
+
?>
|
3 |
+
<div class="notice notice-success">
|
4 |
+
<p><?php esc_html_e('Looks like the everything is set up correctly and Printful integration should work as intended.', 'printful'); ?></p>
|
5 |
+
</div>
|
6 |
+
<?php
|
7 |
+
} else {
|
8 |
+
?>
|
9 |
+
<div class="notice notice-error">
|
10 |
+
<p><?php esc_html_e('There are errors with your store setup that may cause the Printful integration to not work as intended!', 'printful'); ?></p>
|
11 |
+
</div>
|
12 |
+
<?php
|
13 |
+
}
|
14 |
+
?>
|
15 |
+
|
16 |
+
<table class="wp-list-table widefat fixed striped printful-status">
|
17 |
+
<thead>
|
18 |
+
<tr>
|
19 |
+
<td class="col-name"><?php esc_html_e('Name', 'printful'); ?></td>
|
20 |
+
<td class="col-desc"><?php esc_html_e('Description', 'printful'); ?></td>
|
21 |
+
<td class="col-status"><?php esc_html_e('Status', 'printful'); ?></td>
|
22 |
+
</tr>
|
23 |
+
</thead>
|
24 |
+
<tbody>
|
25 |
+
<?php
|
26 |
+
foreach ( $checklist['items'] as $item ) : ?>
|
27 |
+
<tr>
|
28 |
+
<td><?php echo esc_html( $item['name'] ); ?></td>
|
29 |
+
<td><?php echo esc_html( $item['description'] ); ?></td>
|
30 |
+
<td>
|
31 |
+
<?php
|
32 |
+
$status = 'OK';
|
33 |
+
if ( $item['status'] == 1 ) {
|
34 |
+
echo '<span class="pass">' . esc_html__('OK', 'printful') .'</span>';
|
35 |
+
} else if ( $item['status'] == 0 ) {
|
36 |
+
echo '<span class="warning">' . esc_html__('WARNING', 'printful') .'*</span>';
|
37 |
+
} else if ( $item['status'] == 2 ) {
|
38 |
+
echo '<span class="fail">' . esc_html__('NOT CONNECTED', 'printful') .'</span>';
|
39 |
+
} else {
|
40 |
+
echo '<span class="fail">' . esc_html__('FAIL', 'printful') .'</span>';
|
41 |
+
}
|
42 |
+
?>
|
43 |
+
</td>
|
44 |
+
</tr>
|
45 |
+
<?php endforeach; ?>
|
46 |
+
</tbody>
|
47 |
+
<tfoot>
|
48 |
+
<tr>
|
49 |
+
<td class="col-name"><?php esc_html_e('Name', 'printful'); ?></td>
|
50 |
+
<td class="col-desc"><?php esc_html_e('Description', 'printful'); ?></td>
|
51 |
+
<td class="col-status"><?php esc_html_e('Status', 'printful'); ?></td>
|
52 |
+
</tr>
|
53 |
+
</tfoot>
|
54 |
+
</table>
|
55 |
+
|
56 |
+
<p class="asterisk">* <?php esc_html_e('Warnings are issued when the test was unable to come to a definite conclusion or if the result was passable, but not ideal.', 'printful'); ?></p>
|
trunk/includes/templates/support-info.php
ADDED
@@ -0,0 +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>
|
trunk/license.txt
ADDED
@@ -0,0 +1,697 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Printful Integration for WooCommerce
|
2 |
+
|
3 |
+
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
4 |
+
|
5 |
+
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
6 |
+
|
7 |
+
You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/
|
8 |
+
|
9 |
+
This program incorporates work covered by the following copyright and
|
10 |
+
permission notices:
|
11 |
+
|
12 |
+
intercom.js is Copyright (c) 2012 DIY Co
|
13 |
+
https://github.com/diy/intercom.js
|
14 |
+
intercom.js is released under Apache License (v2)
|
15 |
+
|
16 |
+
and
|
17 |
+
|
18 |
+
Printful Integration for WooCommerce
|
19 |
+
Copyright (c) 2022 Printful® Inc
|
20 |
+
Printful Integration for WooCommerce is released under GPL
|
21 |
+
|
22 |
+
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
23 |
+
|
24 |
+
GNU GENERAL PUBLIC LICENSE
|
25 |
+
Version 3, 29 June 2007
|
26 |
+
|
27 |
+
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
28 |
+
Everyone is permitted to copy and distribute verbatim copies
|
29 |
+
of this license document, but changing it is not allowed.
|
30 |
+
|
31 |
+
Preamble
|
32 |
+
|
33 |
+
The GNU General Public License is a free, copyleft license for
|
34 |
+
software and other kinds of works.
|
35 |
+
|
36 |
+
The licenses for most software and other practical works are designed
|
37 |
+
to take away your freedom to share and change the works. By contrast,
|
38 |
+
the GNU General Public License is intended to guarantee your freedom to
|
39 |
+
share and change all versions of a program--to make sure it remains free
|
40 |
+
software for all its users. We, the Free Software Foundation, use the
|
41 |
+
GNU General Public License for most of our software; it applies also to
|
42 |
+
any other work released this way by its authors. You can apply it to
|
43 |
+
your programs, too.
|
44 |
+
|
45 |
+
When we speak of free software, we are referring to freedom, not
|
46 |
+
price. Our General Public Licenses are designed to make sure that you
|
47 |
+
have the freedom to distribute copies of free software (and charge for
|
48 |
+
them if you wish), that you receive source code or can get it if you
|
49 |
+
want it, that you can change the software or use pieces of it in new
|
50 |
+
free programs, and that you know you can do these things.
|
51 |
+
|
52 |
+
To protect your rights, we need to prevent others from denying you
|
53 |
+
these rights or asking you to surrender the rights. Therefore, you have
|
54 |
+
certain responsibilities if you distribute copies of the software, or if
|
55 |
+
you modify it: responsibilities to respect the freedom of others.
|
56 |
+
|
57 |
+
For example, if you distribute copies of such a program, whether
|
58 |
+
gratis or for a fee, you must pass on to the recipients the same
|
59 |
+
freedoms that you received. You must make sure that they, too, receive
|
60 |
+
or can get the source code. And you must show them these terms so they
|
61 |
+
know their rights.
|
62 |
+
|
63 |
+
Developers that use the GNU GPL protect your rights with two steps:
|
64 |
+
(1) assert copyright on the software, and (2) offer you this License
|
65 |
+
giving you legal permission to copy, distribute and/or modify it.
|
66 |
+
|
67 |
+
For the developers' and authors' protection, the GPL clearly explains
|
68 |
+
that there is no warranty for this free software. For both users' and
|
69 |
+
authors' sake, the GPL requires that modified versions be marked as
|
70 |
+
changed, so that their problems will not be attributed erroneously to
|
71 |
+
authors of previous versions.
|
72 |
+
|
73 |
+
Some devices are designed to deny users access to install or run
|
74 |
+
modified versions of the software inside them, although the manufacturer
|
75 |
+
can do so. This is fundamentally incompatible with the aim of
|
76 |
+
protecting users' freedom to change the software. The systematic
|
77 |
+
pattern of such abuse occurs in the area of products for individuals to
|
78 |
+
use, which is precisely where it is most unacceptable. Therefore, we
|
79 |
+
have designed this version of the GPL to prohibit the practice for those
|
80 |
+
products. If such problems arise substantially in other domains, we
|
81 |
+
stand ready to extend this provision to those domains in future versions
|
82 |
+
of the GPL, as needed to protect the freedom of users.
|
83 |
+
|
84 |
+
Finally, every program is threatened constantly by software patents.
|
85 |
+
States should not allow patents to restrict development and use of
|
86 |
+
software on general-purpose computers, but in those that do, we wish to
|
87 |
+
avoid the special danger that patents applied to a free program could
|
88 |
+
make it effectively proprietary. To prevent this, the GPL assures that
|
89 |
+
patents cannot be used to render the program non-free.
|
90 |
+
|
91 |
+
The precise terms and conditions for copying, distribution and
|
92 |
+
modification follow.
|
93 |
+
|
94 |
+
TERMS AND CONDITIONS
|
95 |
+
|
96 |
+
0. Definitions.
|
97 |
+
|
98 |
+
"This License" refers to version 3 of the GNU General Public License.
|
99 |
+
|
100 |
+
"Copyright" also means copyright-like laws that apply to other kinds of
|
101 |
+
works, such as semiconductor masks.
|
102 |
+
|
103 |
+
"The Program" refers to any copyrightable work licensed under this
|
104 |
+
License. Each licensee is addressed as "you". "Licensees" and
|
105 |
+
"recipients" may be individuals or organizations.
|
106 |
+
|
107 |
+
To "modify" a work means to copy from or adapt all or part of the work
|
108 |
+
in a fashion requiring copyright permission, other than the making of an
|
109 |
+
exact copy. The resulting work is called a "modified version" of the
|
110 |
+
earlier work or a work "based on" the earlier work.
|
111 |
+
|
112 |
+
A "covered work" means either the unmodified Program or a work based
|
113 |
+
on the Program.
|
114 |
+
|
115 |
+
To "propagate" a work means to do anything with it that, without
|
116 |
+
permission, would make you directly or secondarily liable for
|
117 |
+
infringement under applicable copyright law, except executing it on a
|
118 |
+
computer or modifying a private copy. Propagation includes copying,
|
119 |
+
distribution (with or without modification), making available to the
|
120 |
+
public, and in some countries other activities as well.
|
121 |
+
|
122 |
+
To "convey" a work means any kind of propagation that enables other
|
123 |
+
parties to make or receive copies. Mere interaction with a user through
|
124 |
+
a computer network, with no transfer of a copy, is not conveying.
|
125 |
+
|
126 |
+
An interactive user interface displays "Appropriate Legal Notices"
|
127 |
+
to the extent that it includes a convenient and prominently visible
|
128 |
+
feature that (1) displays an appropriate copyright notice, and (2)
|
129 |
+
tells the user that there is no warranty for the work (except to the
|
130 |
+
extent that warranties are provided), that licensees may convey the
|
131 |
+
work under this License, and how to view a copy of this License. If
|
132 |
+
the interface presents a list of user commands or options, such as a
|
133 |
+
menu, a prominent item in the list meets this criterion.
|
134 |
+
|
135 |
+
1. Source Code.
|
136 |
+
|
137 |
+
The "source code" for a work means the preferred form of the work
|
138 |
+
for making modifications to it. "Object code" means any non-source
|
139 |
+
form of a work.
|
140 |
+
|
141 |
+
A "Standard Interface" means an interface that either is an official
|
142 |
+
standard defined by a recognized standards body, or, in the case of
|
143 |
+
interfaces specified for a particular programming language, one that
|
144 |
+
is widely used among developers working in that language.
|
145 |
+
|
146 |
+
The "System Libraries" of an executable work include anything, other
|
147 |
+
than the work as a whole, that (a) is included in the normal form of
|
148 |
+
packaging a Major Component, but which is not part of that Major
|
149 |
+
Component, and (b) serves only to enable use of the work with that
|
150 |
+
Major Component, or to implement a Standard Interface for which an
|
151 |
+
implementation is available to the public in source code form. A
|
152 |
+
"Major Component", in this context, means a major essential component
|
153 |
+
(kernel, window system, and so on) of the specific operating system
|
154 |
+
(if any) on which the executable work runs, or a compiler used to
|
155 |
+
produce the work, or an object code interpreter used to run it.
|
156 |
+
|
157 |
+
The "Corresponding Source" for a work in object code form means all
|
158 |
+
the source code needed to generate, install, and (for an executable
|
159 |
+
work) run the object code and to modify the work, including scripts to
|
160 |
+
control those activities. However, it does not include the work's
|
161 |
+
System Libraries, or general-purpose tools or generally available free
|
162 |
+
programs which are used unmodified in performing those activities but
|
163 |
+
which are not part of the work. For example, Corresponding Source
|
164 |
+
includes interface definition files associated with source files for
|
165 |
+
the work, and the source code for shared libraries and dynamically
|
166 |
+
linked subprograms that the work is specifically designed to require,
|
167 |
+
such as by intimate data communication or control flow between those
|
168 |
+
subprograms and other parts of the work.
|
169 |
+
|
170 |
+
The Corresponding Source need not include anything that users
|
171 |
+
can regenerate automatically from other parts of the Corresponding
|
172 |
+
Source.
|
173 |
+
|
174 |
+
The Corresponding Source for a work in source code form is that
|
175 |
+
same work.
|
176 |
+
|
177 |
+
2. Basic Permissions.
|
178 |
+
|
179 |
+
All rights granted under this License are granted for the term of
|
180 |
+
copyright on the Program, and are irrevocable provided the stated
|
181 |
+
conditions are met. This License explicitly affirms your unlimited
|
182 |
+
permission to run the unmodified Program. The output from running a
|
183 |
+
covered work is covered by this License only if the output, given its
|
184 |
+
content, constitutes a covered work. This License acknowledges your
|
185 |
+
rights of fair use or other equivalent, as provided by copyright law.
|
186 |
+
|
187 |
+
You may make, run and propagate covered works that you do not
|
188 |
+
convey, without conditions so long as your license otherwise remains
|
189 |
+
in force. You may convey covered works to others for the sole purpose
|
190 |
+
of having them make modifications exclusively for you, or provide you
|
191 |
+
with facilities for running those works, provided that you comply with
|
192 |
+
the terms of this License in conveying all material for which you do
|
193 |
+
not control copyright. Those thus making or running the covered works
|
194 |
+
for you must do so exclusively on your behalf, under your direction
|
195 |
+
and control, on terms that prohibit them from making any copies of
|
196 |
+
your copyrighted material outside their relationship with you.
|
197 |
+
|
198 |
+
Conveying under any other circumstances is permitted solely under
|
199 |
+
the conditions stated below. Sublicensing is not allowed; section 10
|
200 |
+
makes it unnecessary.
|
201 |
+
|
202 |
+
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
203 |
+
|
204 |
+
No covered work shall be deemed part of an effective technological
|
205 |
+
measure under any applicable law fulfilling obligations under article
|
206 |
+
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
207 |
+
similar laws prohibiting or restricting circumvention of such
|
208 |
+
measures.
|
209 |
+
|
210 |
+
When you convey a covered work, you waive any legal power to forbid
|
211 |
+
circumvention of technological measures to the extent such circumvention
|
212 |
+
is effected by exercising rights under this License with respect to
|
213 |
+
the covered work, and you disclaim any intention to limit operation or
|
214 |
+
modification of the work as a means of enforcing, against the work's
|
215 |
+
users, your or third parties' legal rights to forbid circumvention of
|
216 |
+
technological measures.
|
217 |
+
|
218 |
+
4. Conveying Verbatim Copies.
|
219 |
+
|
220 |
+
You may convey verbatim copies of the Program's source code as you
|
221 |
+
receive it, in any medium, provided that you conspicuously and
|
222 |
+
appropriately publish on each copy an appropriate copyright notice;
|
223 |
+
keep intact all notices stating that this License and any
|
224 |
+
non-permissive terms added in accord with section 7 apply to the code;
|
225 |
+
keep intact all notices of the absence of any warranty; and give all
|
226 |
+
recipients a copy of this License along with the Program.
|
227 |
+
|
228 |
+
You may charge any price or no price for each copy that you convey,
|
229 |
+
and you may offer support or warranty protection for a fee.
|
230 |
+
|
231 |
+
5. Conveying Modified Source Versions.
|
232 |
+
|
233 |
+
You may convey a work based on the Program, or the modifications to
|
234 |
+
produce it from the Program, in the form of source code under the
|
235 |
+
terms of section 4, provided that you also meet all of these conditions:
|
236 |
+
|
237 |
+
a) The work must carry prominent notices stating that you modified
|
238 |
+
it, and giving a relevant date.
|
239 |
+
|
240 |
+
b) The work must carry prominent notices stating that it is
|
241 |
+
released under this License and any conditions added under section
|
242 |
+
7. This requirement modifies the requirement in section 4 to
|
243 |
+
"keep intact all notices".
|
244 |
+
|
245 |
+
c) You must license the entire work, as a whole, under this
|
246 |
+
License to anyone who comes into possession of a copy. This
|
247 |
+
License will therefore apply, along with any applicable section 7
|
248 |
+
additional terms, to the whole of the work, and all its parts,
|
249 |
+
regardless of how they are packaged. This License gives no
|
250 |
+
permission to license the work in any other way, but it does not
|
251 |
+
invalidate such permission if you have separately received it.
|
252 |
+
|
253 |
+
d) If the work has interactive user interfaces, each must display
|
254 |
+
Appropriate Legal Notices; however, if the Program has interactive
|
255 |
+
interfaces that do not display Appropriate Legal Notices, your
|
256 |
+
work need not make them do so.
|
257 |
+
|
258 |
+
A compilation of a covered work with other separate and independent
|
259 |
+
works, which are not by their nature extensions of the covered work,
|
260 |
+
and which are not combined with it such as to form a larger program,
|
261 |
+
in or on a volume of a storage or distribution medium, is called an
|
262 |
+
"aggregate" if the compilation and its resulting copyright are not
|
263 |
+
used to limit the access or legal rights of the compilation's users
|
264 |
+
beyond what the individual works permit. Inclusion of a covered work
|
265 |
+
in an aggregate does not cause this License to apply to the other
|
266 |
+
parts of the aggregate.
|
267 |
+
|
268 |
+
6. Conveying Non-Source Forms.
|
269 |
+
|
270 |
+
You may convey a covered work in object code form under the terms
|
271 |
+
of sections 4 and 5, provided that you also convey the
|
272 |
+
machine-readable Corresponding Source under the terms of this License,
|
273 |
+
in one of these ways:
|
274 |
+
|
275 |
+
a) Convey the object code in, or embodied in, a physical product
|
276 |
+
(including a physical distribution medium), accompanied by the
|
277 |
+
Corresponding Source fixed on a durable physical medium
|
278 |
+
customarily used for software interchange.
|
279 |
+
|
280 |
+
b) Convey the object code in, or embodied in, a physical product
|
281 |
+
(including a physical distribution medium), accompanied by a
|
282 |
+
written offer, valid for at least three years and valid for as
|
283 |
+
long as you offer spare parts or customer support for that product
|
284 |
+
model, to give anyone who possesses the object code either (1) a
|
285 |
+
copy of the Corresponding Source for all the software in the
|
286 |
+
product that is covered by this License, on a durable physical
|
287 |
+
medium customarily used for software interchange, for a price no
|
288 |
+
more than your reasonable cost of physically performing this
|
289 |
+
conveying of source, or (2) access to copy the
|
290 |
+
Corresponding Source from a network server at no charge.
|
291 |
+
|
292 |
+
c) Convey individual copies of the object code with a copy of the
|
293 |
+
written offer to provide the Corresponding Source. This
|
294 |
+
alternative is allowed only occasionally and noncommercially, and
|
295 |
+
only if you received the object code with such an offer, in accord
|
296 |
+
with subsection 6b.
|
297 |
+
|
298 |
+
d) Convey the object code by offering access from a designated
|
299 |
+
place (gratis or for a charge), and offer equivalent access to the
|
300 |
+
Corresponding Source in the same way through the same place at no
|
301 |
+
further charge. You need not require recipients to copy the
|
302 |
+
Corresponding Source along with the object code. If the place to
|
303 |
+
copy the object code is a network server, the Corresponding Source
|
304 |
+
may be on a different server (operated by you or a third party)
|
305 |
+
that supports equivalent copying facilities, provided you maintain
|
306 |
+
clear directions next to the object code saying where to find the
|
307 |
+
Corresponding Source. Regardless of what server hosts the
|
308 |
+
Corresponding Source, you remain obligated to ensure that it is
|
309 |
+
available for as long as needed to satisfy these requirements.
|
310 |
+
|
311 |
+
e) Convey the object code using peer-to-peer transmission, provided
|
312 |
+
you inform other peers where the object code and Corresponding
|
313 |
+
Source of the work are being offered to the general public at no
|
314 |
+
charge under subsection 6d.
|
315 |
+
|
316 |
+
A separable portion of the object code, whose source code is excluded
|
317 |
+
from the Corresponding Source as a System Library, need not be
|
318 |
+
included in conveying the object code work.
|
319 |
+
|
320 |
+
A "User Product" is either (1) a "consumer product", which means any
|
321 |
+
tangible personal property which is normally used for personal, family,
|
322 |
+
or household purposes, or (2) anything designed or sold for incorporation
|
323 |
+
into a dwelling. In determining whether a product is a consumer product,
|
324 |
+
doubtful cases shall be resolved in favor of coverage. For a particular
|
325 |
+
product received by a particular user, "normally used" refers to a
|
326 |
+
typical or common use of that class of product, regardless of the status
|
327 |
+
of the particular user or of the way in which the particular user
|
328 |
+
actually uses, or expects or is expected to use, the product. A product
|
329 |
+
is a consumer product regardless of whether the product has substantial
|
330 |
+
commercial, industrial or non-consumer uses, unless such uses represent
|
331 |
+
the only significant mode of use of the product.
|
332 |
+
|
333 |
+
"Installation Information" for a User Product means any methods,
|
334 |
+
procedures, authorization keys, or other information required to install
|
335 |
+
and execute modified versions of a covered work in that User Product from
|
336 |
+
a modified version of its Corresponding Source. The information must
|
337 |
+
suffice to ensure that the continued functioning of the modified object
|
338 |
+
code is in no case prevented or interfered with solely because
|
339 |
+
modification has been made.
|
340 |
+
|
341 |
+
If you convey an object code work under this section in, or with, or
|
342 |
+
specifically for use in, a User Product, and the conveying occurs as
|
343 |
+
part of a transaction in which the right of possession and use of the
|
344 |
+
User Product is transferred to the recipient in perpetuity or for a
|
345 |
+
fixed term (regardless of how the transaction is characterized), the
|
346 |
+
Corresponding Source conveyed under this section must be accompanied
|
347 |
+
by the Installation Information. But this requirement does not apply
|
348 |
+
if neither you nor any third party retains the ability to install
|
349 |
+
modified object code on the User Product (for example, the work has
|
350 |
+
been installed in ROM).
|
351 |
+
|
352 |
+
The requirement to provide Installation Information does not include a
|
353 |
+
requirement to continue to provide support service, warranty, or updates
|
354 |
+
for a work that has been modified or installed by the recipient, or for
|
355 |
+
the User Product in which it has been modified or installed. Access to a
|
356 |
+
network may be denied when the modification itself materially and
|
357 |
+
adversely affects the operation of the network or violates the rules and
|
358 |
+
protocols for communication across the network.
|
359 |
+
|
360 |
+
Corresponding Source conveyed, and Installation Information provided,
|
361 |
+
in accord with this section must be in a format that is publicly
|
362 |
+
documented (and with an implementation available to the public in
|
363 |
+
source code form), and must require no special password or key for
|
364 |
+
unpacking, reading or copying.
|
365 |
+
|
366 |
+
7. Additional Terms.
|
367 |
+
|
368 |
+
"Additional permissions" are terms that supplement the terms of this
|
369 |
+
License by making exceptions from one or more of its conditions.
|
370 |
+
Additional permissions that are applicable to the entire Program shall
|
371 |
+
be treated as though they were included in this License, to the extent
|
372 |
+
that they are valid under applicable law. If additional permissions
|
373 |
+
apply only to part of the Program, that part may be used separately
|
374 |
+
under those permissions, but the entire Program remains governed by
|
375 |
+
this License without regard to the additional permissions.
|
376 |
+
|
377 |
+
When you convey a copy of a covered work, you may at your option
|
378 |
+
remove any additional permissions from that copy, or from any part of
|
379 |
+
it. (Additional permissions may be written to require their own
|
380 |
+
removal in certain cases when you modify the work.) You may place
|
381 |
+
additional permissions on material, added by you to a covered work,
|
382 |
+
for which you have or can give appropriate copyright permission.
|
383 |
+
|
384 |
+
Notwithstanding any other provision of this License, for material you
|
385 |
+
add to a covered work, you may (if authorized by the copyright holders of
|
386 |
+
that material) supplement the terms of this License with terms:
|
387 |
+
|
388 |
+
a) Disclaiming warranty or limiting liability differently from the
|
389 |
+
terms of sections 15 and 16 of this License; or
|
390 |
+
|
391 |
+
b) Requiring preservation of specified reasonable legal notices or
|
392 |
+
author attributions in that material or in the Appropriate Legal
|
393 |
+
Notices displayed by works containing it; or
|
394 |
+
|
395 |
+
c) Prohibiting misrepresentation of the origin of that material, or
|
396 |
+
requiring that modified versions of such material be marked in
|
397 |
+
reasonable ways as different from the original version; or
|
398 |
+
|
399 |
+
d) Limiting the use for publicity purposes of names of licensors or
|
400 |
+
authors of the material; or
|
401 |
+
|
402 |
+
e) Declining to grant rights under trademark law for use of some
|
403 |
+
trade names, trademarks, or service marks; or
|
404 |
+
|
405 |
+
f) Requiring indemnification of licensors and authors of that
|
406 |
+
material by anyone who conveys the material (or modified versions of
|
407 |
+
it) with contractual assumptions of liability to the recipient, for
|
408 |
+
any liability that these contractual assumptions directly impose on
|
409 |
+
those licensors and authors.
|
410 |
+
|
411 |
+
All other non-permissive additional terms are considered "further
|
412 |
+
restrictions" within the meaning of section 10. If the Program as you
|
413 |
+
received it, or any part of it, contains a notice stating that it is
|
414 |
+
governed by this License along with a term that is a further
|
415 |
+
restriction, you may remove that term. If a license document contains
|
416 |
+
a further restriction but permits relicensing or conveying under this
|
417 |
+
License, you may add to a covered work material governed by the terms
|
418 |
+
of that license document, provided that the further restriction does
|
419 |
+
not survive such relicensing or conveying.
|
420 |
+
|
421 |
+
If you add terms to a covered work in accord with this section, you
|
422 |
+
must place, in the relevant source files, a statement of the
|
423 |
+
additional terms that apply to those files, or a notice indicating
|
424 |
+
where to find the applicable terms.
|
425 |
+
|
426 |
+
Additional terms, permissive or non-permissive, may be stated in the
|
427 |
+
form of a separately written license, or stated as exceptions;
|
428 |
+
the above requirements apply either way.
|
429 |
+
|
430 |
+
8. Termination.
|
431 |
+
|
432 |
+
You may not propagate or modify a covered work except as expressly
|
433 |
+
provided under this License. Any attempt otherwise to propagate or
|
434 |
+
modify it is void, and will automatically terminate your rights under
|
435 |
+
this License (including any patent licenses granted under the third
|
436 |
+
paragraph of section 11).
|
437 |
+
|
438 |
+
However, if you cease all violation of this License, then your
|
439 |
+
license from a particular copyright holder is reinstated (a)
|
440 |
+
provisionally, unless and until the copyright holder explicitly and
|
441 |
+
finally terminates your license, and (b) permanently, if the copyright
|
442 |
+
holder fails to notify you of the violation by some reasonable means
|
443 |
+
prior to 60 days after the cessation.
|
444 |
+
|
445 |
+
Moreover, your license from a particular copyright holder is
|
446 |
+
reinstated permanently if the copyright holder notifies you of the
|
447 |
+
violation by some reasonable means, this is the first time you have
|
448 |
+
received notice of violation of this License (for any work) from that
|
449 |
+
copyright holder, and you cure the violation prior to 30 days after
|
450 |
+
your receipt of the notice.
|
451 |
+
|
452 |
+
Termination of your rights under this section does not terminate the
|
453 |
+
licenses of parties who have received copies or rights from you under
|
454 |
+
this License. If your rights have been terminated and not permanently
|
455 |
+
reinstated, you do not qualify to receive new licenses for the same
|
456 |
+
material under section 10.
|
457 |
+
|
458 |
+
9. Acceptance Not Required for Having Copies.
|
459 |
+
|
460 |
+
You are not required to accept this License in order to receive or
|
461 |
+
run a copy of the Program. Ancillary propagation of a covered work
|
462 |
+
occurring solely as a consequence of using peer-to-peer transmission
|
463 |
+
to receive a copy likewise does not require acceptance. However,
|
464 |
+
nothing other than this License grants you permission to propagate or
|
465 |
+
modify any covered work. These actions infringe copyright if you do
|
466 |
+
not accept this License. Therefore, by modifying or propagating a
|
467 |
+
covered work, you indicate your acceptance of this License to do so.
|
468 |
+
|
469 |
+
10. Automatic Licensing of Downstream Recipients.
|
470 |
+
|
471 |
+
Each time you convey a covered work, the recipient automatically
|
472 |
+
receives a license from the original licensors, to run, modify and
|
473 |
+
propagate that work, subject to this License. You are not responsible
|
474 |
+
for enforcing compliance by third parties with this License.
|
475 |
+
|
476 |
+
An "entity transaction" is a transaction transferring control of an
|
477 |
+
organization, or substantially all assets of one, or subdividing an
|
478 |
+
organization, or merging organizations. If propagation of a covered
|
479 |
+
work results from an entity transaction, each party to that
|
480 |
+
transaction who receives a copy of the work also receives whatever
|
481 |
+
licenses to the work the party's predecessor in interest had or could
|
482 |
+
give under the previous paragraph, plus a right to possession of the
|
483 |
+
Corresponding Source of the work from the predecessor in interest, if
|
484 |
+
the predecessor has it or can get it with reasonable efforts.
|
485 |
+
|
486 |
+
You may not impose any further restrictions on the exercise of the
|
487 |
+
rights granted or affirmed under this License. For example, you may
|
488 |
+
not impose a license fee, royalty, or other charge for exercise of
|
489 |
+
rights granted under this License, and you may not initiate litigation
|
490 |
+
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
491 |
+
any patent claim is infringed by making, using, selling, offering for
|
492 |
+
sale, or importing the Program or any portion of it.
|
493 |
+
|
494 |
+
11. Patents.
|
495 |
+
|
496 |
+
A "contributor" is a copyright holder who authorizes use under this
|
497 |
+
License of the Program or a work on which the Program is based. The
|
498 |
+
work thus licensed is called the contributor's "contributor version".
|
499 |
+
|
500 |
+
A contributor's "essential patent claims" are all patent claims
|
501 |
+
owned or controlled by the contributor, whether already acquired or
|
502 |
+
hereafter acquired, that would be infringed by some manner, permitted
|
503 |
+
by this License, of making, using, or selling its contributor version,
|
504 |
+
but do not include claims that would be infringed only as a
|
505 |
+
consequence of further modification of the contributor version. For
|
506 |
+
purposes of this definition, "control" includes the right to grant
|
507 |
+
patent sublicenses in a manner consistent with the requirements of
|
508 |
+
this License.
|
509 |
+
|
510 |
+
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
511 |
+
patent license under the contributor's essential patent claims, to
|
512 |
+
make, use, sell, offer for sale, import and otherwise run, modify and
|
513 |
+
propagate the contents of its contributor version.
|
514 |
+
|
515 |
+
In the following three paragraphs, a "patent license" is any express
|
516 |
+
agreement or commitment, however denominated, not to enforce a patent
|
517 |
+
(such as an express permission to practice a patent or covenant not to
|
518 |
+
sue for patent infringement). To "grant" such a patent license to a
|
519 |
+
party means to make such an agreement or commitment not to enforce a
|
520 |
+
patent against the party.
|
521 |
+
|
522 |
+
If you convey a covered work, knowingly relying on a patent license,
|
523 |
+
and the Corresponding Source of the work is not available for anyone
|
524 |
+
to copy, free of charge and under the terms of this License, through a
|
525 |
+
publicly available network server or other readily accessible means,
|
526 |
+
then you must either (1) cause the Corresponding Source to be so
|
527 |
+
available, or (2) arrange to deprive yourself of the benefit of the
|
528 |
+
patent license for this particular work, or (3) arrange, in a manner
|
529 |
+
consistent with the requirements of this License, to extend the patent
|
530 |
+
license to downstream recipients. "Knowingly relying" means you have
|
531 |
+
actual knowledge that, but for the patent license, your conveying the
|
532 |
+
covered work in a country, or your recipient's use of the covered work
|
533 |
+
in a country, would infringe one or more identifiable patents in that
|
534 |
+
country that you have reason to believe are valid.
|
535 |
+
|
536 |
+
If, pursuant to or in connection with a single transaction or
|
537 |
+
arrangement, you convey, or propagate by procuring conveyance of, a
|
538 |
+
covered work, and grant a patent license to some of the parties
|
539 |
+
receiving the covered work authorizing them to use, propagate, modify
|
540 |
+
or convey a specific copy of the covered work, then the patent license
|
541 |
+
you grant is automatically extended to all recipients of the covered
|
542 |
+
work and works based on it.
|
543 |
+
|
544 |
+
A patent license is "discriminatory" if it does not include within
|
545 |
+
the scope of its coverage, prohibits the exercise of, or is
|
546 |
+
conditioned on the non-exercise of one or more of the rights that are
|
547 |
+
specifically granted under this License. You may not convey a covered
|
548 |
+
work if you are a party to an arrangement with a third party that is
|
549 |
+
in the business of distributing software, under which you make payment
|
550 |
+
to the third party based on the extent of your activity of conveying
|
551 |
+
the work, and under which the third party grants, to any of the
|
552 |
+
parties who would receive the covered work from you, a discriminatory
|
553 |
+
patent license (a) in connection with copies of the covered work
|
554 |
+
conveyed by you (or copies made from those copies), or (b) primarily
|
555 |
+
for and in connection with specific products or compilations that
|
556 |
+
contain the covered work, unless you entered into that arrangement,
|
557 |
+
or that patent license was granted, prior to 28 March 2007.
|
558 |
+
|
559 |
+
Nothing in this License shall be construed as excluding or limiting
|
560 |
+
any implied license or other defenses to infringement that may
|
561 |
+
otherwise be available to you under applicable patent law.
|
562 |
+
|
563 |
+
12. No Surrender of Others' Freedom.
|
564 |
+
|
565 |
+
If conditions are imposed on you (whether by court order, agreement or
|
566 |
+
otherwise) that contradict the conditions of this License, they do not
|
567 |
+
excuse you from the conditions of this License. If you cannot convey a
|
568 |
+
covered work so as to satisfy simultaneously your obligations under this
|
569 |
+
License and any other pertinent obligations, then as a consequence you may
|
570 |
+
not convey it at all. For example, if you agree to terms that obligate you
|
571 |
+
to collect a royalty for further conveying from those to whom you convey
|
572 |
+
the Program, the only way you could satisfy both those terms and this
|
573 |
+
License would be to refrain entirely from conveying the Program.
|
574 |
+
|
575 |
+
13. Use with the GNU Affero General Public License.
|
576 |
+
|
577 |
+
Notwithstanding any other provision of this License, you have
|
578 |
+
permission to link or combine any covered work with a work licensed
|
579 |
+
under version 3 of the GNU Affero General Public License into a single
|
580 |
+
combined work, and to convey the resulting work. The terms of this
|
581 |
+
License will continue to apply to the part which is the covered work,
|
582 |
+
but the special requirements of the GNU Affero General Public License,
|
583 |
+
section 13, concerning interaction through a network will apply to the
|
584 |
+
combination as such.
|
585 |
+
|
586 |
+
14. Revised Versions of this License.
|
587 |
+
|
588 |
+
The Free Software Foundation may publish revised and/or new versions of
|
589 |
+
the GNU General Public License from time to time. Such new versions will
|
590 |
+
be similar in spirit to the present version, but may differ in detail to
|
591 |
+
address new problems or concerns.
|
592 |
+
|
593 |
+
Each version is given a distinguishing version number. If the
|
594 |
+
Program specifies that a certain numbered version of the GNU General
|
595 |
+
Public License "or any later version" applies to it, you have the
|
596 |
+
option of following the terms and conditions either of that numbered
|
597 |
+
version or of any later version published by the Free Software
|
598 |
+
Foundation. If the Program does not specify a version number of the
|
599 |
+
GNU General Public License, you may choose any version ever published
|
600 |
+
by the Free Software Foundation.
|
601 |
+
|
602 |
+
If the Program specifies that a proxy can decide which future
|
603 |
+
versions of the GNU General Public License can be used, that proxy's
|
604 |
+
public statement of acceptance of a version permanently authorizes you
|
605 |
+
to choose that version for the Program.
|
606 |
+
|
607 |
+
Later license versions may give you additional or different
|
608 |
+
permissions. However, no additional obligations are imposed on any
|
609 |
+
author or copyright holder as a result of your choosing to follow a
|
610 |
+
later version.
|
611 |
+
|
612 |
+
15. Disclaimer of Warranty.
|
613 |
+
|
614 |
+
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
615 |
+
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
616 |
+
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
617 |
+
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
618 |
+
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
619 |
+
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
620 |
+
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
621 |
+
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
622 |
+
|
623 |
+
16. Limitation of Liability.
|
624 |
+
|
625 |
+
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
626 |
+
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
627 |
+
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
628 |
+
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
629 |
+
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
630 |
+
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
631 |
+
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
632 |
+
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
633 |
+
SUCH DAMAGES.
|
634 |
+
|
635 |
+
17. Interpretation of Sections 15 and 16.
|
636 |
+
|
637 |
+
If the disclaimer of warranty and limitation of liability provided
|
638 |
+
above cannot be given local legal effect according to their terms,
|
639 |
+
reviewing courts shall apply local law that most closely approximates
|
640 |
+
an absolute waiver of all civil liability in connection with the
|
641 |
+
Program, unless a warranty or assumption of liability accompanies a
|
642 |
+
copy of the Program in return for a fee.
|
643 |
+
|
644 |
+
END OF TERMS AND CONDITIONS
|
645 |
+
|
646 |
+
How to Apply These Terms to Your New Programs
|
647 |
+
|
648 |
+
If you develop a new program, and you want it to be of the greatest
|
649 |
+
possible use to the public, the best way to achieve this is to make it
|
650 |
+
free software which everyone can redistribute and change under these terms.
|
651 |
+
|
652 |
+
To do so, attach the following notices to the program. It is safest
|
653 |
+
to attach them to the start of each source file to most effectively
|
654 |
+
state the exclusion of warranty; and each file should have at least
|
655 |
+
the "copyright" line and a pointer to where the full notice is found.
|
656 |
+
|
657 |
+
<one line to give the program's name and a brief idea of what it does.>
|
658 |
+
Copyright (C) <year> <name of author>
|
659 |
+
|
660 |
+
This program is free software: you can redistribute it and/or modify
|
661 |
+
it under the terms of the GNU General Public License as published by
|
662 |
+
the Free Software Foundation, either version 3 of the License, or
|
663 |
+
(at your option) any later version.
|
664 |
+
|
665 |
+
This program is distributed in the hope that it will be useful,
|
666 |
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
667 |
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
668 |
+
GNU General Public License for more details.
|
669 |
+
|
670 |
+
You should have received a copy of the GNU General Public License
|
671 |
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
672 |
+
|
673 |
+
Also add information on how to contact you by electronic and paper mail.
|
674 |
+
|
675 |
+
If the program does terminal interaction, make it output a short
|
676 |
+
notice like this when it starts in an interactive mode:
|
677 |
+
|
678 |
+
<program> Copyright (C) <year> <name of author>
|
679 |
+
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
680 |
+
This is free software, and you are welcome to redistribute it
|
681 |
+
under certain conditions; type `show c' for details.
|
682 |
+
|
683 |
+
The hypothetical commands `show w' and `show c' should show the appropriate
|
684 |
+
parts of the General Public License. Of course, your program's commands
|
685 |
+
might be different; for a GUI interface, you would use an "about box".
|
686 |
+
|
687 |
+
You should also get your employer (if you work as a programmer) or school,
|
688 |
+
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
689 |
+
For more information on this, and how to apply and follow the GNU GPL, see
|
690 |
+
<https://www.gnu.org/licenses/>.
|
691 |
+
|
692 |
+
The GNU General Public License does not permit incorporating your program
|
693 |
+
into proprietary programs. If your program is a subroutine library, you
|
694 |
+
may consider it more useful to permit linking proprietary applications with
|
695 |
+
the library. If this is what you want to do, use the GNU Lesser General
|
696 |
+
Public License instead of this License. But first, please read
|
697 |
+
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
trunk/printful-shipping.php
ADDED
@@ -0,0 +1,142 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
Plugin Name: Printful Integration for WooCommerce
|
4 |
+
Plugin URI: https://wordpress.org/plugins/printful-shipping-for-woocommerce/
|
5 |
+
Description: Connects your Printful account with WooCommerce.
|
6 |
+
Version: 2.1.32
|
7 |
+
Author: Printful
|
8 |
+
Author URI: http://www.printful.com
|
9 |
+
License: GPL3 https://www.gnu.org/licenses/gpl-3.0.en.html
|
10 |
+
Text Domain: printful
|
11 |
+
WC requires at least: 3.0.0
|
12 |
+
WC tested up to: 6.5.1
|
13 |
+
*/
|
14 |
+
|
15 |
+
if ( ! defined( 'ABSPATH' ) ) exit;
|
16 |
+
|
17 |
+
if ( ! defined( 'PF_PLUGIN_FILE' ) ) {
|
18 |
+
define( 'PF_PLUGIN_FILE', __FILE__ );
|
19 |
+
}
|
20 |
+
|
21 |
+
class Printful_Base {
|
22 |
+
|
23 |
+
const VERSION = '2.1.32';
|
24 |
+
const PF_HOST = 'https://www.printful.com/';
|
25 |
+
const PF_API_HOST = 'https://api.printful.com/';
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Construct the plugin.
|
29 |
+
*/
|
30 |
+
public function __construct() {
|
31 |
+
add_action( 'plugins_loaded', array( $this, 'init' ) );
|
32 |
+
add_action( 'plugins_loaded', array( $this, 'printful_load_plugin_textdomain') );
|
33 |
+
}
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Initialize the plugin.
|
37 |
+
*/
|
38 |
+
public function init() {
|
39 |
+
|
40 |
+
if (!class_exists('WC_Integration')) {
|
41 |
+
return;
|
42 |
+
}
|
43 |
+
|
44 |
+
// WP REST API.
|
45 |
+
$this->rest_api_init();
|
46 |
+
|
47 |
+
//load required classes
|
48 |
+
require_once 'includes/class-printful-integration.php';
|
49 |
+
require_once 'includes/class-printful-carriers.php';
|
50 |
+
require_once 'includes/class-printful-taxes.php';
|
51 |
+
require_once 'includes/class-printful-shipping.php';
|
52 |
+
require_once 'includes/class-printful-request-log.php';
|
53 |
+
require_once 'includes/class-printful-admin.php';
|
54 |
+
require_once 'includes/class-printful-admin-dashboard.php';
|
55 |
+
require_once 'includes/class-printful-admin-settings.php';
|
56 |
+
require_once 'includes/class-printful-admin-status.php';
|
57 |
+
require_once 'includes/class-printful-admin-support.php';
|
58 |
+
require_once 'includes/class-printful-size-chart-tab.php';
|
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();
|
66 |
+
Printful_Shipping::init();
|
67 |
+
Printful_Request_log::init();
|
68 |
+
Printful_Admin::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' ) );
|
76 |
+
add_action( 'wp_ajax_ajax_force_check_connect_status', array( 'Printful_Integration', 'ajax_force_check_connect_status' ) );
|
77 |
+
add_action( 'wp_ajax_get_printful_stats', array( 'Printful_Admin_Dashboard', 'render_stats_ajax' ) );
|
78 |
+
add_action( 'wp_ajax_get_printful_orders', array( 'Printful_Admin_Dashboard', 'render_orders_ajax' ) );
|
79 |
+
add_action( 'wp_ajax_get_printful_status_checklist', array( 'Printful_Admin_Status', 'render_status_table_ajax' ) );
|
80 |
+
add_action( 'wp_ajax_get_printful_status_report', array( 'Printful_Admin_Support', 'render_status_report_ajax' ) );
|
81 |
+
add_action( 'wp_ajax_get_printful_carriers', array( 'Printful_Admin_Settings', 'render_carriers_ajax' ) );
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* Load Localisation files.
|
86 |
+
*
|
87 |
+
* Note: the first-loaded translation file overrides any following ones if the same translation is present.
|
88 |
+
*/
|
89 |
+
public function printful_load_plugin_textdomain() {
|
90 |
+
load_plugin_textdomain( 'printful', false, plugin_basename( dirname( PF_PLUGIN_FILE ) ) . '/i18n/languages' );
|
91 |
+
}
|
92 |
+
|
93 |
+
/**
|
94 |
+
* @return string
|
95 |
+
*/
|
96 |
+
public static function get_asset_url() {
|
97 |
+
return trailingslashit(plugin_dir_url(__FILE__)) . 'assets/';
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* @return string
|
102 |
+
*/
|
103 |
+
public static function get_printful_host() {
|
104 |
+
if ( defined( 'PF_DEV_HOST' ) ) {
|
105 |
+
return PF_DEV_HOST;
|
106 |
+
}
|
107 |
+
|
108 |
+
return self::PF_HOST;
|
109 |
+
}
|
110 |
+
|
111 |
+
/**
|
112 |
+
* @return string
|
113 |
+
*/
|
114 |
+
public static function get_printful_api_host() {
|
115 |
+
if ( defined( 'PF_DEV_API_HOST' ) ) {
|
116 |
+
return PF_DEV_API_HOST;
|
117 |
+
}
|
118 |
+
|
119 |
+
return self::PF_API_HOST;
|
120 |
+
}
|
121 |
+
|
122 |
+
private function rest_api_init()
|
123 |
+
{
|
124 |
+
// REST API was included starting WordPress 4.4.
|
125 |
+
if ( ! class_exists( 'WP_REST_Server' ) ) {
|
126 |
+
return;
|
127 |
+
}
|
128 |
+
|
129 |
+
// Init REST API routes.
|
130 |
+
add_action( 'rest_api_init', array( $this, 'register_rest_routes' ), 20);
|
131 |
+
}
|
132 |
+
|
133 |
+
public function register_rest_routes()
|
134 |
+
{
|
135 |
+
require_once 'includes/class-printful-rest-api-controller.php';
|
136 |
+
|
137 |
+
$printfulRestAPIController = new Printful_REST_API_Controller();
|
138 |
+
$printfulRestAPIController->register_routes();
|
139 |
+
}
|
140 |
+
}
|
141 |
+
|
142 |
+
new Printful_Base(); //let's go
|
trunk/readme.txt
ADDED
@@ -0,0 +1,438 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
=== Printful Integration for WooCommerce ===
|
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: 5.3
|
5 |
+
Tested up to: 6.0
|
6 |
+
Requires PHP: 5.6
|
7 |
+
Stable tag: 2.1.32
|
8 |
+
License: GPLv3 or later
|
9 |
+
License URI: https://www.gnu.org/licenses/gpl-3.0.en.html
|
10 |
+
|
11 |
+
Grow your store with the top print-on-demand dropshipping plugin
|
12 |
+
|
13 |
+
== Description ==
|
14 |
+
|
15 |
+
= Partner with the #1 print-on-demand drop shipping plugin. Over 300,000 active customers and counting! =
|
16 |
+
|
17 |
+
With Printful, you can focus on building your store. We'll print, fulfill and ship your products for you. Simply upload your design, add it to your store, and we'll do the rest.
|
18 |
+
|
19 |
+
We offer more than 305+ products in various categories—apparel, home & living items, jewelry, accessories, and more! Once your orders start rolling in, we’ll fulfill and ship each one under your brand at our in-house and partner facilities, located globally.
|
20 |
+
|
21 |
+
Printful customers have already made more than $1 billion in sales, so start selling today!
|
22 |
+
|
23 |
+
= How does Printful work? =
|
24 |
+
|
25 |
+
When a customer buys something from your WooCommerce store, the order is automatically imported to Printful, where we fulfill and ship it to them under your brand. You can then see your order, revenue, and profit statistics on your WooCommerce dashboard.
|
26 |
+
|
27 |
+
We’re trusted to print over 1 million products monthly and we can't wait to print yours!
|
28 |
+
|
29 |
+
Learn more about the WooCommerce + Printful integration [here](https://www.printful.com/dashboard/woocommerce/register):
|
30 |
+
|
31 |
+
= With WooCommerce + Printful, you get: =
|
32 |
+
|
33 |
+
- Control over your profit: We charge you to cover production while you set your retail price, and what's left is your profit
|
34 |
+
- Automated tax settings and live shipping rates
|
35 |
+
- Free design templates: Get creative with our Design Maker
|
36 |
+
- Product personalization: Sell designs your customers can personalize themselves
|
37 |
+
- Warehousing & Fulfillment services: Store your inventory at our warehouse, and we'll fulfill all your orders
|
38 |
+
- Stress-free shipping: Lost shipments are on us, and we offer shipping methods with tracking
|
39 |
+
- Discounted samples: Order samples of your products with 20% off + free shipping to select destinations to ensure your customers are getting quality products.
|
40 |
+
- 24/7 support: Get in touch with us using chat or email
|
41 |
+
|
42 |
+
Still not convinced? Check out our [demo store](https://woocommerce-demo.printful.com/) to see what's possible!
|
43 |
+
|
44 |
+
== Installation ==
|
45 |
+
1. Upload 'printful-shipping-for-woocommerce' to the '/wp-content/plugins/' directory
|
46 |
+
1. Activate the plugin through the 'Plugins' menu in WordPress
|
47 |
+
1. Click the "Connect" button or add your Printful API key manually to Printful->Settings tab
|
48 |
+
1. Enable shipping rate calculation in WooCommerce->Settings->Shipping->Printful Shipping tab
|
49 |
+
1. To automatically calculate taxes please check 'Enable taxes and tax calculations' under WooCommerce Tax settings.
|
50 |
+
1. Then go to Printful->Settings tab and check 'Calculated for all products shipped to states where Printful applies sales tax'.
|
51 |
+
|
52 |
+
== Frequently Asked Questions ==
|
53 |
+
|
54 |
+
= How do I get Printful API key? =
|
55 |
+
|
56 |
+
Go to https://www.printful.com/dashboard/store , select your WooCommerce store, click "Edit" and then click "Enable API Access". Your API key will be generated and displayed there.
|
57 |
+
|
58 |
+
== Screenshots ==
|
59 |
+
|
60 |
+
1. Connect to Printful
|
61 |
+
2. Printful dashboard
|
62 |
+
3. Integration settings
|
63 |
+
4. Plugin status page
|
64 |
+
5. Support page
|
65 |
+
|
66 |
+
== Upgrade Notice ==
|
67 |
+
|
68 |
+
= 2.1.32 =
|
69 |
+
Woocommerce compatibility raised to 6.5.1
|
70 |
+
WordPress compatibility raised to 6.0
|
71 |
+
|
72 |
+
= 2.1.31 =
|
73 |
+
Licence updated to GPLv3. WordPress compatibility raised to 5.9 and Woocommerce compatibility raised to 6.4
|
74 |
+
|
75 |
+
= 2.1.30 =
|
76 |
+
Additional billing setting check
|
77 |
+
|
78 |
+
= 2.1.29 =
|
79 |
+
WordPress compatibility raised to 5.8. Size guide bugfix
|
80 |
+
|
81 |
+
= 2.1.28 =
|
82 |
+
Woocommerce compatibility raised to 6.1
|
83 |
+
|
84 |
+
= 2.1.27 =
|
85 |
+
Woocommerce compatibility raised to 6.0. Size guide bug fix
|
86 |
+
|
87 |
+
= 2.1.26 =
|
88 |
+
Woocommerce compatibility raised to 5.9
|
89 |
+
|
90 |
+
= 2.1.25 =
|
91 |
+
Woocommerce compatibility raised to 5.5.
|
92 |
+
|
93 |
+
= 2.1.24 =
|
94 |
+
Woocommerce compatibility raised to 5.4. Minor bug fixes
|
95 |
+
|
96 |
+
= 2.1.23 =
|
97 |
+
Woocommerce compatibility raised to 5.3. Minor bug fixes
|
98 |
+
|
99 |
+
= 2.1.22 =
|
100 |
+
Woocommerce compatibility raised to 5.2
|
101 |
+
|
102 |
+
= 2.1.21 =
|
103 |
+
Advanced size guide translations fix
|
104 |
+
|
105 |
+
= 2.1.20 =
|
106 |
+
Woocommerce compatibility raised to 5.0
|
107 |
+
|
108 |
+
= 2.1.19 =
|
109 |
+
Woocommerce compatibility raised to 4.9
|
110 |
+
|
111 |
+
= 2.1.18 =
|
112 |
+
Woocommerce compatibility raised to 4.8
|
113 |
+
|
114 |
+
= 2.1.17 =
|
115 |
+
Woocommerce compatibility raised to 4.7
|
116 |
+
|
117 |
+
= 2.1.16 =
|
118 |
+
Woocommerce compatibility raised to 4.6
|
119 |
+
|
120 |
+
= 2.1.15 =
|
121 |
+
Fix translations
|
122 |
+
|
123 |
+
= 2.1.14 =
|
124 |
+
Advanced size guide feature
|
125 |
+
|
126 |
+
= 2.1.13 =
|
127 |
+
Woocommerce compatibility raised to 4.3
|
128 |
+
|
129 |
+
= 2.1.12 =
|
130 |
+
Woocommerce compatibility raised to 4.2
|
131 |
+
|
132 |
+
= 2.1.11 =
|
133 |
+
Woocommerce compatibility raised to 4.1
|
134 |
+
|
135 |
+
= 2.1.10 =
|
136 |
+
Improved shipping settings and status page, Woocommerce compatibility raised to 4.0
|
137 |
+
|
138 |
+
= 2.1.9 =
|
139 |
+
Improved support for WooCommerce 3.9 and other minor bug fixes
|
140 |
+
|
141 |
+
= 2.1.8 =
|
142 |
+
Improved support for WooCommerce 3.8 and other minor bug fixes
|
143 |
+
|
144 |
+
= 2.1.7 =
|
145 |
+
Updated labels in plugin settings
|
146 |
+
|
147 |
+
= 2.1.6 =
|
148 |
+
Added the ability to change personalization popup title
|
149 |
+
|
150 |
+
= 2.1.5 =
|
151 |
+
Fixed issues with personalization tool and other minor improvements
|
152 |
+
|
153 |
+
= 2.1.4 =
|
154 |
+
Fixed an issue with personalized order submit
|
155 |
+
|
156 |
+
= 2.1.3 =
|
157 |
+
Fixed minor issue with site URL verification in plugin status page
|
158 |
+
|
159 |
+
= 2.1.2 =
|
160 |
+
Improved theme support for personalized products and other minor bug fixes
|
161 |
+
|
162 |
+
= 2.1.1 =
|
163 |
+
Improved support for WooCommerce 3.6 and other minor bug fixes
|
164 |
+
|
165 |
+
= 2.1 =
|
166 |
+
Personalization feature
|
167 |
+
|
168 |
+
= 2.0.8 =
|
169 |
+
Shipping & Tax information improvements
|
170 |
+
|
171 |
+
= 2.0.7 =
|
172 |
+
Bug fixes, security improvements and improved i18n support
|
173 |
+
|
174 |
+
= 2.0.6 =
|
175 |
+
Improvement support for WordPress 5.0 and fixed various minor issues
|
176 |
+
|
177 |
+
= 2.0.5 =
|
178 |
+
Improvements to Printful shipping carrier settings
|
179 |
+
|
180 |
+
= 2.0.4 =
|
181 |
+
Improvements and fixed various minor issues
|
182 |
+
|
183 |
+
= 2.0.3 =
|
184 |
+
Fixed various minor issues, enabled localization, Improved compatibility with WC 3.4.3
|
185 |
+
|
186 |
+
= 2.0.2 =
|
187 |
+
Improved compatibility with WC 3.3.0
|
188 |
+
|
189 |
+
= 2.0.1 =
|
190 |
+
Fixed various minor issues
|
191 |
+
|
192 |
+
= 2.0 =
|
193 |
+
New major plugin version
|
194 |
+
|
195 |
+
= 1.2.8 =
|
196 |
+
Fixed bug that caused tax rates to become invisible on checkout since WC 3.0
|
197 |
+
|
198 |
+
= 1.2.7 =
|
199 |
+
Do not calculate shipping rates for US addresses while ZIP or state is not entered
|
200 |
+
|
201 |
+
= 1.2.6 =
|
202 |
+
Include shipping rates in tax calculation for states that require that
|
203 |
+
|
204 |
+
= 1.2.5 =
|
205 |
+
Added option to allow Woocommerce default rates together with Printful rates for Printful products
|
206 |
+
|
207 |
+
= 1.2.4 =
|
208 |
+
Prevent virtual products from requiring shipping rate when bought together with Printful products
|
209 |
+
|
210 |
+
= 1.2.3 =
|
211 |
+
Fixed issue introduced in 1.2.2
|
212 |
+
|
213 |
+
= 1.2.2 =
|
214 |
+
Fixed PHP warning on Woocommerce 2.6
|
215 |
+
|
216 |
+
= 1.2.1 =
|
217 |
+
Fixed bug
|
218 |
+
|
219 |
+
= 1.2 =
|
220 |
+
Support calculating shipping rates for both Printful and non-Printful products at the same time
|
221 |
+
|
222 |
+
= 1.1.2 =
|
223 |
+
Removed check for Curl extension
|
224 |
+
|
225 |
+
= 1.1.1 =
|
226 |
+
Ignore virtual and downloadable products when calculating shipping rates
|
227 |
+
|
228 |
+
= 1.1 =
|
229 |
+
Added tax rate calculation
|
230 |
+
|
231 |
+
= 1.0.2 =
|
232 |
+
Added option to disable SSL
|
233 |
+
|
234 |
+
= 1.0.1 =
|
235 |
+
Minor improvements
|
236 |
+
|
237 |
+
= 1.0 =
|
238 |
+
First release
|
239 |
+
|
240 |
+
== Changelog ==
|
241 |
+
|
242 |
+
= 2.1.32 =
|
243 |
+
Woocommerce compatibility raised to 6.5.1
|
244 |
+
WordPress compatibility raised to 6.0
|
245 |
+
|
246 |
+
= 2.1.31 =
|
247 |
+
* Licence updated to GPLv3
|
248 |
+
* WordPress compatibility raised to 5.9
|
249 |
+
* Woocommerce compatibility raised to 6.4
|
250 |
+
|
251 |
+
= 2.1.30 =
|
252 |
+
Additional billing setting check
|
253 |
+
|
254 |
+
= 2.1.29 =
|
255 |
+
* WordPress compatibility raised to 5.8. Size guide bugfix
|
256 |
+
|
257 |
+
= 2.1.28 =
|
258 |
+
* Woocommerce compatibility raised to 6.1
|
259 |
+
|
260 |
+
= 2.1.27 =
|
261 |
+
* Woocommerce compatibility raised to 6.0
|
262 |
+
* Size guide bug fix
|
263 |
+
|
264 |
+
= 2.1.26 =
|
265 |
+
* Woocommerce compatibility raised to 5.9
|
266 |
+
|
267 |
+
= 2.1.25 =
|
268 |
+
* Woocommerce compatibility raised to 5.5
|
269 |
+
|
270 |
+
= 2.1.24 =
|
271 |
+
* Woocommerce compatibility raised to 5.4
|
272 |
+
|
273 |
+
= 2.1.23 =
|
274 |
+
* Woocommerce compatibility raised to 5.3
|
275 |
+
* Minor bug fixes
|
276 |
+
|
277 |
+
= 2.1.22 =
|
278 |
+
* Woocommerce compatibility raised to 5.2
|
279 |
+
|
280 |
+
= 2.1.21 =
|
281 |
+
* Advanced size guide translations fix
|
282 |
+
|
283 |
+
= 2.1.20 =
|
284 |
+
* Woocommerce compatibility raised to 5.0
|
285 |
+
|
286 |
+
= 2.1.19 =
|
287 |
+
* Woocommerce compatibility raised to 4.9
|
288 |
+
|
289 |
+
= 2.1.18 =
|
290 |
+
* Woocommerce compatibility raised to 4.8
|
291 |
+
|
292 |
+
= 2.1.17 =
|
293 |
+
* Woocommerce compatibility raised to 4.7
|
294 |
+
|
295 |
+
= 2.1.16 =
|
296 |
+
* Woocommerce compatibility raised to 4.6
|
297 |
+
|
298 |
+
= 2.1.15 =
|
299 |
+
* Fix translations
|
300 |
+
|
301 |
+
= 2.1.14 =
|
302 |
+
* Advanced size guide feature
|
303 |
+
|
304 |
+
= 2.1.13 =
|
305 |
+
* Improved support for WooCommerce 4.3
|
306 |
+
|
307 |
+
= 2.1.12 =
|
308 |
+
* Improved support for WooCommerce 4.2
|
309 |
+
|
310 |
+
= 2.1.11 =
|
311 |
+
* Improved support for WooCommerce 4.1
|
312 |
+
|
313 |
+
= 2.1.10 =
|
314 |
+
* Improved support for WooCommerce 4.0 and other minor bug fixes
|
315 |
+
|
316 |
+
= 2.1.9 =
|
317 |
+
* Improved support for WooCommerce 3.9 and other minor bug fixes
|
318 |
+
|
319 |
+
= 2.1.8 =
|
320 |
+
* Improved support for WooCommerce 3.8 and other minor bug fixes
|
321 |
+
|
322 |
+
= 2.1.7 =
|
323 |
+
* Updated labels in plugin settings
|
324 |
+
|
325 |
+
= 2.1.6 =
|
326 |
+
* Added the ability to change personalization popup title
|
327 |
+
|
328 |
+
= 2.1.5 =
|
329 |
+
* Fixed issues with personalization tool and other minor improvements
|
330 |
+
|
331 |
+
= 2.1.4 =
|
332 |
+
* Fixed an issue with personalized order submit
|
333 |
+
|
334 |
+
= 2.1.3 =
|
335 |
+
* Fixed minor issue with site URL verification in plugin status page
|
336 |
+
|
337 |
+
= 2.1.2 =
|
338 |
+
* Improved theme support for personalized products and other minor bug fixes
|
339 |
+
|
340 |
+
= 2.1.1 =
|
341 |
+
* Improved support for WooCommerce 3.6 and other minor bug fixes
|
342 |
+
|
343 |
+
= 2.1 =
|
344 |
+
* Personalization feature
|
345 |
+
|
346 |
+
= 2.0.8 =
|
347 |
+
* Shipping & Tax information improvements
|
348 |
+
|
349 |
+
= 2.0.7 =
|
350 |
+
* Bug fixes, security improvements and improved i18n support
|
351 |
+
|
352 |
+
= 2.0.6 =
|
353 |
+
* Improvement support for WordPress 5.0 and fixed various minor issues
|
354 |
+
|
355 |
+
= 2.0.5 =
|
356 |
+
* Improvements to Printful shipping carrier settings
|
357 |
+
|
358 |
+
= 2.0.4 =
|
359 |
+
* Improved support for older PHP versions
|
360 |
+
* Other minor bug fixes
|
361 |
+
|
362 |
+
= 2.0.3 =
|
363 |
+
* Improved compatibility with WC 3.4.3
|
364 |
+
* Wordpress localization support
|
365 |
+
* Minor bug fixes
|
366 |
+
|
367 |
+
= 2.0.2 =
|
368 |
+
* Fixed incorrect webhook status indication since WC 3.3.0
|
369 |
+
|
370 |
+
= 2.0.1 =
|
371 |
+
* Improved Printful connection status detection
|
372 |
+
* Improvements for system report
|
373 |
+
* Show warning if attempting to connect from localhost
|
374 |
+
|
375 |
+
= 2.0 =
|
376 |
+
* New major plugin version
|
377 |
+
* All new Printful dashboard
|
378 |
+
* Connect to Printful with a single click
|
379 |
+
* View your Printful profits and latest Printful product orders in WordPress admin
|
380 |
+
* Edit your shipping carriers from Printful dashboard
|
381 |
+
* Improved sales tax compatibility with existing tax rates
|
382 |
+
* New status page - see if your integration is running smoothly
|
383 |
+
* New support page - all info about finding help in one place
|
384 |
+
* Size chart tab - when pushing products from Printful, the size chart will be placed in a separate tab
|
385 |
+
* Improved logging of API requests coming to and from Printful
|
386 |
+
|
387 |
+
= 1.2.8 =
|
388 |
+
* Fixed bug that caused tax rates to become invisible on checkout since WC 3.0
|
389 |
+
|
390 |
+
= 1.2.7 =
|
391 |
+
* Do not calculate shipping rates for US addresses while ZIP or state is not entered
|
392 |
+
|
393 |
+
= 1.2.6 =
|
394 |
+
* Include shipping rates in tax calculation for states that require that
|
395 |
+
|
396 |
+
= 1.2.5 =
|
397 |
+
* Added option to allow Woocommerce default rates together with Printful rates for Printful products
|
398 |
+
|
399 |
+
= 1.2.4 =
|
400 |
+
* Prevent virtual products from requiring shipping rate when bought together with Printful products
|
401 |
+
|
402 |
+
= 1.2.3 =
|
403 |
+
* Fixed issue introduced in 1.2.2
|
404 |
+
|
405 |
+
= 1.2.2 =
|
406 |
+
* Fixed PHP warning on Woocommerce 2.6 due to changed method signature
|
407 |
+
* Fixed conflict with "Multiple Packages for WooCommerce" plugin
|
408 |
+
|
409 |
+
= 1.2.1 =
|
410 |
+
* Fixed bug that could have show error message when calculating shipping rates
|
411 |
+
|
412 |
+
= 1.2 =
|
413 |
+
* Support calculating shipping rates for both Printful and non-Printful products at the same time (non-Printful
|
414 |
+
products will get default rates provided by Woocommerce)
|
415 |
+
* Added caching to tax rates
|
416 |
+
* Improved compatibility with Woocommerce 2.6
|
417 |
+
|
418 |
+
= 1.1.2 =
|
419 |
+
* Removed check for Curl extension (since we already used wp_remote_get and it is no longer necessary)
|
420 |
+
|
421 |
+
= 1.1.1 =
|
422 |
+
* Ignore virtual and downloadable products when calculating shipping rates
|
423 |
+
|
424 |
+
= 1.1 =
|
425 |
+
* Added option to calculate sales tax rates for locations where it is required for Printful orders
|
426 |
+
* Added automatic conversion of shipping rates to the currency used by Woocommerce
|
427 |
+
* Printful API client library updated to use Wordpress internal wp_remote_get method instead of CURL directly
|
428 |
+
* Changed plugin code structure for easier implementation of new features in the future
|
429 |
+
|
430 |
+
= 1.0.2 =
|
431 |
+
* Added option to disable SSL for users that do not have a valid CA certificates in their PHP installation
|
432 |
+
|
433 |
+
= 1.0.1 =
|
434 |
+
* Removed CURLOPT_FOLLOWLOCATION that caused problems on some hosting environments
|
435 |
+
* Added option to display reason status messages if the rate API request has failed
|
436 |
+
|
437 |
+
= 1.0 =
|
438 |
+
* First release
|