Version Description
New major plugin version * All new Printful dashboard * Connect to Printful with a single click * View your Printful profits and latest Printful product orders in WordPress admin * Edit your shipping carriers from Printful dashboard * Improved sales tax compatibility with existing tax rates * New status page - see if your integration is running smoothly * New support page - all info about finding help in one place * Size chart tab - when pushing products from Printful, the size chart will be placed in a separate tab * Improved logging of API requests coming to and from Printful
Download this release
Release Info
Developer | printful |
Plugin | Printful Integration for WooCommerce |
Version | 2.0 |
Comparing to | |
See all releases |
Code changes from version 1.2.8 to 2.0
- assets/css/dashboard.css +181 -0
- assets/css/global.css +19 -0
- assets/css/settings.css +40 -0
- assets/css/status.css +18 -0
- assets/css/support.css +62 -0
- assets/images/connect.svg +74 -0
- assets/images/printful-logo-footer.png +0 -0
- assets/images/printful-menu-icon.png +0 -0
- assets/js/block-loader.js +22 -0
- assets/js/connect.js +51 -0
- assets/js/intercom.min.js +12 -0
- assets/js/settings.js +42 -0
- includes/class-printful-admin-dashboard.php +267 -0
- includes/class-printful-admin-settings.php +250 -0
- includes/class-printful-admin-status.php +541 -0
- includes/class-printful-admin-support.php +235 -0
- includes/class-printful-admin.php +167 -0
- includes/class-printful-api-resource.php +109 -22
- includes/class-printful-carriers.php +88 -0
- includes/class-printful-client.php +110 -91
- includes/class-printful-integration.php +147 -238
- includes/class-printful-request-log.php +142 -0
- includes/class-printful-shipping.php +308 -266
- includes/class-printful-size-chart-tab.php +132 -0
- includes/class-printful-taxes.php +188 -0
- includes/templates/ajax-loader.php +11 -0
- includes/templates/connect.php +40 -0
- includes/templates/error.php +1 -0
- includes/templates/footer.php +1 -0
- includes/templates/header.php +20 -0
- includes/templates/inline-script.php +3 -0
- includes/templates/order-table.php +69 -0
- includes/templates/quick-links.php +32 -0
- includes/templates/setting-group.php +59 -0
- includes/templates/setting-submit.php +21 -0
- includes/templates/shipping-notification.php +4 -0
- includes/templates/stats.php +44 -0
- includes/templates/status-report.php +19 -0
- includes/templates/status-table.php +54 -0
- includes/templates/support-info.php +27 -0
- printful-shipping.php +114 -56
- readme.txt +159 -132
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 |
+
}
|
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 |
+
}
|
assets/css/settings.css
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
}
|
assets/css/status.css
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
}
|
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 |
+
}
|
assets/images/connect.svg
ADDED
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<svg xmlns="http://www.w3.org/2000/svg" width="408" height="177" viewBox="0 0 408 177">
|
2 |
+
<g fill="none" fill-rule="evenodd" transform="translate(1 1)">
|
3 |
+
<path fill="#FFF" fill-rule="nonzero" stroke="#231F20" stroke-width="2" d="M299 120h107v8H299zM0 120h107v8H0z"/>
|
4 |
+
<path stroke="#D1D3D4" d="M197.5 21V10.2c0-5.5-4.1-9.8-9.6-9.8h-124c-5.5 0-10.4 4.2-10.4 9.8V87M208.5 21V10.2c0-5.5 4.9-9.8 10.4-9.8h124c5.5 0 9.6 4.2 9.6 9.8V87"/>
|
5 |
+
<path fill="#FFF" fill-rule="nonzero" stroke="#231F20" stroke-width="2" d="M5 48h97v72H5z"/>
|
6 |
+
<g fill-rule="nonzero">
|
7 |
+
<path fill="#9B5C8F" d="M27.104 70h44.77C74.706 70 77 72.312 77 75.168v17.225c0 2.856-2.293 5.168-5.127 5.168H55.82L58.022 103l-9.691-5.44H27.127C24.294 97.56 22 95.25 22 92.394V75.168C21.978 72.334 24.27 70 27.104 70z"/>
|
8 |
+
<path fill="#FFF" d="M25.389 74.664c.316-.431.79-.658 1.422-.703 1.152-.091 1.807.453 1.965 1.633.7 4.743 1.468 8.76 2.28 12.05l4.946-9.463c.451-.862 1.016-1.316 1.693-1.361.994-.068 1.603.567 1.852 1.906.565 3.017 1.287 5.582 2.145 7.76.587-5.763 1.58-9.916 2.98-12.48.34-.635.836-.953 1.491-.998.52-.046.994.113 1.423.453.429.34.655.772.7 1.294.022.408-.045.749-.226 1.09-.88 1.633-1.603 4.378-2.19 8.19-.565 3.699-.768 6.58-.633 8.646.046.567-.045 1.066-.27 1.497-.272.5-.678.772-1.197.817-.588.046-1.197-.227-1.784-.84-2.1-2.155-3.771-5.377-4.99-9.666a425.409 425.409 0 0 0-3.252 6.535c-1.333 2.564-2.462 3.88-3.41 3.948-.61.046-1.13-.476-1.58-1.565-1.152-2.973-2.394-8.714-3.726-17.223-.09-.59.044-1.112.36-1.52zm48.526 3.563c-.813-1.43-2.01-2.292-3.613-2.633a5.904 5.904 0 0 0-1.22-.136c-2.167 0-3.928 1.135-5.306 3.404-1.174 1.929-1.76 4.062-1.76 6.399 0 1.747.36 3.244 1.083 4.493.813 1.43 2.01 2.291 3.613 2.632.429.09.835.136 1.22.136 2.19 0 3.95-1.135 5.306-3.404 1.174-1.951 1.761-4.084 1.761-6.421.023-1.77-.361-3.245-1.084-4.47zm-2.845 6.285c-.316 1.498-.88 2.61-1.716 3.358-.655.59-1.265.84-1.83.726-.541-.113-.993-.59-1.331-1.475a5.778 5.778 0 0 1-.407-2.065c0-.567.045-1.134.158-1.656a7.357 7.357 0 0 1 1.197-2.7c.745-1.112 1.535-1.566 2.348-1.407.542.113.994.59 1.333 1.475.27.703.406 1.407.406 2.065 0 .59-.045 1.157-.158 1.679zm-11.29-6.285c-.813-1.43-2.033-2.292-3.613-2.633a5.902 5.902 0 0 0-1.22-.136c-2.168 0-3.929 1.135-5.306 3.404-1.174 1.929-1.762 4.062-1.762 6.399 0 1.747.362 3.244 1.084 4.493.813 1.43 2.01 2.291 3.613 2.632.43.09.835.136 1.22.136 2.19 0 3.951-1.135 5.306-3.404 1.174-1.951 1.761-4.084 1.761-6.421 0-1.77-.361-3.245-1.084-4.47zm-2.868 6.285c-.316 1.498-.881 2.61-1.716 3.358-.655.59-1.265.84-1.83.726-.541-.113-.993-.59-1.332-1.475a5.787 5.787 0 0 1-.406-2.065c0-.567.045-1.134.158-1.656a7.354 7.354 0 0 1 1.197-2.7c.745-1.112 1.535-1.566 2.348-1.407.542.113.994.59 1.332 1.475.272.703.407 1.407.407 2.065a6.726 6.726 0 0 1-.158 1.679z"/>
|
9 |
+
</g>
|
10 |
+
<path fill="#FFF" fill-rule="nonzero" stroke="#231F20" stroke-width="2" d="M304 48h97v72h-97z"/>
|
11 |
+
<g fill-rule="nonzero">
|
12 |
+
<path fill="#F2C994" d="M320 98l17.5-30L355 98z"/>
|
13 |
+
<path fill="#ED4642" d="M336 98l17.5-30L371 98z"/>
|
14 |
+
<path fill="#17BCB5" d="M352 98l17.5-30L387 98z"/>
|
15 |
+
<path fill="#16342F" d="M352 98h19l-9.5-16z"/>
|
16 |
+
<path fill="#DF392F" d="M336 98h19l-9.5-16z"/>
|
17 |
+
<path fill="#15291B" d="M352 98h3l-1.5-3z"/>
|
18 |
+
</g>
|
19 |
+
<path fill="#FFF" fill-rule="nonzero" stroke="#231F20" stroke-width="2" d="M155 21h97v124h-97z"/>
|
20 |
+
<path fill="#FFF" fill-rule="nonzero" stroke="#231F20" stroke-width="2" d="M150 142h107v33H150z"/>
|
21 |
+
<path stroke="#D1D3D4" d="M162.5 161.8l1.3-3.4 2 3.6 5.3-9.2 4 9.2 2.7-4.6 3.3 2.6"/>
|
22 |
+
<path stroke="#231F20" d="M161.5 148.5h20v20h-20zM187.5 148.5h61v20h-61z"/>
|
23 |
+
<circle cx="191.2" cy="33.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
|
24 |
+
<path stroke="#FFF" d="M191.2 33.8l.7-3.8"/>
|
25 |
+
<circle cx="191.2" cy="44.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
|
26 |
+
<path stroke="#FFF" d="M191.2 44.8l2.3 3"/>
|
27 |
+
<circle cx="191.2" cy="55.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
|
28 |
+
<path stroke="#FFF" d="M191.2 55.8l-3 2.3"/>
|
29 |
+
<circle cx="191.2" cy="66.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
|
30 |
+
<path stroke="#FFF" d="M191.2 66.8l2.3 3"/>
|
31 |
+
<circle cx="191.2" cy="77.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
|
32 |
+
<path stroke="#FFF" d="M191.2 77.8l3.6 1.2"/>
|
33 |
+
<path fill="#F1F2F2" fill-rule="nonzero" stroke="#231F20" d="M183.5 87.5h7v3h-7zM193.5 87.5h7v3h-7zM203.5 87.5h7v3h-7zM213.5 87.5h7v3h-7zM223.5 87.5h7v3h-7z"/>
|
34 |
+
<path fill="#F2C994" fill-rule="nonzero" stroke="#231F20" d="M233.5 87.5h7v3h-7z"/>
|
35 |
+
<path stroke="#231F20" stroke-width="2" d="M184 30v52"/>
|
36 |
+
<circle cx="214.2" cy="33.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
|
37 |
+
<path stroke="#FFF" d="M214.2 33.8l2.3 3"/>
|
38 |
+
<circle cx="214.2" cy="44.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
|
39 |
+
<path stroke="#FFF" d="M214.2 44.8l3-2.4"/>
|
40 |
+
<circle cx="214.2" cy="55.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
|
41 |
+
<path stroke="#FFF" d="M214.2 55.8l2.3 3"/>
|
42 |
+
<circle cx="214.2" cy="66.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
|
43 |
+
<path stroke="#FFF" d="M214.2 66.8l-3 2.3"/>
|
44 |
+
<circle cx="214.2" cy="77.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
|
45 |
+
<path stroke="#FFF" d="M214.2 77.8l2.3 3"/>
|
46 |
+
<path stroke="#231F20" stroke-width="2" d="M207 30v52"/>
|
47 |
+
<circle cx="237.2" cy="33.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
|
48 |
+
<path stroke="#FFF" d="M237.2 33.8l3.7.8"/>
|
49 |
+
<circle cx="237.2" cy="44.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
|
50 |
+
<path stroke="#FFF" d="M237.2 44.8l2.3 3"/>
|
51 |
+
<circle cx="237.2" cy="55.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
|
52 |
+
<path stroke="#FFF" d="M237.2 55.8l.3 3.8"/>
|
53 |
+
<circle cx="237.2" cy="66.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
|
54 |
+
<path stroke="#FFF" d="M237.2 66.8l3.4-1.7"/>
|
55 |
+
<circle cx="237.2" cy="77.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
|
56 |
+
<path stroke="#FFF" d="M237.2 77.8l2.3 3"/>
|
57 |
+
<path stroke="#231F20" stroke-width="2" d="M230 30v52M184 96v34M207 96v34"/>
|
58 |
+
<path fill="#F1F2F2" fill-rule="nonzero" stroke="#231F20" d="M179.5 109.5h9v3h-9zM202.5 125.5h9v4h-9z"/>
|
59 |
+
<path stroke="#231F20" stroke-width="2" d="M230 96v34"/>
|
60 |
+
<path fill="#F1F2F2" fill-rule="nonzero" stroke="#231F20" d="M225.5 114.5h9v4h-9z"/>
|
61 |
+
<circle cx="169.5" cy="35.8" r="5" fill="#F2C994" fill-rule="nonzero" stroke="#231F20" stroke-width="2"/>
|
62 |
+
<circle cx="169.5" cy="48.8" r="3" fill="#D1D3D4" fill-rule="nonzero"/>
|
63 |
+
<circle cx="169.5" cy="59.8" r="3" fill="#D1D3D4" fill-rule="nonzero"/>
|
64 |
+
<circle cx="169.5" cy="70.8" r="3" fill="#D1D3D4" fill-rule="nonzero"/>
|
65 |
+
<circle cx="169.5" cy="81.8" r="3" fill="#D1D3D4" fill-rule="nonzero"/>
|
66 |
+
<circle cx="169.5" cy="99.8" r="3" fill="#D1D3D4" fill-rule="nonzero"/>
|
67 |
+
<circle cx="169.5" cy="113.8" r="3" fill="#D1D3D4" fill-rule="nonzero"/>
|
68 |
+
<circle cx="169.5" cy="127.8" r="3" fill="#D1D3D4" fill-rule="nonzero"/>
|
69 |
+
<circle cx="194.2" cy="152.8" r="2" fill="#F2C994" fill-rule="nonzero"/>
|
70 |
+
<circle cx="210.2" cy="162.8" r="2" fill="#F2C994" fill-rule="nonzero"/>
|
71 |
+
<path stroke="#231F20" d="M187 156.5h61"/>
|
72 |
+
<circle cx="239.2" cy="159.8" r="2" fill="#F2C994" fill-rule="nonzero"/>
|
73 |
+
</g>
|
74 |
+
</svg>
|
assets/images/printful-logo-footer.png
ADDED
Binary file
|
assets/images/printful-menu-icon.png
ADDED
Binary file
|
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 |
+
})();
|
assets/js/connect.js
ADDED
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
},
|
21 |
+
listen_status: function () {
|
22 |
+
this.interval = setInterval(this.get_status.bind(this), 10000); //check status every 10 secs
|
23 |
+
},
|
24 |
+
get_status: function () {
|
25 |
+
var interval = this.interval;
|
26 |
+
jQuery.ajax( {
|
27 |
+
type: "GET",
|
28 |
+
url: this.ajax_url,
|
29 |
+
success: function( response ) {
|
30 |
+
if (response === 'OK') {
|
31 |
+
clearInterval(interval);
|
32 |
+
Printful_Connect.send_return_message();
|
33 |
+
}
|
34 |
+
}
|
35 |
+
});
|
36 |
+
},
|
37 |
+
listen_auth_return: function () {
|
38 |
+
var intercom = Intercom.getInstance();
|
39 |
+
intercom.on('printful-auth', function (data) {
|
40 |
+
if (data.success === true) {
|
41 |
+
location.reload();
|
42 |
+
}
|
43 |
+
});
|
44 |
+
},
|
45 |
+
send_return_message: function () {
|
46 |
+
var intercom = Intercom.getInstance();
|
47 |
+
intercom.emit('printful-auth', {success: true});
|
48 |
+
window.top.close();
|
49 |
+
}
|
50 |
+
};
|
51 |
+
})();
|
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}();
|
assets/js/settings.js
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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.show(0).delay(3000).hide(0);
|
33 |
+
}
|
34 |
+
}
|
35 |
+
});
|
36 |
+
});
|
37 |
+
},
|
38 |
+
enable_submit_btn: function () {
|
39 |
+
jQuery('.printful-submit input[type=submit]').removeClass('disabled').prop('disabled', false);
|
40 |
+
}
|
41 |
+
};
|
42 |
+
})();
|
includes/class-printful-admin-dashboard.php
ADDED
@@ -0,0 +1,267 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
*/
|
32 |
+
public static function view() {
|
33 |
+
|
34 |
+
$dashboard = self::instance();
|
35 |
+
$api_key = Printful_Integration::instance()->get_option('printful_key');
|
36 |
+
$connect_status = Printful_Integration::instance()->is_connected();
|
37 |
+
|
38 |
+
if ( $connect_status ) {
|
39 |
+
$dashboard->render_dashboard();
|
40 |
+
} else if(!$connect_status && strlen($api_key) > 0) {
|
41 |
+
$dashboard->render_connect_error();
|
42 |
+
} else {
|
43 |
+
$dashboard->render_connect();
|
44 |
+
}
|
45 |
+
}
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Display the Printful connect page
|
49 |
+
*/
|
50 |
+
public function render_connect() {
|
51 |
+
|
52 |
+
$status = Printful_Admin_Status::instance();
|
53 |
+
$issues = array();
|
54 |
+
|
55 |
+
$api_enabled = $status->run_single_test('check_WC_API_enabled');
|
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 ( $api_enabled == Printful_Admin_Status::PF_STATUS_FAIL ) {
|
65 |
+
$message = 'Your site needs to enable WooCommerce API so that Printful can connect to it. Go to <a href="%s">API settings</a> and enable it.';
|
66 |
+
$settings_url = admin_url( 'admin.php?page=wc-settings&tab=api' );
|
67 |
+
$issues[] = sprintf( $message, $settings_url );
|
68 |
+
}
|
69 |
+
|
70 |
+
Printful_Admin::load_template( 'header', array( 'tabs' => Printful_Admin::get_tabs() ) );
|
71 |
+
|
72 |
+
Printful_Admin::load_template( 'connect', array(
|
73 |
+
'consumer_key' => $this->_get_consumer_key(),
|
74 |
+
'waiting_sync' => isset( $_GET['sync-in-progress'] ),
|
75 |
+
'consumer_key_error' => isset( $_GET['consumer-key-error'] ),
|
76 |
+
'issues' => $issues,
|
77 |
+
)
|
78 |
+
);
|
79 |
+
|
80 |
+
if ( isset( $_GET['sync-in-progress'] ) ) {
|
81 |
+
$emit_auth_response = 'Printful_Connect.send_return_message();';
|
82 |
+
Printful_Admin::load_template( 'inline-script', array( 'script' => $emit_auth_response ) );
|
83 |
+
}
|
84 |
+
|
85 |
+
Printful_Admin::load_template('footer');
|
86 |
+
}
|
87 |
+
|
88 |
+
/**
|
89 |
+
* Display the Printful connect error page
|
90 |
+
*/
|
91 |
+
public function render_connect_error() {
|
92 |
+
|
93 |
+
Printful_Admin::load_template( 'header', array( 'tabs' => Printful_Admin::get_tabs() ) );
|
94 |
+
|
95 |
+
$connect_error = Printful_Integration::instance()->get_connect_error();
|
96 |
+
if ( $connect_error ) {
|
97 |
+
Printful_Admin::load_template('error', array('error' => $connect_error));
|
98 |
+
}
|
99 |
+
|
100 |
+
Printful_Admin::load_template('footer');
|
101 |
+
}
|
102 |
+
|
103 |
+
/**
|
104 |
+
* Display the dashboard
|
105 |
+
*/
|
106 |
+
public function render_dashboard() {
|
107 |
+
|
108 |
+
Printful_Admin::load_template( 'header', array( 'tabs' => Printful_Admin::get_tabs() ) );
|
109 |
+
|
110 |
+
$stats = $this->_get_stats(true);
|
111 |
+
$orders = $this->_get_orders(true);
|
112 |
+
$error = false;
|
113 |
+
|
114 |
+
if ( is_wp_error( $stats ) ) {
|
115 |
+
$error = $stats;
|
116 |
+
}
|
117 |
+
if ( is_wp_error( $orders ) ) {
|
118 |
+
$error = $orders;
|
119 |
+
}
|
120 |
+
|
121 |
+
if ( ! $error ) {
|
122 |
+
|
123 |
+
if ( $stats ) {
|
124 |
+
Printful_Admin::load_template( 'stats', array( 'stats' => $stats ) );
|
125 |
+
} else {
|
126 |
+
Printful_Admin::load_template( 'ajax-loader', array( 'action' => 'get_printful_stats', 'message' => 'Loading your stats...' ) );
|
127 |
+
}
|
128 |
+
|
129 |
+
if ($orders) {
|
130 |
+
Printful_Admin::load_template( 'order-table', array( 'orders' => $orders ) );
|
131 |
+
} else {
|
132 |
+
Printful_Admin::load_template( 'ajax-loader', array( 'action' => 'get_printful_orders', 'message' => 'Loading your orders...' ) );
|
133 |
+
}
|
134 |
+
|
135 |
+
} else {
|
136 |
+
Printful_Admin::load_template( 'error', array( 'error' => $error->get_error_message('printful') ) );
|
137 |
+
}
|
138 |
+
|
139 |
+
Printful_Admin::load_template( 'quick-links' );
|
140 |
+
|
141 |
+
if ( isset( $_GET['sync-in-progress'] ) ) {
|
142 |
+
$emit_auth_response = 'Printful_Connect.send_return_message();';
|
143 |
+
Printful_Admin::load_template( 'inline-script', array( 'script' => $emit_auth_response ) );
|
144 |
+
}
|
145 |
+
|
146 |
+
Printful_Admin::load_template( 'footer' );
|
147 |
+
}
|
148 |
+
|
149 |
+
/**
|
150 |
+
* Ajax response for stats block
|
151 |
+
*/
|
152 |
+
public static function render_stats_ajax() {
|
153 |
+
|
154 |
+
$stats = self::instance()->_get_stats();
|
155 |
+
|
156 |
+
if ( ! empty( $stats ) && ! is_wp_error( $stats ) ) {
|
157 |
+
Printful_Admin::load_template( 'stats', array( 'stats' => $stats ) );
|
158 |
+
} else {
|
159 |
+
Printful_Admin::load_template( 'error', array( 'error' => $stats->get_error_message( 'printful' ) ) );
|
160 |
+
}
|
161 |
+
|
162 |
+
exit;
|
163 |
+
}
|
164 |
+
|
165 |
+
/**
|
166 |
+
* Ajax response for stats block
|
167 |
+
*/
|
168 |
+
public static function render_orders_ajax() {
|
169 |
+
|
170 |
+
$orders = self::instance()->_get_orders();
|
171 |
+
|
172 |
+
if ( ! empty( $orders ) && is_wp_error( $orders ) ) {
|
173 |
+
Printful_Admin::load_template( 'error', array( 'error' => $orders->get_error_message('printful') ) );
|
174 |
+
} else {
|
175 |
+
Printful_Admin::load_template( 'order-table', array( 'orders' => $orders ) );
|
176 |
+
}
|
177 |
+
|
178 |
+
exit;
|
179 |
+
}
|
180 |
+
|
181 |
+
/**
|
182 |
+
* Get store statistics from API
|
183 |
+
* @param bool $only_cached_results
|
184 |
+
* @return mixed
|
185 |
+
*/
|
186 |
+
private function _get_stats($only_cached_results = false) {
|
187 |
+
|
188 |
+
$stats = get_transient( 'printful_stats' );
|
189 |
+
if ( $only_cached_results || $stats ) {
|
190 |
+
return $stats;
|
191 |
+
}
|
192 |
+
|
193 |
+
try {
|
194 |
+
$stats = Printful_Integration::instance()->get_client()->get( 'store/statistics' );
|
195 |
+
if ( ! empty( $stats['store_statistics'] ) ) {
|
196 |
+
$stats = $stats['store_statistics'];
|
197 |
+
}
|
198 |
+
set_transient( 'printful_stats', $stats, MINUTE_IN_SECONDS * 5 ); //cache for 5 minute
|
199 |
+
} catch (PrintfulApiException $e) {
|
200 |
+
return new WP_Error('printful', 'Could not connect to Printful API. Please try again later!');
|
201 |
+
} catch (PrintfulException $e) {
|
202 |
+
return new WP_Error('printful', 'Could not connect to Printful API. Please try again later!');
|
203 |
+
}
|
204 |
+
|
205 |
+
return $stats;
|
206 |
+
}
|
207 |
+
|
208 |
+
/**
|
209 |
+
* Get Printful orders from the API
|
210 |
+
* @param bool $only_cached_results
|
211 |
+
* @return mixed
|
212 |
+
*/
|
213 |
+
private function _get_orders($only_cached_results = false) {
|
214 |
+
|
215 |
+
$orders = get_transient( 'printful_orders' );
|
216 |
+
|
217 |
+
if ( $only_cached_results || $orders ) {
|
218 |
+
return $orders;
|
219 |
+
}
|
220 |
+
|
221 |
+
try {
|
222 |
+
$order_data = Printful_Integration::instance()->get_client()->get( 'orders' );
|
223 |
+
|
224 |
+
if ( ! empty( $order_data ) ) {
|
225 |
+
|
226 |
+
foreach ( $order_data as $key => $order ) {
|
227 |
+
|
228 |
+
if($order['status'] == 'pending') {
|
229 |
+
$order_data[$key]['status'] = 'Waiting for fulfillment';
|
230 |
+
}
|
231 |
+
}
|
232 |
+
}
|
233 |
+
|
234 |
+
$orders = array( 'count' => count( $order_data ), 'results' => $order_data );
|
235 |
+
set_transient( 'printful_orders', $orders, MINUTE_IN_SECONDS * 5 ); //cache for 5 minute
|
236 |
+
} catch (PrintfulApiException $e) {
|
237 |
+
return new WP_Error('printful', 'Could not connect to Printful API. Please try again later!');
|
238 |
+
} catch (PrintfulException $e) {
|
239 |
+
return new WP_Error('printful', 'Could not connect to Printful API. Please try again later!');
|
240 |
+
}
|
241 |
+
|
242 |
+
return $orders;
|
243 |
+
}
|
244 |
+
|
245 |
+
/**
|
246 |
+
* Get the last used consumer key fragment and use it for validating the address
|
247 |
+
* @return null|string
|
248 |
+
*/
|
249 |
+
private function _get_consumer_key() {
|
250 |
+
|
251 |
+
global $wpdb;
|
252 |
+
|
253 |
+
// Get the API key
|
254 |
+
$search = "AND description LIKE '%" . esc_sql( $wpdb->esc_like( wc_clean( self::API_KEY_SEARCH_STRING ) ) ) . "%' ";
|
255 |
+
$query = "SELECT truncated_key FROM {$wpdb->prefix}woocommerce_api_keys WHERE 1 = 1 {$search} ORDER BY key_id DESC LIMIT 1";
|
256 |
+
$consumer_key = $wpdb->get_var( $query );
|
257 |
+
|
258 |
+
//if not found by description, it was probably manually created. try the last used key instead
|
259 |
+
if ( ! $consumer_key ) {
|
260 |
+
$query = "SELECT truncated_key FROM {$wpdb->prefix}woocommerce_api_keys ORDER BY key_id DESC LIMIT 1";
|
261 |
+
$consumer_key = $wpdb->get_var( $query );
|
262 |
+
}
|
263 |
+
|
264 |
+
return $consumer_key;
|
265 |
+
}
|
266 |
+
|
267 |
+
}
|
includes/class-printful-admin-settings.php
ADDED
@@ -0,0 +1,250 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
|
10 |
+
public static $integration_fields = array(
|
11 |
+
'printful_key' => array(
|
12 |
+
'title' => 'Printful store API key',
|
13 |
+
'type' => 'text',
|
14 |
+
'desc_tip' => true,
|
15 |
+
'description' => 'Your store\'s Printful API key. Create it in the Prinful dashboard',
|
16 |
+
'default' => false,
|
17 |
+
),
|
18 |
+
'calculate_tax' => array(
|
19 |
+
'title' => 'Calculate sales tax',
|
20 |
+
'type' => 'checkbox',
|
21 |
+
'label' => 'Calculated for all products shipped to North Carolina and California',
|
22 |
+
'default' => 'no',
|
23 |
+
),
|
24 |
+
'disable_ssl' => array(
|
25 |
+
'title' => 'Disable SSL',
|
26 |
+
'type' => 'checkbox',
|
27 |
+
'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)',
|
28 |
+
'default' => 'no',
|
29 |
+
),
|
30 |
+
);
|
31 |
+
|
32 |
+
/**
|
33 |
+
* @return Printful_Admin_Settings
|
34 |
+
*/
|
35 |
+
public static function instance() {
|
36 |
+
|
37 |
+
if ( is_null( self::$_instance ) ) {
|
38 |
+
self::$_instance = new self();
|
39 |
+
}
|
40 |
+
|
41 |
+
return self::$_instance;
|
42 |
+
}
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Setup the view
|
46 |
+
*/
|
47 |
+
public static function view() {
|
48 |
+
|
49 |
+
$settings = self::instance();
|
50 |
+
$settings->render();
|
51 |
+
}
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Display the view
|
55 |
+
*/
|
56 |
+
public function render() {
|
57 |
+
|
58 |
+
Printful_Admin::load_template( 'header', array( 'tabs' => Printful_Admin::get_tabs() ) );
|
59 |
+
|
60 |
+
echo '<form method="post" name="printful_settings" action="' . admin_url( 'admin-ajax.php?action=save_printful_settings' ) . '">';
|
61 |
+
|
62 |
+
//integration settings
|
63 |
+
$integration_settings = $this->setup_integration_fields();
|
64 |
+
Printful_Admin::load_template( 'setting-group', $integration_settings );
|
65 |
+
|
66 |
+
Printful_Admin::load_template( 'shipping-notification' );
|
67 |
+
|
68 |
+
//carriers settings
|
69 |
+
Printful_Admin::load_template( 'ajax-loader', array( 'action' => 'get_printful_carriers', 'message' => 'Loading your carriers...' ) );
|
70 |
+
|
71 |
+
Printful_Admin::load_template( 'setting-submit', array( 'nonce' => wp_create_nonce( 'printful_settings' ), 'disabled' => true ) );
|
72 |
+
|
73 |
+
echo '</from>';
|
74 |
+
|
75 |
+
Printful_Admin::load_template( 'footer' );
|
76 |
+
}
|
77 |
+
|
78 |
+
/**
|
79 |
+
* Display the ajax content for carrier settings
|
80 |
+
*/
|
81 |
+
public static function render_carriers_ajax() {
|
82 |
+
|
83 |
+
$carrier_settings = self::instance()->setup_carrier_fields();
|
84 |
+
Printful_Admin::load_template( 'setting-group', $carrier_settings );
|
85 |
+
$enable_submit = 'Printful_Settings.enable_submit_btn();';
|
86 |
+
Printful_Admin::load_template( 'inline-script', array('script' => $enable_submit) );
|
87 |
+
exit;
|
88 |
+
}
|
89 |
+
|
90 |
+
/**
|
91 |
+
* @return mixed
|
92 |
+
* @internal param $integration_settings
|
93 |
+
*/
|
94 |
+
public function setup_integration_fields() {
|
95 |
+
|
96 |
+
$integration_settings = array(
|
97 |
+
'title' => 'Integration settings',
|
98 |
+
'description' => '',
|
99 |
+
'settings' => self::$integration_fields,
|
100 |
+
);
|
101 |
+
|
102 |
+
foreach ( $integration_settings['settings'] as $key => $setting ) {
|
103 |
+
if ( $setting['type'] !== 'title' ) {
|
104 |
+
$integration_settings['settings'][ $key ]['value'] = Printful_Integration::instance()->get_option( $key, $setting['default'] );
|
105 |
+
}
|
106 |
+
}
|
107 |
+
|
108 |
+
return $integration_settings;
|
109 |
+
}
|
110 |
+
|
111 |
+
/**
|
112 |
+
* @internal param $carrier_settings
|
113 |
+
*/
|
114 |
+
public function setup_carrier_fields() {
|
115 |
+
|
116 |
+
$carrier_settings = array(
|
117 |
+
'title' => 'Carriers & Services',
|
118 |
+
'description' => "You can specify here which shipping carriers are available for this store.\n
|
119 |
+
Uncheck the ones you want to disable. If you leave “Flat rate” checked it means that any order where flat rate was selected can still ship with any carrier and service.",
|
120 |
+
'settings' => array(),
|
121 |
+
);
|
122 |
+
|
123 |
+
if ( ! Printful_Integration::instance()->is_connected() ) {
|
124 |
+
$carrier_settings['description'] = 'You need to be connected to Printful API to edit carrier settings!';
|
125 |
+
return $carrier_settings;
|
126 |
+
}
|
127 |
+
|
128 |
+
$carriers = Printful_Carriers::instance()->carriers;
|
129 |
+
if ( empty( $carriers ) ) {
|
130 |
+
return false;
|
131 |
+
}
|
132 |
+
|
133 |
+
$standard = array();
|
134 |
+
$expedited = array();
|
135 |
+
|
136 |
+
foreach ( $carriers as $carrier ) {
|
137 |
+
|
138 |
+
$item = array(
|
139 |
+
'title' => false,
|
140 |
+
'type' => 'checkbox',
|
141 |
+
'label' => $carrier['title'] . ' <i>' . $carrier['subtitle'] . '</i>',
|
142 |
+
'default' => 'yes',
|
143 |
+
'value' => ( $carrier['status'] == 'on' ? 'yes' : 'no' ),
|
144 |
+
);
|
145 |
+
|
146 |
+
if ( $carrier['type'] == self::CARRIER_TYPE_STANDARD ) {
|
147 |
+
$standard[ $carrier['carrier_id'] ] = $item;
|
148 |
+
} else {
|
149 |
+
$expedited[ $carrier['carrier_id'] ] = $item;
|
150 |
+
}
|
151 |
+
}
|
152 |
+
|
153 |
+
$carrier_settings['settings'][ self::CARRIER_TYPE_STANDARD ] = array(
|
154 |
+
'title' => 'Standard shipping',
|
155 |
+
'type' => 'checkbox-group',
|
156 |
+
'items' => $standard,
|
157 |
+
);
|
158 |
+
|
159 |
+
$carrier_settings['settings'][ self::CARRIER_TYPE_EXPEDITED ] = array(
|
160 |
+
'title' => 'Expedited shipping',
|
161 |
+
'type' => 'checkbox-group',
|
162 |
+
'items' => $expedited,
|
163 |
+
);
|
164 |
+
|
165 |
+
return $carrier_settings;
|
166 |
+
}
|
167 |
+
|
168 |
+
/**
|
169 |
+
* Prepare carrier data for posting to Printful API
|
170 |
+
* @return array
|
171 |
+
*/
|
172 |
+
public function prepare_carriers() {
|
173 |
+
|
174 |
+
$carriers = Printful_Carriers::instance()->carriers;
|
175 |
+
|
176 |
+
if ( empty( $carriers ) ) {
|
177 |
+
return false;
|
178 |
+
}
|
179 |
+
|
180 |
+
$standard = ( ! empty( $_POST[ self::CARRIER_TYPE_STANDARD ] ) ? $_POST[ self::CARRIER_TYPE_STANDARD ] : array() );
|
181 |
+
$expedited = ( ! empty( $_POST[ self::CARRIER_TYPE_EXPEDITED ] ) ? $_POST[ self::CARRIER_TYPE_EXPEDITED ] : array() );
|
182 |
+
|
183 |
+
$saved_carriers = array_merge( $standard, $expedited );
|
184 |
+
|
185 |
+
if ( empty( $saved_carriers ) ) {
|
186 |
+
return false;
|
187 |
+
}
|
188 |
+
|
189 |
+
$request_body = array();
|
190 |
+
|
191 |
+
foreach ( $carriers as $carrier ) {
|
192 |
+
$status = 'off';
|
193 |
+
if ( in_array( $carrier['carrier_id'], $saved_carriers ) ) {
|
194 |
+
$status = 'on';
|
195 |
+
}
|
196 |
+
$request_body[] = array(
|
197 |
+
'carrier_id' => $carrier['carrier_id'],
|
198 |
+
'status' => $status,
|
199 |
+
);
|
200 |
+
}
|
201 |
+
|
202 |
+
return $request_body;
|
203 |
+
}
|
204 |
+
|
205 |
+
/**
|
206 |
+
* Ajax endpoint for saving the settings
|
207 |
+
*/
|
208 |
+
public static function save_printful_settings() {
|
209 |
+
|
210 |
+
if ( ! empty( $_POST ) ) {
|
211 |
+
|
212 |
+
check_admin_referer( 'printful_settings' );
|
213 |
+
|
214 |
+
//save carriers first, so API key change does not affect this
|
215 |
+
if ( Printful_Integration::instance()->is_connected(true) ) {
|
216 |
+
|
217 |
+
//save remote carrier settings
|
218 |
+
$request_body = Printful_Admin_Settings::instance()->prepare_carriers();
|
219 |
+
$result = Printful_Carriers::instance()->post_carriers( $request_body );
|
220 |
+
|
221 |
+
if ( ! $result ) {
|
222 |
+
die( 'Error: failed to save carriers' );
|
223 |
+
}
|
224 |
+
}
|
225 |
+
|
226 |
+
$options = array();
|
227 |
+
|
228 |
+
//build save options list
|
229 |
+
foreach ( self::$integration_fields as $key => $field ) {
|
230 |
+
|
231 |
+
if ( $field['type'] == 'checkbox' ) {
|
232 |
+
if ( isset( $_POST[ $key ] ) ) {
|
233 |
+
$options[ $key ] = 'yes';
|
234 |
+
} else {
|
235 |
+
$options[ $key ] = 'no';
|
236 |
+
}
|
237 |
+
} else {
|
238 |
+
if ( isset( $_POST[ $key ] ) ) {
|
239 |
+
$options[ $key ] = $_POST[ $key ];
|
240 |
+
}
|
241 |
+
}
|
242 |
+
}
|
243 |
+
|
244 |
+
//save integration settings
|
245 |
+
Printful_Integration::instance()->update_settings( $options );
|
246 |
+
|
247 |
+
die('OK');
|
248 |
+
}
|
249 |
+
}
|
250 |
+
}
|
includes/class-printful-admin-status.php
ADDED
@@ -0,0 +1,541 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
|
10 |
+
const API_KEY_SEARCH_STRING = 'Printful';
|
11 |
+
const PF_WEBHOOK_NAME = 'Printful Integration';
|
12 |
+
const PF_REMOTE_REQUEST_URL = 'webhook/woocommerce?store=1';
|
13 |
+
const PF_REMOTE_REQUEST_TOPIC = 'woo.plugin.test';
|
14 |
+
const PF_STATUS_ISSUE_COUNT = 'printful_status_issue_count';
|
15 |
+
const PF_CACHED_CHECKLIST = 'printful_cached_checklist';
|
16 |
+
|
17 |
+
public static $_instance;
|
18 |
+
public static $checklist_items = array(
|
19 |
+
array(
|
20 |
+
'name' => 'WordPress Permalinks',
|
21 |
+
'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".',
|
22 |
+
'method' => 'check_permalinks',
|
23 |
+
),
|
24 |
+
array(
|
25 |
+
'name' => 'WordPress version',
|
26 |
+
'description' => 'WordPress should always be updated to the latest version. Updates can be installed from your WordPress admin dashboard.',
|
27 |
+
'method' => 'check_WP_version',
|
28 |
+
),
|
29 |
+
array(
|
30 |
+
'name' => 'WooCommerce API enabled',
|
31 |
+
'description' => 'Your site needs to enbable WooCommerce API so that Printful can connect to it',
|
32 |
+
'method' => 'check_WC_API_enabled',
|
33 |
+
),
|
34 |
+
array(
|
35 |
+
'name' => 'WooCommerce Webhooks',
|
36 |
+
'description' => 'Printful requires WooCommerce webhooks to quickly capture you incoming orders, products updates etc.',
|
37 |
+
'method' => 'check_PF_webhooks',
|
38 |
+
),
|
39 |
+
array(
|
40 |
+
'name' => 'WooCommerce API keys are set',
|
41 |
+
'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.',
|
42 |
+
'method' => 'check_WC_API_access',
|
43 |
+
),
|
44 |
+
array(
|
45 |
+
'name' => 'WooCommerce authentication URL access',
|
46 |
+
'description' => 'Some hosts have unnecessarily intrusive security checks in place that prevent WooCommerce API authentication from working (mod_security rule #1234234). If this check fails, you will not be able authorize Printful app.',
|
47 |
+
'method' => 'check_WC_auth_url_access',
|
48 |
+
),
|
49 |
+
array(
|
50 |
+
'name' => 'WordPress remote requests',
|
51 |
+
'description' => 'WordPress needs to be able to connect to Printful server to call webhooks. If this check fails, contact your hosting support.',
|
52 |
+
'method' => 'check_remote_requests',
|
53 |
+
),
|
54 |
+
array(
|
55 |
+
'name' => 'Printful API key is set',
|
56 |
+
'description' => 'Your store needs access to Printful API to use most of it\'s features like shipping rates, tax rates and other settings.',
|
57 |
+
'method' => 'check_PF_API_key',
|
58 |
+
),
|
59 |
+
array(
|
60 |
+
'name' => 'Connection to Printful API',
|
61 |
+
'description' => 'Is your store successfully connected to Printful API',
|
62 |
+
'method' => 'check_PF_API_connect',
|
63 |
+
),
|
64 |
+
array(
|
65 |
+
'name' => 'Recent store sync errors',
|
66 |
+
'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.',
|
67 |
+
'method' => 'check_PF_sync_errors',
|
68 |
+
),
|
69 |
+
array(
|
70 |
+
'name' => 'Write permissions',
|
71 |
+
'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.',
|
72 |
+
'method' => 'check_uploads_write',
|
73 |
+
),
|
74 |
+
array(
|
75 |
+
'name' => 'PHP memory limit',
|
76 |
+
'description' => 'Set PHP allocated memory limit to at least 128mb. Contact your hosting provider if you need help with this.',
|
77 |
+
'method' => 'check_PHP_memory_limit',
|
78 |
+
),
|
79 |
+
array(
|
80 |
+
'name' => 'PHP script time limit',
|
81 |
+
'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.',
|
82 |
+
'method' => 'check_PHP_time_limit',
|
83 |
+
),
|
84 |
+
// array(
|
85 |
+
// 'name' => 'Error logs',
|
86 |
+
// 'description' => 'Your WordPress site needs to have it\'s error logging set up correctly so in case of issues you can figure out what\'s wrong. Note: your hosting might already have an independent log from WordPress.',
|
87 |
+
// 'method' => 'check_WP_error_logs',
|
88 |
+
// ),
|
89 |
+
array(
|
90 |
+
'name' => 'W3 Total Cache DB Cache',
|
91 |
+
'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.',
|
92 |
+
'method' => 'check_W3_db_cache',
|
93 |
+
'silent' => true,
|
94 |
+
),
|
95 |
+
array(
|
96 |
+
'name' => 'Remove Print Aura plugin',
|
97 |
+
'description' => 'Print Aura plugin is known to cause issues so it needs to be removed',
|
98 |
+
'method' => 'check_printaura_plugin',
|
99 |
+
'silent' => true,
|
100 |
+
),
|
101 |
+
);
|
102 |
+
|
103 |
+
/**
|
104 |
+
* @return Printful_Admin_Status
|
105 |
+
*/
|
106 |
+
public static function instance() {
|
107 |
+
|
108 |
+
if ( is_null( self::$_instance ) ) {
|
109 |
+
self::$_instance = new self();
|
110 |
+
}
|
111 |
+
|
112 |
+
return self::$_instance;
|
113 |
+
}
|
114 |
+
|
115 |
+
/**
|
116 |
+
* Setup the view variables
|
117 |
+
*/
|
118 |
+
public static function view() {
|
119 |
+
|
120 |
+
$status = self::instance();
|
121 |
+
$status->render();
|
122 |
+
}
|
123 |
+
|
124 |
+
/**
|
125 |
+
* Render the view
|
126 |
+
*/
|
127 |
+
public function render() {
|
128 |
+
|
129 |
+
Printful_Admin::load_template( 'header', array( 'tabs' => Printful_Admin::get_tabs() ) );
|
130 |
+
|
131 |
+
$checklist = self::get_checklist( true );
|
132 |
+
if ( $checklist ) {
|
133 |
+
Printful_Admin::load_template( 'status-table', array( 'checklist' => $checklist ) );
|
134 |
+
} else {
|
135 |
+
Printful_Admin::load_template( 'ajax-loader', array( 'action' => 'get_printful_status_checklist', 'message' => 'Testing your store (this may take up to 30 seconds)...' ) );
|
136 |
+
}
|
137 |
+
|
138 |
+
Printful_Admin::load_template( 'footer' );
|
139 |
+
}
|
140 |
+
|
141 |
+
/**
|
142 |
+
* Build the content for status page
|
143 |
+
*/
|
144 |
+
public static function render_status_table_ajax() {
|
145 |
+
|
146 |
+
$checklist = self::get_checklist();
|
147 |
+
Printful_Admin::load_template( 'status-table', array( 'checklist' => $checklist ) );
|
148 |
+
|
149 |
+
exit;
|
150 |
+
}
|
151 |
+
|
152 |
+
/**
|
153 |
+
* Run the tests
|
154 |
+
* @param bool $only_cached_results
|
155 |
+
* @return array
|
156 |
+
*/
|
157 |
+
public static function get_checklist($only_cached_results = false) {
|
158 |
+
|
159 |
+
$status = self::instance();
|
160 |
+
|
161 |
+
$list = get_transient(Printful_Admin_Status::PF_CACHED_CHECKLIST);
|
162 |
+
|
163 |
+
if ( $only_cached_results || $list ) {
|
164 |
+
return $list;
|
165 |
+
}
|
166 |
+
|
167 |
+
$list = array();
|
168 |
+
$list['overall_status'] = true;
|
169 |
+
$issueCount = 0;
|
170 |
+
|
171 |
+
foreach ( self::$checklist_items as $item ) {
|
172 |
+
$list_item = array();
|
173 |
+
$list_item['name'] = $item['name'];
|
174 |
+
$list_item['description'] = $item['description'];
|
175 |
+
|
176 |
+
if ( method_exists( $status, $item['method'] ) ) {
|
177 |
+
$list_item['status'] = $status->{$item['method']}();
|
178 |
+
|
179 |
+
if ( $status->should_result_be_visible( $list_item['status'], $item ) ) {
|
180 |
+
$list['items'][] = $list_item;
|
181 |
+
}
|
182 |
+
|
183 |
+
if ( $list_item['status'] == self::PF_STATUS_FAIL) {
|
184 |
+
$list['overall_status'] = false;
|
185 |
+
$issueCount ++;
|
186 |
+
}
|
187 |
+
}
|
188 |
+
}
|
189 |
+
|
190 |
+
set_transient( Printful_Admin_Status::PF_CACHED_CHECKLIST, $list, MINUTE_IN_SECONDS );
|
191 |
+
set_transient( Printful_Admin_Status::PF_STATUS_ISSUE_COUNT, $issueCount, HOUR_IN_SECONDS );
|
192 |
+
|
193 |
+
return $list;
|
194 |
+
}
|
195 |
+
|
196 |
+
/**
|
197 |
+
* Execute only one test
|
198 |
+
* @param $method
|
199 |
+
* @return mixed
|
200 |
+
*/
|
201 |
+
public function run_single_test( $method ) {
|
202 |
+
if ( method_exists( $this, $method ) ) {
|
203 |
+
return $this->{$method}();
|
204 |
+
}
|
205 |
+
return false;
|
206 |
+
}
|
207 |
+
|
208 |
+
/**
|
209 |
+
* @param $status
|
210 |
+
* @param bool $item
|
211 |
+
*
|
212 |
+
* @return int
|
213 |
+
*/
|
214 |
+
private function should_result_be_visible( $status, $item = false ) {
|
215 |
+
|
216 |
+
if ( ! isset( $item['silent'] ) || ( $item['silent'] === true && $status === self::PF_STATUS_FAIL ) ) { //silent items are only shown on FAIL
|
217 |
+
return true;
|
218 |
+
}
|
219 |
+
|
220 |
+
return false;
|
221 |
+
}
|
222 |
+
|
223 |
+
/**
|
224 |
+
* Function for checking if thumbnails are resized
|
225 |
+
*/
|
226 |
+
private function check_uploads_write() {
|
227 |
+
|
228 |
+
$upload_dir = wp_upload_dir();
|
229 |
+
if ( is_writable( $upload_dir['basedir'] ) ) {
|
230 |
+
return self::PF_STATUS_OK;
|
231 |
+
}
|
232 |
+
|
233 |
+
return self::PF_STATUS_FAIL;
|
234 |
+
}
|
235 |
+
|
236 |
+
/**
|
237 |
+
* @return int
|
238 |
+
*/
|
239 |
+
private function check_PHP_memory_limit() {
|
240 |
+
|
241 |
+
$memory_limit = ini_get( 'memory_limit' );
|
242 |
+
|
243 |
+
if ( preg_match( '/^(\d+)(.)$/', $memory_limit, $matches ) ) {
|
244 |
+
if ( $matches[2] == 'M' ) {
|
245 |
+
$memory_limit = $matches[1] * 1024 * 1024; // nnnM -> nnn MB
|
246 |
+
} else if ( $matches[2] == 'K' ) {
|
247 |
+
$memory_limit = $matches[1] * 1024; // nnnK -> nnn KB
|
248 |
+
}
|
249 |
+
}
|
250 |
+
|
251 |
+
$ok = ( $memory_limit >= 128 * 1024 * 1024 ); // at least 128M?
|
252 |
+
|
253 |
+
if ( $ok ) {
|
254 |
+
return self::PF_STATUS_OK;
|
255 |
+
}
|
256 |
+
|
257 |
+
return self::PF_STATUS_FAIL;
|
258 |
+
}
|
259 |
+
|
260 |
+
/**
|
261 |
+
* @return int
|
262 |
+
*/
|
263 |
+
private function check_WP_version() {
|
264 |
+
|
265 |
+
$current = get_bloginfo( 'version' );
|
266 |
+
|
267 |
+
try {
|
268 |
+
$url = 'https://api.wordpress.org/core/version-check/1.7/';
|
269 |
+
$response = wp_remote_get( $url );
|
270 |
+
|
271 |
+
if ( ! is_wp_error( $response ) ) {
|
272 |
+
$json = $response['body'];
|
273 |
+
$obj = json_decode( $json );
|
274 |
+
}
|
275 |
+
|
276 |
+
if ( empty( $obj ) ) {
|
277 |
+
return self::PF_STATUS_FAIL;
|
278 |
+
}
|
279 |
+
|
280 |
+
$version = $obj->offers[0];
|
281 |
+
$latest = $version->version;
|
282 |
+
|
283 |
+
} catch ( Exception $e ) {
|
284 |
+
return self::PF_STATUS_FAIL;
|
285 |
+
}
|
286 |
+
|
287 |
+
if ( ! $latest ) {
|
288 |
+
return self::PF_STATUS_FAIL;
|
289 |
+
}
|
290 |
+
|
291 |
+
if ( version_compare( $current, $latest, '==' ) ) {
|
292 |
+
return self::PF_STATUS_OK;
|
293 |
+
}
|
294 |
+
|
295 |
+
return self::PF_STATUS_FAIL;
|
296 |
+
}
|
297 |
+
|
298 |
+
/**
|
299 |
+
* @return int
|
300 |
+
*/
|
301 |
+
private function check_PF_webhooks() {
|
302 |
+
|
303 |
+
// Query args
|
304 |
+
$args = array(
|
305 |
+
'post_type' => 'shop_webhook',
|
306 |
+
'nopaging' => true,
|
307 |
+
'ignore_sticky_posts' => true,
|
308 |
+
's' => self::PF_WEBHOOK_NAME,
|
309 |
+
'post_status' => 'published',
|
310 |
+
);
|
311 |
+
|
312 |
+
// Get the webhooks
|
313 |
+
$webhook_results = new WP_Query( $args );
|
314 |
+
$webhooks = $webhook_results->posts;
|
315 |
+
|
316 |
+
if ( count( $webhooks ) > 0 ) {
|
317 |
+
return self::PF_STATUS_OK;
|
318 |
+
}
|
319 |
+
|
320 |
+
return self::PF_STATUS_FAIL;
|
321 |
+
}
|
322 |
+
|
323 |
+
/**
|
324 |
+
* @return int
|
325 |
+
*/
|
326 |
+
private function check_WC_API_access() {
|
327 |
+
|
328 |
+
global $wpdb;
|
329 |
+
|
330 |
+
//if any keys are set
|
331 |
+
$query = "SELECT COUNT(*) as key_count FROM {$wpdb->prefix}woocommerce_api_keys";
|
332 |
+
$count = $wpdb->get_var( $query );
|
333 |
+
|
334 |
+
if ( $count == 0 ) {
|
335 |
+
return self::PF_STATUS_FAIL;
|
336 |
+
}
|
337 |
+
|
338 |
+
// Get the API key with matching description
|
339 |
+
$search = "AND description LIKE '%" . esc_sql( $wpdb->esc_like( wc_clean( self::API_KEY_SEARCH_STRING ) ) ) . "%' ";
|
340 |
+
$query = "SELECT * FROM {$wpdb->prefix}woocommerce_api_keys WHERE 1 = 1 {$search} ORDER BY last_access DESC LIMIT 1";
|
341 |
+
$key = $wpdb->get_row( $query );
|
342 |
+
|
343 |
+
if ( ! empty( $key ) && $key->permissions == 'read_write' ) {
|
344 |
+
return self::PF_STATUS_OK;
|
345 |
+
}
|
346 |
+
|
347 |
+
return self::PF_STATUS_WARNING;
|
348 |
+
}
|
349 |
+
|
350 |
+
/**
|
351 |
+
* @return int
|
352 |
+
*/
|
353 |
+
private function check_PF_API_key() {
|
354 |
+
|
355 |
+
$option = get_option( 'woocommerce_printful_settings', array() );
|
356 |
+
if ( ! empty( $option['printful_key'] ) && strlen( $option['printful_key'] ) == 36 ) {
|
357 |
+
return self::PF_STATUS_OK;
|
358 |
+
}
|
359 |
+
|
360 |
+
return self::PF_STATUS_FAIL;
|
361 |
+
}
|
362 |
+
|
363 |
+
/**
|
364 |
+
* @return int
|
365 |
+
*/
|
366 |
+
private function check_PF_API_connect() {
|
367 |
+
|
368 |
+
if ( Printful_Integration::instance()->is_connected(true) ) {
|
369 |
+
return self::PF_STATUS_OK;
|
370 |
+
}
|
371 |
+
|
372 |
+
return self::PF_STATUS_FAIL;
|
373 |
+
}
|
374 |
+
|
375 |
+
/**
|
376 |
+
* @return int
|
377 |
+
*/
|
378 |
+
private function check_PHP_time_limit() {
|
379 |
+
$time_limit = ini_get( 'max_execution_time' );
|
380 |
+
|
381 |
+
if ( $time_limit >= 30 ) {
|
382 |
+
return self::PF_STATUS_OK;
|
383 |
+
}
|
384 |
+
|
385 |
+
return self::PF_STATUS_FAIL;
|
386 |
+
}
|
387 |
+
|
388 |
+
/**
|
389 |
+
* @return int
|
390 |
+
*/
|
391 |
+
private function check_PF_sync_errors() {
|
392 |
+
|
393 |
+
$sync_log = get_option( Printful_Request_log::PF_OPTION_INCOMING_API_REQUEST_LOG, array() );
|
394 |
+
if ( empty( $sync_log ) ) {
|
395 |
+
return self::PF_STATUS_OK; //no results means no errors
|
396 |
+
}
|
397 |
+
|
398 |
+
$sync_log = array_reverse( $sync_log );
|
399 |
+
$sync_log = array_slice( $sync_log, 0, 6 ); //we only care about last to syncs
|
400 |
+
|
401 |
+
foreach ( $sync_log as $sl ) {
|
402 |
+
if ( ! empty( $sl['result'] ) && $sl['result'] == 'ERROR' ) {
|
403 |
+
return self::PF_STATUS_FAIL;
|
404 |
+
}
|
405 |
+
}
|
406 |
+
|
407 |
+
return self::PF_STATUS_OK;
|
408 |
+
}
|
409 |
+
|
410 |
+
/**
|
411 |
+
* @return int
|
412 |
+
*/
|
413 |
+
private function check_W3_db_cache() {
|
414 |
+
|
415 |
+
if ( ! is_plugin_active( 'w3-total-cache/w3-total-cache.php' ) ) {
|
416 |
+
return self::PF_STATUS_OK;
|
417 |
+
}
|
418 |
+
|
419 |
+
$w3tc_config_file = get_home_path() . 'wp-content/w3tc-config/master.php';
|
420 |
+
if ( file_exists( $w3tc_config_file ) && is_readable( $w3tc_config_file ) ) {
|
421 |
+
$content = @file_get_contents( $w3tc_config_file );
|
422 |
+
$config = @json_decode( substr( $content, 14 ), true );
|
423 |
+
|
424 |
+
if ( is_array( $config ) && ! empty( $config['dbcache.enabled'] ) ) {
|
425 |
+
return ! $config['dbcache.enabled'];
|
426 |
+
}
|
427 |
+
}
|
428 |
+
|
429 |
+
return self::PF_STATUS_OK;
|
430 |
+
}
|
431 |
+
|
432 |
+
/**
|
433 |
+
* @return int
|
434 |
+
*/
|
435 |
+
private function check_WC_API_enabled() {
|
436 |
+
|
437 |
+
$enabled = get_option('woocommerce_api_enabled', false);
|
438 |
+
|
439 |
+
if($enabled == 'yes') {
|
440 |
+
return self::PF_STATUS_OK;
|
441 |
+
}
|
442 |
+
|
443 |
+
return self::PF_STATUS_FAIL;
|
444 |
+
}
|
445 |
+
|
446 |
+
/**
|
447 |
+
* @return int
|
448 |
+
*/
|
449 |
+
private function check_permalinks() {
|
450 |
+
|
451 |
+
$permalinks = get_option( 'permalink_structure', false );
|
452 |
+
|
453 |
+
if ( $permalinks && strlen( $permalinks ) > 0 ) {
|
454 |
+
return self::PF_STATUS_OK;
|
455 |
+
}
|
456 |
+
|
457 |
+
return self::PF_STATUS_FAIL;
|
458 |
+
}
|
459 |
+
|
460 |
+
/**
|
461 |
+
* @return int
|
462 |
+
*/
|
463 |
+
private function check_printaura_plugin() {
|
464 |
+
|
465 |
+
if ( ! is_plugin_active( 'printaura-woocommerce-api/printaura-woocommerce-api.php' ) ) {
|
466 |
+
return self::PF_STATUS_OK;
|
467 |
+
}
|
468 |
+
|
469 |
+
return self::PF_STATUS_FAIL;
|
470 |
+
}
|
471 |
+
|
472 |
+
/**
|
473 |
+
* @return int
|
474 |
+
*/
|
475 |
+
private function check_remote_requests() {
|
476 |
+
|
477 |
+
// Setup request args.
|
478 |
+
$http_args = array(
|
479 |
+
'method' => 'POST',
|
480 |
+
'timeout' => MINUTE_IN_SECONDS,
|
481 |
+
'redirection' => 0,
|
482 |
+
'httpversion' => '1.0',
|
483 |
+
'blocking' => true,
|
484 |
+
'user-agent' => sprintf( 'WooCommerce/%s Hookshot (WordPress/%s)', WC_VERSION, $GLOBALS['wp_version'] ),
|
485 |
+
'body' => trim( json_encode( array( 'test' => true ) ) ),
|
486 |
+
'headers' => array( 'Content-Type' => 'application/json' ),
|
487 |
+
'cookies' => array(),
|
488 |
+
);
|
489 |
+
|
490 |
+
// Add custom headers.
|
491 |
+
$http_args['headers']['X-WC-Webhook-Source'] = home_url( '/' ); // Since 2.6.0.
|
492 |
+
$http_args['headers']['X-WC-Webhook-Topic'] = self::PF_REMOTE_REQUEST_TOPIC;
|
493 |
+
|
494 |
+
// Webhook away!
|
495 |
+
$response = wp_safe_remote_request( Printful_Base::get_printful_host() . self::PF_REMOTE_REQUEST_URL, $http_args );
|
496 |
+
|
497 |
+
if ( is_wp_error( $response ) ) {
|
498 |
+
return self::PF_STATUS_FAIL;
|
499 |
+
}
|
500 |
+
|
501 |
+
return self::PF_STATUS_OK;
|
502 |
+
}
|
503 |
+
|
504 |
+
/**
|
505 |
+
* @return int
|
506 |
+
*/
|
507 |
+
private function check_WC_auth_url_access() {
|
508 |
+
|
509 |
+
$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%2Fwww.printful.com%2Fwebhook%2Fwoocommerce-auth-callback';
|
510 |
+
$http_args = array(
|
511 |
+
'timeout' => 60,
|
512 |
+
'method' => 'GET',
|
513 |
+
'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',
|
514 |
+
);
|
515 |
+
|
516 |
+
$response = wp_safe_remote_get( $url, $http_args );
|
517 |
+
$code = $response['response']['code'];
|
518 |
+
|
519 |
+
if ( $code == 200 ) {
|
520 |
+
return self::PF_STATUS_OK;
|
521 |
+
}
|
522 |
+
|
523 |
+
return self::PF_STATUS_FAIL;
|
524 |
+
}
|
525 |
+
|
526 |
+
/**
|
527 |
+
* @return int
|
528 |
+
*/
|
529 |
+
private function check_WP_error_logs() {
|
530 |
+
|
531 |
+
if (
|
532 |
+
( defined( 'WP_DEBUG' ) && WP_DEBUG == true )
|
533 |
+
&&
|
534 |
+
( defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG == true )
|
535 |
+
) {
|
536 |
+
return self::PF_STATUS_OK;
|
537 |
+
}
|
538 |
+
|
539 |
+
return self::PF_STATUS_FAIL;
|
540 |
+
}
|
541 |
+
}
|
includes/class-printful-admin-support.php
ADDED
@@ -0,0 +1,235 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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)...' ) );
|
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 |
+
*/
|
58 |
+
public function generate_report() {
|
59 |
+
|
60 |
+
if ( ! class_exists( 'WC_REST_System_Status_Controller' ) ) {
|
61 |
+
return false;
|
62 |
+
}
|
63 |
+
|
64 |
+
$system_status = new WC_REST_System_Status_Controller; //make use of the woocommerce system status report
|
65 |
+
|
66 |
+
ob_start();
|
67 |
+
|
68 |
+
echo "##### Printful Checklist #####\n";
|
69 |
+
$checklist = Printful_Admin_Status::get_checklist();
|
70 |
+
foreach ( $checklist['items'] as $item ) {
|
71 |
+
|
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 |
+
}
|
78 |
+
echo str_pad( esc_html($item['name']), 30 ) . "=> " . esc_html($status) . "\n";
|
79 |
+
}
|
80 |
+
|
81 |
+
echo "\n\n##### Printful Last Sync's #####\n";
|
82 |
+
$syncReport = $this->get_sync_report();
|
83 |
+
if ( ! empty( $syncReport ) ) {
|
84 |
+
|
85 |
+
echo str_pad( 'Date', 30 );
|
86 |
+
echo str_pad( 'Request', 30 );
|
87 |
+
echo str_pad( 'Message', 30 );
|
88 |
+
echo "\n";
|
89 |
+
|
90 |
+
foreach ( $syncReport as $sr ) {
|
91 |
+
|
92 |
+
echo str_pad( esc_html($sr['date']) . ';', 30 );
|
93 |
+
echo str_pad( esc_html($sr['path']) . ';', 30 );
|
94 |
+
echo str_pad( esc_html($sr['message']) . ';', 30 );
|
95 |
+
|
96 |
+
echo "\n";
|
97 |
+
}
|
98 |
+
}
|
99 |
+
|
100 |
+
echo "\n\n##### Environment #####\n";
|
101 |
+
$this->output_report_block( $system_status->get_environment_info() );
|
102 |
+
|
103 |
+
echo "\n\n##### Database #####\n";
|
104 |
+
$this->output_report_block( $system_status->get_database_info() );
|
105 |
+
|
106 |
+
echo "\n\n##### Active Plugins #####\n";
|
107 |
+
foreach ( $system_status->get_active_plugins() as $plugin ) {
|
108 |
+
if ( ! empty( $plugin['name'] ) ) {
|
109 |
+
echo esc_html( $plugin['name'] ) . " (" . $plugin['version'] . ")\n";
|
110 |
+
}
|
111 |
+
}
|
112 |
+
|
113 |
+
echo "\n\n##### Theme #####\n";
|
114 |
+
$this->output_report_block( $system_status->get_theme_info() );
|
115 |
+
|
116 |
+
echo "\n\n##### WooCommerce settings #####\n";
|
117 |
+
$this->output_report_block( $system_status->get_settings() );
|
118 |
+
|
119 |
+
if (
|
120 |
+
( defined( 'WP_DEBUG' ) && WP_DEBUG == true )
|
121 |
+
&&
|
122 |
+
( defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG == true )
|
123 |
+
) {
|
124 |
+
echo "\n\n##### Wordpress Error log (last 50 entries) #####\n";
|
125 |
+
$contents = $this->get_error_log_contents();
|
126 |
+
if ( $contents ) {
|
127 |
+
print_r( $contents );
|
128 |
+
}
|
129 |
+
}
|
130 |
+
|
131 |
+
$report = ob_get_contents();
|
132 |
+
ob_end_clean();
|
133 |
+
|
134 |
+
return $report;
|
135 |
+
}
|
136 |
+
|
137 |
+
/**
|
138 |
+
* Get last 50 lines of error log
|
139 |
+
* @return bool|string
|
140 |
+
*/
|
141 |
+
public function get_error_log_contents() {
|
142 |
+
|
143 |
+
if ( ! function_exists( 'fopen' ) ) {
|
144 |
+
return false;
|
145 |
+
}
|
146 |
+
|
147 |
+
return $this->file_tail( WP_CONTENT_DIR . '/debug.log', 50 );
|
148 |
+
}
|
149 |
+
|
150 |
+
/**
|
151 |
+
* source: https://gist.github.com/lorenzos/1711e81a9162320fde20
|
152 |
+
* @param $filepath
|
153 |
+
* @param int $lines
|
154 |
+
* @param bool $adaptive
|
155 |
+
*
|
156 |
+
* @return bool|string
|
157 |
+
*/
|
158 |
+
function file_tail( $filepath, $lines = 1, $adaptive = true ) {
|
159 |
+
|
160 |
+
$f = @fopen( $filepath, "rb" );
|
161 |
+
if ( $f === false ) {
|
162 |
+
return false;
|
163 |
+
}
|
164 |
+
|
165 |
+
// Sets buffer size, according to the number of lines to retrieve.
|
166 |
+
if ( ! $adaptive ) {
|
167 |
+
$buffer = 4096;
|
168 |
+
} else {
|
169 |
+
$buffer = ( $lines < 2 ? 64 : ( $lines < 10 ? 512 : 4096 ) );
|
170 |
+
}
|
171 |
+
|
172 |
+
// Jump to last character
|
173 |
+
fseek( $f, - 1, SEEK_END );
|
174 |
+
if ( fread( $f, 1 ) != "\n" ) {
|
175 |
+
$lines -= 1;
|
176 |
+
}
|
177 |
+
|
178 |
+
$output = '';
|
179 |
+
$chunk = '';
|
180 |
+
while ( ftell( $f ) > 0 && $lines >= 0 ) {
|
181 |
+
// Figure out how far back we should jump
|
182 |
+
$seek = min( ftell( $f ), $buffer );
|
183 |
+
// Do the jump (backwards, relative to where we are)
|
184 |
+
fseek( $f, - $seek, SEEK_CUR );
|
185 |
+
$output = ( $chunk = fread( $f, $seek ) ) . $output;
|
186 |
+
fseek( $f, - mb_strlen( $chunk, '8bit' ), SEEK_CUR );
|
187 |
+
$lines -= substr_count( $chunk, "\n" );
|
188 |
+
}
|
189 |
+
while ( $lines ++ < 0 ) {
|
190 |
+
$output = substr( $output, strpos( $output, "\n" ) + 1 );
|
191 |
+
}
|
192 |
+
fclose( $f );
|
193 |
+
|
194 |
+
return trim( $output );
|
195 |
+
}
|
196 |
+
|
197 |
+
|
198 |
+
/**
|
199 |
+
* Displays the data
|
200 |
+
* @param $data
|
201 |
+
*/
|
202 |
+
public function output_report_block( $data ) {
|
203 |
+
|
204 |
+
foreach ( $data as $key => $item ) {
|
205 |
+
if ( is_string( $item ) ) {
|
206 |
+
echo str_pad( $key, 30 ) . "=> " . $item . "\n";
|
207 |
+
}
|
208 |
+
}
|
209 |
+
}
|
210 |
+
|
211 |
+
/**
|
212 |
+
* Returns log of last incoming API requests from Printful
|
213 |
+
* @return array
|
214 |
+
*/
|
215 |
+
public function get_sync_report() {
|
216 |
+
|
217 |
+
$report = array();
|
218 |
+
$request_log = get_option( Printful_Request_log::PF_OPTION_INCOMING_API_REQUEST_LOG, array() );
|
219 |
+
$request_log = array_reverse( $request_log );
|
220 |
+
|
221 |
+
if ( empty( $request_log ) ) {
|
222 |
+
return $report;
|
223 |
+
}
|
224 |
+
|
225 |
+
foreach ( $request_log as $log ) {
|
226 |
+
$report[] = array(
|
227 |
+
'date' => $log['date'],
|
228 |
+
'path' => $log['request'],
|
229 |
+
'message' => $log['result'],
|
230 |
+
);
|
231 |
+
}
|
232 |
+
|
233 |
+
return $report;
|
234 |
+
}
|
235 |
+
}
|
includes/class-printful-admin.php
ADDED
@@ -0,0 +1,167 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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( 'printful-dashboard', plugins_url( '../assets/css/dashboard.css', __FILE__ ) );
|
39 |
+
wp_enqueue_style( 'printful-status', plugins_url( '../assets/css/status.css', __FILE__ ) );
|
40 |
+
wp_enqueue_style( 'printful-support', plugins_url( '../assets/css/support.css', __FILE__ ) );
|
41 |
+
wp_enqueue_style( 'printful-settings', plugins_url( '../assets/css/settings.css', __FILE__ ) );
|
42 |
+
}
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Loads stylesheet for printful toolbar element
|
47 |
+
*/
|
48 |
+
public function add_global_style() {
|
49 |
+
wp_enqueue_style( 'printful-global', plugins_url( '../assets/css/global.css', __FILE__ ) );
|
50 |
+
}
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Loads scripts used in printful admin pages
|
54 |
+
* @param $hook
|
55 |
+
*/
|
56 |
+
public function add_admin_scripts($hook) {
|
57 |
+
if ( strpos( $hook, 'printful-dashboard' ) !== false ) {
|
58 |
+
wp_enqueue_script( 'printful-settings', plugins_url( '../assets/js/settings.js', __FILE__ ) );
|
59 |
+
wp_enqueue_script( 'printful-connect', plugins_url( '../assets/js/connect.js', __FILE__ ) );
|
60 |
+
wp_enqueue_script( 'printful-block-loader', plugins_url( '../assets/js/block-loader.js', __FILE__ ) );
|
61 |
+
wp_enqueue_script( 'printful-intercom', plugins_url( '../assets/js/intercom.min.js', __FILE__ ) );
|
62 |
+
}
|
63 |
+
}
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Register admin menu pages
|
67 |
+
*/
|
68 |
+
public function register_admin_menu_page() {
|
69 |
+
|
70 |
+
add_menu_page(
|
71 |
+
self::PAGE_TITLE_DASHBOARD,
|
72 |
+
self::MENU_TITLE_TOP,
|
73 |
+
self::CAPABILITY,
|
74 |
+
self::MENU_SLUG_DASHBOARD,
|
75 |
+
array( 'Printful_Admin', 'route' ),
|
76 |
+
Printful_Base::get_asset_url() . 'images/printful-menu-icon.png',
|
77 |
+
58
|
78 |
+
);
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Route the tabs
|
83 |
+
*/
|
84 |
+
public static function route() {
|
85 |
+
|
86 |
+
$tabs = array(
|
87 |
+
'dashboard' => 'Printful_Admin_Dashboard',
|
88 |
+
'settings' => 'Printful_Admin_Settings',
|
89 |
+
'status' => 'Printful_Admin_Status',
|
90 |
+
'support' => 'Printful_Admin_Support',
|
91 |
+
);
|
92 |
+
|
93 |
+
$tab = ( ! empty( $_GET['tab'] ) ? $_GET['tab'] : 'dashboard' );
|
94 |
+
if ( ! empty( $tabs[ $tab ] ) ) {
|
95 |
+
call_user_func( array( $tabs[ $tab ], 'view' ) );
|
96 |
+
}
|
97 |
+
}
|
98 |
+
|
99 |
+
/**
|
100 |
+
* Get the tabs used in printful admin pages
|
101 |
+
* @return array
|
102 |
+
*/
|
103 |
+
public static function get_tabs() {
|
104 |
+
|
105 |
+
$tabs = array(
|
106 |
+
array( 'name' => 'Settings', 'tab_url' => 'settings' ),
|
107 |
+
array( 'name' => 'Status', 'tab_url' => 'status' ),
|
108 |
+
array( 'name' => 'Support', 'tab_url' => 'support' ),
|
109 |
+
);
|
110 |
+
|
111 |
+
if ( Printful_Integration::instance()->is_connected() ) {
|
112 |
+
array_unshift( $tabs, array( 'name' => 'Dashboard', 'tab_url' => false ) );
|
113 |
+
} else {
|
114 |
+
array_unshift( $tabs, array( 'name' => 'Connect', 'tab_url' => false ) );
|
115 |
+
}
|
116 |
+
|
117 |
+
return $tabs;
|
118 |
+
}
|
119 |
+
|
120 |
+
/**
|
121 |
+
* Create the printful toolbar
|
122 |
+
* @param $wp_admin_bar
|
123 |
+
*/
|
124 |
+
public function add_printful_status_toolbar( $wp_admin_bar ) {
|
125 |
+
|
126 |
+
$issueCount = get_transient( Printful_Admin_Status::PF_STATUS_ISSUE_COUNT );
|
127 |
+
|
128 |
+
if ( $issueCount ) {
|
129 |
+
//Add top level menu item
|
130 |
+
$args = array(
|
131 |
+
'id' => 'printful_toolbar',
|
132 |
+
'title' => 'Printful Integration' . ( $issueCount > 0 ? ' <span class="printful-toolbar-issues">' . esc_attr( $issueCount ) . '</span>' : '' ),
|
133 |
+
'href' => get_admin_url( null, 'admin.php?page=' . Printful_Admin::MENU_SLUG_DASHBOARD ),
|
134 |
+
'meta' => array( 'class' => 'printful-toolbar' ),
|
135 |
+
);
|
136 |
+
$wp_admin_bar->add_node( $args );
|
137 |
+
|
138 |
+
//Add status
|
139 |
+
$args = array(
|
140 |
+
'id' => 'printful_toolbar_status',
|
141 |
+
'parent' => 'printful_toolbar',
|
142 |
+
'title' => 'Integration status' . ( $issueCount > 0 ? ' (' . esc_attr( $issueCount ) . _n( ' issue', ' issues', $issueCount ) . ')' : '' ),
|
143 |
+
'href' => get_admin_url( null, 'admin.php?page=' . Printful_Admin::MENU_SLUG_DASHBOARD . '&tab=status' ),
|
144 |
+
'meta' => array( 'class' => 'printful-toolbar-status' ),
|
145 |
+
);
|
146 |
+
$wp_admin_bar->add_node( $args );
|
147 |
+
}
|
148 |
+
}
|
149 |
+
|
150 |
+
/**
|
151 |
+
* Load a template file. Extract any variables that are passed
|
152 |
+
* @param $name
|
153 |
+
* @param array $variables
|
154 |
+
*/
|
155 |
+
public static function load_template( $name, $variables = array() ) {
|
156 |
+
|
157 |
+
if ( ! empty( $variables ) ) {
|
158 |
+
extract( $variables );
|
159 |
+
}
|
160 |
+
|
161 |
+
$filename = plugin_dir_path( __FILE__ ) . 'templates/' . $name . '.php';
|
162 |
+
if ( file_exists( $filename ) ) {
|
163 |
+
include( $filename );
|
164 |
+
}
|
165 |
+
}
|
166 |
+
|
167 |
+
}
|
includes/class-printful-api-resource.php
CHANGED
@@ -6,32 +6,119 @@ class Printful_API_Resource extends WC_API_Resource {
|
|
6 |
/** @var string $base the route base */
|
7 |
protected $base = '/printful';
|
8 |
|
9 |
-
|
10 |
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
|
17 |
/**
|
18 |
* Allow remotely get plugin version for debug purposes
|
19 |
*/
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
|
37 |
}
|
6 |
/** @var string $base the route base */
|
7 |
protected $base = '/printful';
|
8 |
|
9 |
+
public function register_routes( $routes ) {
|
10 |
|
11 |
+
$routes[ $this->base . '/version' ] = array(
|
12 |
+
array( array( $this, 'get_status' ), WC_API_Server::READABLE | WC_API_Server::ACCEPT_DATA ),
|
13 |
+
);
|
14 |
+
|
15 |
+
$routes[ $this->base . '/access' ] = array(
|
16 |
+
array( array( $this, 'put_access_data' ), WC_API_Server::EDITABLE | WC_API_Server::ACCEPT_DATA ),
|
17 |
+
);
|
18 |
+
|
19 |
+
$routes[ $this->base . '/products/(?P<product_id>\d+)/size-chart' ] = array(
|
20 |
+
array( array( $this, 'post_size_chart' ), WC_API_Server::EDITABLE | WC_API_Server::ACCEPT_DATA ),
|
21 |
+
);
|
22 |
+
|
23 |
+
return $routes;
|
24 |
+
}
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Push size chart to meta property
|
28 |
+
* @param $product_id
|
29 |
+
* @param $data
|
30 |
+
* @return array|WP_Error
|
31 |
+
*/
|
32 |
+
public function post_size_chart( $product_id, $data ) {
|
33 |
+
|
34 |
+
if ( empty( $data['size_chart'] ) ) {
|
35 |
+
return new WP_Error( 'printful_api_size_chart_empty', __( 'No size chart was provided', 'printful' ), array( 'status' => 400 ) );
|
36 |
+
}
|
37 |
+
|
38 |
+
//product id is valid
|
39 |
+
$product_id = intval( $product_id );
|
40 |
+
if ( $product_id < 1 ) {
|
41 |
+
return new WP_Error( 'printful_api_product_not_found', __( 'The product ID is invalid', 'printful' ), array( 'status' => 400 ) );
|
42 |
+
}
|
43 |
+
|
44 |
+
//product exists
|
45 |
+
$product = get_post( $product_id );
|
46 |
+
if ( empty( $product ) || $product->post_type != 'product' ) {
|
47 |
+
return new WP_Error( 'printful_api_product_not_found', __( 'The product is not found', 'printful' ), array( 'status' => 400 ) );
|
48 |
+
}
|
49 |
+
|
50 |
+
//how about permissions?
|
51 |
+
$post_type = get_post_type_object( $product->post_type );
|
52 |
+
if ( ! current_user_can( $post_type->cap->edit_post, $product->ID ) ) {
|
53 |
+
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 ) );
|
54 |
+
}
|
55 |
+
|
56 |
+
//lets do this
|
57 |
+
update_post_meta( $product_id, 'pf_size_chart', htmlspecialchars( $data['size_chart'] ) );
|
58 |
+
|
59 |
+
return array(
|
60 |
+
'product' => $product,
|
61 |
+
'size_chart' => $data['size_chart'],
|
62 |
+
);
|
63 |
+
}
|
64 |
|
65 |
/**
|
66 |
* Allow remotely get plugin version for debug purposes
|
67 |
*/
|
68 |
+
public function get_status() {
|
69 |
+
|
70 |
+
$error = false;
|
71 |
+
try {
|
72 |
+
$client = Printful_Integration::instance()->get_client();
|
73 |
+
$storeData = $client->get( 'store' );
|
74 |
+
} catch ( Exception $e ) {
|
75 |
+
$error = $e->getMessage();
|
76 |
+
}
|
77 |
+
|
78 |
+
$checklist = Printful_Admin_Status::get_checklist();
|
79 |
+
$checklist['overall_status'] = ( $checklist['overall_status'] ? 'OK' : 'FAIL' );
|
80 |
+
|
81 |
+
foreach ( $checklist['items'] as $key => $item ) {
|
82 |
+
$item['status'] = ( $item['status'] ? 'OK' : 'FAIL' );
|
83 |
+
$checklist['items'][ $key ] = $item;
|
84 |
+
}
|
85 |
+
|
86 |
+
return array(
|
87 |
+
'version' => Printful_Base::VERSION,
|
88 |
+
'api_key' => Printful_Integration::instance()->get_option('printful_key'),
|
89 |
+
'store_id' => ! empty( $storeData['id'] ) ? $storeData['id'] : false,
|
90 |
+
'error' => $error,
|
91 |
+
'status_checklist' => $checklist,
|
92 |
+
);
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* @param $data
|
97 |
+
*
|
98 |
+
* @return array
|
99 |
+
*/
|
100 |
+
public function put_access_data( $data ) {
|
101 |
+
|
102 |
+
$error = false;
|
103 |
+
|
104 |
+
$apiKey = $data['accessKey'];
|
105 |
+
$storeId = $data['storeId'];
|
106 |
+
|
107 |
+
$option = Printful_Integration::instance()->get_option( 'printful_settings', array() );
|
108 |
+
$storeId = intval( $storeId );
|
109 |
+
|
110 |
+
if ( ! is_string( $apiKey ) || strlen( $apiKey ) == 0 || $storeId == 0 ) {
|
111 |
+
$error = 'Failed to update access data';
|
112 |
+
}
|
113 |
+
|
114 |
+
$option['printful_key'] = $apiKey;
|
115 |
+
$option['printful_store_id'] = $storeId;
|
116 |
+
|
117 |
+
Printful_Integration::instance()->update_settings( $option );
|
118 |
+
|
119 |
+
return array(
|
120 |
+
'error' => $error,
|
121 |
+
);
|
122 |
+
}
|
123 |
|
124 |
}
|
includes/class-printful-carriers.php
ADDED
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
|
29 |
+
$carriers = get_transient( 'printful_carriers' );
|
30 |
+
if ( ! $carriers ) {
|
31 |
+
$carriers = $this->refresh_carriers();
|
32 |
+
}
|
33 |
+
|
34 |
+
return $carriers;
|
35 |
+
}
|
36 |
+
|
37 |
+
/**
|
38 |
+
* Refresh carrier data from Printful
|
39 |
+
* @return mixed
|
40 |
+
*/
|
41 |
+
public function refresh_carriers() {
|
42 |
+
|
43 |
+
try {
|
44 |
+
$carriers = Printful_Integration::instance()->get_client()->get( 'store/carriers-services' );
|
45 |
+
$this->update_carrier_cache( $carriers );
|
46 |
+
} catch (PrintfulApiException $e) {
|
47 |
+
$carriers = array();
|
48 |
+
} catch (PrintfulException $e) {
|
49 |
+
$carriers = array();
|
50 |
+
}
|
51 |
+
|
52 |
+
return $carriers;
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Update carrier transient
|
57 |
+
* @param $carriers
|
58 |
+
*/
|
59 |
+
public function update_carrier_cache($carriers) {
|
60 |
+
|
61 |
+
set_transient( 'printful_carriers', $carriers, MINUTE_IN_SECONDS * 5); //5mins
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Post carrier settings to printful
|
66 |
+
* @param $data
|
67 |
+
* @return mixed
|
68 |
+
*/
|
69 |
+
public function post_carriers( $data ) {
|
70 |
+
|
71 |
+
if ( empty( $data ) ) {
|
72 |
+
return false;
|
73 |
+
}
|
74 |
+
|
75 |
+
$shipping = new self;
|
76 |
+
try {
|
77 |
+
$carriers = Printful_Integration::instance()->get_client()->post( 'store/carriers-services', $data );
|
78 |
+
$shipping->update_carrier_cache( $carriers );
|
79 |
+
} catch ( PrintfulApiException $e ) {
|
80 |
+
$carriers = false;
|
81 |
+
} catch (PrintfulException $e) {
|
82 |
+
$carriers = false;
|
83 |
+
}
|
84 |
+
|
85 |
+
return $carriers;
|
86 |
+
}
|
87 |
+
|
88 |
+
}
|
includes/class-printful-client.php
CHANGED
@@ -1,47 +1,54 @@
|
|
1 |
<?php
|
|
|
|
|
2 |
/**
|
3 |
* Printful API client
|
4 |
*/
|
5 |
-
|
6 |
class Printful_Client {
|
7 |
-
private $key = false;
|
8 |
-
private $lastResponseRaw;
|
9 |
-
private $lastResponse;
|
10 |
-
private $userAgent = 'Printful WooCommerce Plugin';
|
11 |
-
private $apiUrl = 'https://api.theprintful.com/';
|
12 |
-
|
13 |
-
/**
|
14 |
-
* @param string $key Printful Store API key
|
15 |
-
* @param string $disable_ssl Force HTTP instead of HTTPS for API requests
|
16 |
-
* @throws PrintfulException if the library failed to initialize
|
17 |
-
*/
|
18 |
-
public function __construct($key = '', $disable_ssl = false){
|
19 |
-
$key = (string)$key;
|
20 |
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
|
37 |
/**
|
38 |
* Returns total available item count from the last request if it supports paging (e.g order list) or null otherwise.
|
39 |
*
|
40 |
* @return int|null Item count
|
41 |
*/
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
|
46 |
/**
|
47 |
* Perform a GET request to the API
|
@@ -51,9 +58,9 @@ class Printful_Client {
|
|
51 |
* @throws PrintfulApiException if the API call status code is not in the 2xx range
|
52 |
* @throws PrintfulException if the API call has failed or the response is invalid
|
53 |
*/
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
|
58 |
/**
|
59 |
* Perform a DELETE request to the API
|
@@ -63,9 +70,9 @@ class Printful_Client {
|
|
63 |
* @throws PrintfulApiException if the API call status code is not in the 2xx range
|
64 |
* @throws PrintfulException if the API call has failed or the response is invalid
|
65 |
*/
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
|
70 |
/**
|
71 |
* Perform a POST request to the API
|
@@ -76,9 +83,9 @@ class Printful_Client {
|
|
76 |
* @throws PrintfulApiException if the API call status code is not in the 2xx range
|
77 |
* @throws PrintfulException if the API call has failed or the response is invalid
|
78 |
*/
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
/**
|
83 |
* Perform a PUT request to the API
|
84 |
* @param string $path Request path (e.g. 'orders' or 'orders/123')
|
@@ -88,65 +95,77 @@ class Printful_Client {
|
|
88 |
* @throws PrintfulApiException if the API call status code is not in the 2xx range
|
89 |
* @throws PrintfulException if the API call has failed or the response is invalid
|
90 |
*/
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
|
95 |
/**
|
96 |
* Return raw response data from the last request
|
97 |
* @return string|null Response data
|
98 |
*/
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
/**
|
103 |
* Return decoded response data from the last request
|
104 |
* @return array|null Response data
|
105 |
*/
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
150 |
}
|
151 |
|
152 |
/**
|
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 = 'https://api.printful.com/';
|
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 |
+
if ( defined( 'PF_DEV_API_HOST' ) ) {
|
40 |
+
$this->apiUrl = PF_DEV_API_HOST;
|
41 |
+
}
|
42 |
+
}
|
43 |
|
44 |
/**
|
45 |
* Returns total available item count from the last request if it supports paging (e.g order list) or null otherwise.
|
46 |
*
|
47 |
* @return int|null Item count
|
48 |
*/
|
49 |
+
public function getItemCount() {
|
50 |
+
return isset( $this->lastResponse['paging']['total'] ) ? $this->lastResponse['paging']['total'] : null;
|
51 |
+
}
|
52 |
|
53 |
/**
|
54 |
* Perform a GET request to the API
|
58 |
* @throws PrintfulApiException if the API call status code is not in the 2xx range
|
59 |
* @throws PrintfulException if the API call has failed or the response is invalid
|
60 |
*/
|
61 |
+
public function get( $path, $params = array() ) {
|
62 |
+
return $this->request( 'GET', $path, $params );
|
63 |
+
}
|
64 |
|
65 |
/**
|
66 |
* Perform a DELETE request to the API
|
70 |
* @throws PrintfulApiException if the API call status code is not in the 2xx range
|
71 |
* @throws PrintfulException if the API call has failed or the response is invalid
|
72 |
*/
|
73 |
+
public function delete( $path, $params = array() ) {
|
74 |
+
return $this->request( 'DELETE', $path, $params );
|
75 |
+
}
|
76 |
|
77 |
/**
|
78 |
* Perform a POST request to the API
|
83 |
* @throws PrintfulApiException if the API call status code is not in the 2xx range
|
84 |
* @throws PrintfulException if the API call has failed or the response is invalid
|
85 |
*/
|
86 |
+
public function post( $path, $data = array(), $params = array() ) {
|
87 |
+
return $this->request( 'POST', $path, $params, $data );
|
88 |
+
}
|
89 |
/**
|
90 |
* Perform a PUT request to the API
|
91 |
* @param string $path Request path (e.g. 'orders' or 'orders/123')
|
95 |
* @throws PrintfulApiException if the API call status code is not in the 2xx range
|
96 |
* @throws PrintfulException if the API call has failed or the response is invalid
|
97 |
*/
|
98 |
+
public function put( $path, $data = array(), $params = array() ) {
|
99 |
+
return $this->request( 'PUT', $path, $params, $data );
|
100 |
+
}
|
101 |
|
102 |
/**
|
103 |
* Return raw response data from the last request
|
104 |
* @return string|null Response data
|
105 |
*/
|
106 |
+
public function getLastResponseRaw() {
|
107 |
+
return $this->lastResponseRaw;
|
108 |
+
}
|
109 |
/**
|
110 |
* Return decoded response data from the last request
|
111 |
* @return array|null Response data
|
112 |
*/
|
113 |
+
public function getLastResponse() {
|
114 |
+
return $this->lastResponse;
|
115 |
+
}
|
116 |
+
|
117 |
+
/**
|
118 |
+
* Internal request implementation
|
119 |
+
*
|
120 |
+
* @param $method
|
121 |
+
* @param $path
|
122 |
+
* @param array $params
|
123 |
+
* @param null $data
|
124 |
+
*
|
125 |
+
* @return
|
126 |
+
* @throws PrintfulApiException
|
127 |
+
* @throws PrintfulException
|
128 |
+
*/
|
129 |
+
private function request( $method, $path, array $params = array(), $data = null ) {
|
130 |
+
|
131 |
+
$this->lastResponseRaw = null;
|
132 |
+
$this->lastResponse = null;
|
133 |
+
|
134 |
+
$url = trim( $path, '/' );
|
135 |
+
|
136 |
+
if ( ! empty( $params ) ) {
|
137 |
+
$url .= '?' . http_build_query( $params );
|
138 |
+
}
|
139 |
+
|
140 |
+
$request = array(
|
141 |
+
'timeout' => 10,
|
142 |
+
'user-agent' => $this->userAgent,
|
143 |
+
'method' => $method,
|
144 |
+
'headers' => array( 'Authorization' => 'Basic ' . base64_encode( $this->key ) ),
|
145 |
+
'body' => $data !== null ? json_encode( $data ) : null,
|
146 |
+
);
|
147 |
+
|
148 |
+
$result = wp_remote_get( $this->apiUrl . $url, $request );
|
149 |
+
|
150 |
+
//allow other methods to hook in on the api result
|
151 |
+
$result = apply_filters( 'printful_api_result', $result, $method, $this->apiUrl . $url, $request );
|
152 |
+
|
153 |
+
if ( is_wp_error( $result ) ) {
|
154 |
+
throw new PrintfulException( "API request failed - " . $result->get_error_message() );
|
155 |
+
}
|
156 |
+
$this->lastResponseRaw = $result['body'];
|
157 |
+
$this->lastResponse = $response = json_decode( $result['body'], true );
|
158 |
+
|
159 |
+
if ( ! isset( $response['code'], $response['result'] ) ) {
|
160 |
+
throw new PrintfulException( 'Invalid API response' );
|
161 |
+
}
|
162 |
+
$status = (int) $response['code'];
|
163 |
+
if ( $status < 200 || $status >= 300 ) {
|
164 |
+
throw new PrintfulApiException( (string) $response['result'], $status );
|
165 |
+
}
|
166 |
+
|
167 |
+
return $response['result'];
|
168 |
+
}
|
169 |
}
|
170 |
|
171 |
/**
|
includes/class-printful-integration.php
CHANGED
@@ -1,243 +1,152 @@
|
|
1 |
<?php
|
2 |
if ( ! defined( 'ABSPATH' ) ) exit;
|
3 |
|
4 |
-
class Printful_Integration
|
5 |
{
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
)
|
153 |
-
);
|
154 |
-
}
|
155 |
-
}
|
156 |
-
//Return no taxes
|
157 |
-
return array();
|
158 |
-
}
|
159 |
-
|
160 |
-
/**
|
161 |
-
* Gets list of countries and states where Printful needs to calculate sales tax
|
162 |
-
*/
|
163 |
-
private function get_tax_countries()
|
164 |
-
{
|
165 |
-
$countries = get_transient('printful_tax_countries');
|
166 |
-
if (!$countries)
|
167 |
-
{
|
168 |
-
$countries = array();
|
169 |
-
try {
|
170 |
-
$client = $this->get_client();
|
171 |
-
$list = $client->get('tax/countries');
|
172 |
-
|
173 |
-
foreach($list as $country)
|
174 |
-
{
|
175 |
-
foreach($country['states'] as $state){
|
176 |
-
$countries[$country['code']][$state['code']] = 1;
|
177 |
-
}
|
178 |
-
}
|
179 |
-
if(!empty($countries))
|
180 |
-
{
|
181 |
-
set_transient('printful_tax_countries', $countries, 6 * 3600);
|
182 |
-
}
|
183 |
-
}
|
184 |
-
catch(Exception $e)
|
185 |
-
{
|
186 |
-
//Default to CA if can't get the actual state list
|
187 |
-
return array('US' => array('CA' => 1));
|
188 |
-
}
|
189 |
-
}
|
190 |
-
return $countries;
|
191 |
-
}
|
192 |
-
|
193 |
-
/**
|
194 |
-
* Creates dummy tax rate ID to display Printful tax rates in the cart summary.
|
195 |
-
*/
|
196 |
-
private function get_printful_rate_id($cc, $state, $includeShipping = false)
|
197 |
-
{
|
198 |
-
global $wpdb;
|
199 |
-
|
200 |
-
$includeShipping = (int)$includeShipping;
|
201 |
-
|
202 |
-
$states = WC()->countries->get_states($cc);
|
203 |
-
$tax_title = (isset($states[$state]) ? $states[$state] .' ': '' ). 'Sales Tax';
|
204 |
-
$id = $wpdb->get_var(
|
205 |
-
$wpdb->prepare("SELECT tax_rate_id FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_class='printful'
|
206 |
-
and tax_rate_country = %s AND tax_rate_state = %s AND tax_rate_shipping = %s LIMIT 1",
|
207 |
-
$cc,
|
208 |
-
$state,
|
209 |
-
$includeShipping
|
210 |
-
));
|
211 |
-
if(empty($id))
|
212 |
-
{
|
213 |
-
$wpdb->insert(
|
214 |
-
$wpdb->prefix . "woocommerce_tax_rates",
|
215 |
-
array(
|
216 |
-
'tax_rate_country' => $cc,
|
217 |
-
'tax_rate_state' => $state,
|
218 |
-
'tax_rate' => 0,
|
219 |
-
'tax_rate_name' => $tax_title,
|
220 |
-
'tax_rate_priority' => 1,
|
221 |
-
'tax_rate_compound' => 0,
|
222 |
-
'tax_rate_shipping' => $includeShipping,
|
223 |
-
'tax_rate_class' => 'printful'
|
224 |
-
)
|
225 |
-
);
|
226 |
-
$id = $wpdb->insert_id;
|
227 |
-
}
|
228 |
-
return $id;
|
229 |
-
}
|
230 |
-
|
231 |
-
public function show_tax_warning(){
|
232 |
-
?>
|
233 |
-
<div class="error below-h2">
|
234 |
-
<p>
|
235 |
-
Warning: Tax rates are overriden by Printful Integration plugin. Go to
|
236 |
-
<a href="<?php echo admin_url('admin.php?page=wc-settings&tab=integration§ion=printful') ?>">Printful Integration settings</a>
|
237 |
-
to disable automatic tax calculation if you want to use your own settings.
|
238 |
-
</p>
|
239 |
-
</div>
|
240 |
-
<?php
|
241 |
-
}
|
242 |
-
|
243 |
}
|
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 |
+
*/
|
26 |
+
public function get_client() {
|
27 |
+
|
28 |
+
require_once 'class-printful-client.php';
|
29 |
+
$client = new Printful_Client( $this->get_option( 'printful_key' ), $this->get_option( 'disable_ssl' ) == 'yes' );
|
30 |
+
|
31 |
+
return $client;
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Check if the connection to printful is working
|
36 |
+
* @param bool $force
|
37 |
+
* @return bool
|
38 |
+
*/
|
39 |
+
public function is_connected($force = false) {
|
40 |
+
|
41 |
+
$api_key = $this->get_option( 'printful_key' );
|
42 |
+
|
43 |
+
//dont need to show error - the plugin is simply not setup
|
44 |
+
if ( empty( $api_key )) {
|
45 |
+
return false;
|
46 |
+
}
|
47 |
+
|
48 |
+
//validate length, show error
|
49 |
+
if ( strlen( $api_key ) != 36 ) {
|
50 |
+
$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>.';
|
51 |
+
$settings_url = admin_url( 'admin.php?page=printful-dashboard&tab=settings' );
|
52 |
+
$printful_url = Printful_Base::get_printful_host() . 'dashboard/';
|
53 |
+
$this->set_connect_error(sprintf( $message, $settings_url, $printful_url ) );
|
54 |
+
return false;
|
55 |
+
}
|
56 |
+
|
57 |
+
//show connect status from cache
|
58 |
+
if ( ! $force ) {
|
59 |
+
$connected = get_transient( self::PF_API_CONNECT_STATUS );
|
60 |
+
if ( $connected && $connected['status'] == 1 ) {
|
61 |
+
$this->clear_connect_error();
|
62 |
+
return true;
|
63 |
+
} else if ( $connected && $connected['status'] == 0 ) { //try again in a minute
|
64 |
+
return false;
|
65 |
+
}
|
66 |
+
}
|
67 |
+
|
68 |
+
$client = $this->get_client();
|
69 |
+
$response = false;
|
70 |
+
|
71 |
+
//attempt to connect to printful to verify the API key
|
72 |
+
try {
|
73 |
+
$storeData = $client->get( 'store' );
|
74 |
+
if ( ! empty( $storeData ) ) {
|
75 |
+
$response = true;
|
76 |
+
$this->clear_connect_error();
|
77 |
+
set_transient( self::PF_API_CONNECT_STATUS, array( 'status' => 1 ) ); //no expiry
|
78 |
+
}
|
79 |
+
} catch ( Exception $e ) {
|
80 |
+
|
81 |
+
if ( $e->getCode() == 401 ) {
|
82 |
+
$message = 'Invalid API key. Please ensure that your API key in <a href="%s">Settings</a> matches the one in your <a href="%s">Printful dashboard</a>.';
|
83 |
+
$settings_url = admin_url( 'admin.php?page=printful-dashboard&tab=settings' );
|
84 |
+
$printful_url = Printful_Base::get_printful_host() . 'dashboard/';
|
85 |
+
$this->set_connect_error( sprintf( $message, $settings_url, $printful_url ) );
|
86 |
+
set_transient( self::PF_API_CONNECT_STATUS, array( 'status' => 0 ), MINUTE_IN_SECONDS ); //try again in 1 minute
|
87 |
+
} else {
|
88 |
+
$this->set_connect_error( 'Could not connect to Printful API. Please try again later.' );
|
89 |
+
}
|
90 |
+
|
91 |
+
//do nothing
|
92 |
+
set_transient( self::PF_API_CONNECT_STATUS, array( 'status' => 0 ), MINUTE_IN_SECONDS ); //try again in 1 minute
|
93 |
+
}
|
94 |
+
|
95 |
+
return $response;
|
96 |
+
}
|
97 |
+
|
98 |
+
/**
|
99 |
+
* Update connect error message
|
100 |
+
* @param string $error
|
101 |
+
*/
|
102 |
+
public function set_connect_error($error = '') {
|
103 |
+
update_option(self::PF_CONNECT_ERROR, $error);
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* Get current connect error message
|
108 |
+
*/
|
109 |
+
public function get_connect_error() {
|
110 |
+
return get_option(self::PF_CONNECT_ERROR, false);
|
111 |
+
}
|
112 |
+
|
113 |
+
/**
|
114 |
+
* Remove option used for storing current connect error
|
115 |
+
*/
|
116 |
+
public function clear_connect_error() {
|
117 |
+
delete_option(self::PF_CONNECT_ERROR);
|
118 |
+
}
|
119 |
+
|
120 |
+
/**
|
121 |
+
* AJAX call endpoint for connect status check
|
122 |
+
*/
|
123 |
+
public static function ajax_force_check_connect_status() {
|
124 |
+
if ( Printful_Integration::instance()->is_connected( true ) ) {
|
125 |
+
die( 'OK' );
|
126 |
+
}
|
127 |
+
die( 'FAIL' );
|
128 |
+
}
|
129 |
+
|
130 |
+
/**
|
131 |
+
* Wrapper method for getting an option
|
132 |
+
* @param $name
|
133 |
+
* @param array $default
|
134 |
+
* @return bool
|
135 |
+
*/
|
136 |
+
public function get_option($name, $default = array()) {
|
137 |
+
$options = get_option( 'woocommerce_printful_settings', $default );
|
138 |
+
if ( ! empty( $options[ $name ] ) ) {
|
139 |
+
return $options[ $name ];
|
140 |
+
}
|
141 |
+
return false;
|
142 |
+
}
|
143 |
+
|
144 |
+
/**
|
145 |
+
* Save the setting
|
146 |
+
* @param $settings
|
147 |
+
*/
|
148 |
+
public function update_settings( $settings ) {
|
149 |
+
delete_transient( self::PF_API_CONNECT_STATUS ); //remove the successful API status since API key could have changed
|
150 |
+
update_option( 'woocommerce_printful_settings', $settings );
|
151 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
152 |
}
|
includes/class-printful-request-log.php
ADDED
@@ -0,0 +1,142 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
|
64 |
+
if ( ! is_wp_error( $result ) ) {
|
65 |
+
$result = json_decode( $result['body'], true );
|
66 |
+
$params_set = ! isset( $result['code'], $result['result'] );
|
67 |
+
$status = (int) $result['code'];
|
68 |
+
$code_success = ( $status < 200 || $status >= 300 );
|
69 |
+
}
|
70 |
+
|
71 |
+
//if the request contains error, log it
|
72 |
+
if ( is_wp_error($result) || $params_set || $code_success ) {
|
73 |
+
$this->save_to_wc_log( $request, $result, self::PF_OUTGOING_API_ERRORS );
|
74 |
+
}
|
75 |
+
|
76 |
+
$response_hash = md5( serialize( array( 'request' => $request, 'results' => $result ) ) );
|
77 |
+
|
78 |
+
//save summary in database to be easily accessible for status page
|
79 |
+
$this->save_to_printful_log( $method . ' ' . $url, $result, $response_hash, self::PF_OPTION_OUTGOING_API_REQUEST_LOG);
|
80 |
+
|
81 |
+
return $original_result; //don't change the result
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* Write Printful API request errors to log
|
86 |
+
*
|
87 |
+
* @param $request
|
88 |
+
* @param $result
|
89 |
+
* @param string $context
|
90 |
+
*/
|
91 |
+
private function save_to_wc_log( $request, $result, $context) {
|
92 |
+
|
93 |
+
$logger = wc_get_logger();
|
94 |
+
$context = array( 'source' => $context );
|
95 |
+
$log_item = array(
|
96 |
+
'request' => (array) $request,
|
97 |
+
'results' => (array) $result,
|
98 |
+
);
|
99 |
+
$logger->error( wc_print_r( $log_item, true ), $context );
|
100 |
+
}
|
101 |
+
|
102 |
+
/**
|
103 |
+
* Save 20 lasts requests in easily accessible location
|
104 |
+
*
|
105 |
+
* @param $request_title
|
106 |
+
* @param $result
|
107 |
+
* @param $response_hash
|
108 |
+
* @param $log
|
109 |
+
*/
|
110 |
+
private function save_to_printful_log( $request_title, $result, $response_hash, $log ) {
|
111 |
+
|
112 |
+
$request_log = get_option( $log, array() );
|
113 |
+
if ( count( $request_log ) > 20 ) {
|
114 |
+
$request_log = array_slice( $request_log, 1, 19 ); //if there are more than 20, remove the first entry
|
115 |
+
}
|
116 |
+
|
117 |
+
$is_error = is_wp_error($result) || !empty( $result['errors'] );
|
118 |
+
|
119 |
+
$request_log[] = array(
|
120 |
+
'date' => date( 'Y-m-d H:i:s' ),
|
121 |
+
'request' => $request_title,
|
122 |
+
'result' => ( $is_error ? 'ERROR' : 'OK' ),
|
123 |
+
);
|
124 |
+
|
125 |
+
update_option( $log, $request_log );
|
126 |
+
update_option( self::PF_OPTION_LAST_API_RESPONSE, $response_hash );
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* Check requests header for indications that this is a printful api request
|
131 |
+
* @param $request
|
132 |
+
* @return bool
|
133 |
+
*/
|
134 |
+
private function isPrintfulApiRequest( $request ) {
|
135 |
+
|
136 |
+
if ( ! empty( $request->headers ) && ! empty( $request->headers['USER_AGENT'] ) && $request->headers['USER_AGENT'] == self::PF_USER_AGENT ) {
|
137 |
+
return true;
|
138 |
+
}
|
139 |
+
|
140 |
+
return false;
|
141 |
+
}
|
142 |
+
}
|
includes/class-printful-shipping.php
CHANGED
@@ -11,272 +11,314 @@ class Printful_Shipping extends WC_Shipping_Method
|
|
11 |
//Store whether currently processed package contains Printful products (for WC<2.6)
|
12 |
private $printful_package = true;
|
13 |
|
14 |
-
public function
|
15 |
-
|
16 |
-
$this->id = 'printful_shipping';
|
17 |
-
$this->method_title = $this->title = 'Printful Shipping';
|
18 |
-
$this->method_description = 'Calculate live shipping rates based on actual Printful shipping costs.';
|
19 |
-
|
20 |
-
$this->init_form_fields();
|
21 |
-
$this->init_settings();
|
22 |
-
|
23 |
-
add_action('woocommerce_update_options_shipping_' . $this->id, array(&$this, 'process_admin_options'));
|
24 |
-
|
25 |
-
$this->enabled = $this->get_option('enabled');
|
26 |
-
$this->show_warnings = $this->get_option('show_warnings') == 'yes';
|
27 |
-
$this->override_defaults = $this->get_option('override_defaults') == 'yes';
|
28 |
-
|
29 |
-
//Initialize shipping methods for specific package (or no package)
|
30 |
-
add_filter('woocommerce_load_shipping_methods', array($this, 'woocommerce_load_shipping_methods'), 10000);
|
31 |
-
|
32 |
-
//Remove other shipping methods for Printful package on WC < 2.6
|
33 |
-
add_filter('woocommerce_shipping_methods', array($this, 'woocommerce_shipping_methods'), 10000);
|
34 |
-
|
35 |
-
add_filter('woocommerce_cart_shipping_packages', array($this, 'woocommerce_cart_shipping_packages'), 10000);
|
36 |
-
|
37 |
-
}
|
38 |
-
public function init_form_fields()
|
39 |
-
{
|
40 |
-
$this->form_fields = array(
|
41 |
-
'enabled' => array(
|
42 |
-
'title' => __( 'Enable/Disable', 'woocommerce' ),
|
43 |
-
'type' => 'checkbox',
|
44 |
-
'label' => __( 'Enable this shipping method', 'woocommerce' ),
|
45 |
-
'default' => 'no'
|
46 |
-
),
|
47 |
-
'override_defaults' => array(
|
48 |
-
'title' => 'Disable Woocommerce rates',
|
49 |
-
'type' => 'checkbox',
|
50 |
-
'label' => 'Disable standard Woocommerce rates for products fulfilled by Printful',
|
51 |
-
'default' => 'yes'
|
52 |
-
),
|
53 |
-
'show_warnings' => array(
|
54 |
-
'title' => 'Show Printful warnings',
|
55 |
-
'type' => 'checkbox',
|
56 |
-
'label' => 'Display Printful status messages if rate API request fails',
|
57 |
-
'default' => 'yes'
|
58 |
-
)
|
59 |
-
);
|
60 |
-
|
61 |
-
if (empty(Printful_Integration::instance()->settings['printful_key'])) {
|
62 |
-
$this->form_fields['info'] = array(
|
63 |
-
'type' => 'title',
|
64 |
-
'description' => 'Please add Printful API key to the
|
65 |
-
<a href="'. admin_url('admin.php?page=wc-settings&tab=integration§ion=printful') .'">Printful Integration settings section</a>
|
66 |
-
to enable rate calculation.'
|
67 |
-
);
|
68 |
-
}
|
69 |
-
}
|
70 |
-
|
71 |
-
//Enable only Printful shipping method for Printful packages
|
72 |
-
public function woocommerce_load_shipping_methods( $package = array() ) {
|
73 |
-
$this->printful_package = false;
|
74 |
-
if($package && !empty($package['printful'])) {
|
75 |
-
if($this->enabled == 'yes') {
|
76 |
-
$this->printful_package = true;
|
77 |
-
if($this->override_defaults) {
|
78 |
-
//Remove default methods if we process Printful package
|
79 |
-
WC()->shipping()->unregister_shipping_methods();
|
80 |
-
}
|
81 |
-
WC()->shipping()->register_shipping_method($this);
|
82 |
-
}
|
83 |
-
} else if(!$package) {
|
84 |
-
//Show Printful tab on Shipping rate settings
|
85 |
-
WC()->shipping()->register_shipping_method($this);
|
86 |
-
}
|
87 |
}
|
88 |
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
282 |
}
|
11 |
//Store whether currently processed package contains Printful products (for WC<2.6)
|
12 |
private $printful_package = true;
|
13 |
|
14 |
+
public static function init() {
|
15 |
+
new self;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
}
|
17 |
|
18 |
+
public function __construct() {
|
19 |
+
|
20 |
+
$this->id = 'printful_shipping';
|
21 |
+
$this->method_title = $this->title = 'Printful Shipping';
|
22 |
+
$this->method_description = 'Calculate live shipping rates based on actual Printful shipping costs.';
|
23 |
+
|
24 |
+
$this->init_form_fields();
|
25 |
+
$this->init_settings();
|
26 |
+
|
27 |
+
add_action( 'woocommerce_update_options_shipping_' . $this->id, array( &$this, 'process_admin_options' ) );
|
28 |
+
|
29 |
+
$this->enabled = $this->get_option( 'enabled' );
|
30 |
+
$this->show_warnings = $this->get_option( 'show_warnings' ) == 'yes';
|
31 |
+
$this->override_defaults = $this->get_option( 'override_defaults' ) == 'yes';
|
32 |
+
|
33 |
+
//Initialize shipping methods for specific package (or no package)
|
34 |
+
add_filter( 'woocommerce_load_shipping_methods', array( $this, 'woocommerce_load_shipping_methods' ), 10000 );
|
35 |
+
|
36 |
+
//Remove other shipping methods for Printful package on WC < 2.6
|
37 |
+
add_filter( 'woocommerce_shipping_methods', array( $this, 'woocommerce_shipping_methods' ), 10000 );
|
38 |
+
|
39 |
+
add_filter( 'woocommerce_cart_shipping_packages', array( $this, 'woocommerce_cart_shipping_packages' ), 10000 );
|
40 |
+
}
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Init fields for Printful Shipping form
|
44 |
+
*/
|
45 |
+
public function init_form_fields() {
|
46 |
+
|
47 |
+
$this->form_fields = array(
|
48 |
+
'enabled' => array(
|
49 |
+
'title' => __( 'Enable/Disable', 'woocommerce' ),
|
50 |
+
'type' => 'checkbox',
|
51 |
+
'label' => __( 'Enable this shipping method', 'woocommerce' ),
|
52 |
+
'default' => 'no',
|
53 |
+
),
|
54 |
+
'override_defaults' => array(
|
55 |
+
'title' => 'Disable Woocommerce rates',
|
56 |
+
'type' => 'checkbox',
|
57 |
+
'label' => 'Disable standard Woocommerce rates for products fulfilled by Printful',
|
58 |
+
'default' => 'yes',
|
59 |
+
),
|
60 |
+
'show_warnings' => array(
|
61 |
+
'title' => 'Show Printful warnings',
|
62 |
+
'type' => 'checkbox',
|
63 |
+
'label' => 'Display Printful status messages if rate API request fails',
|
64 |
+
'default' => 'yes',
|
65 |
+
),
|
66 |
+
);
|
67 |
+
|
68 |
+
if ( !Printful_Integration::instance()->get_option('printful_key') ) {
|
69 |
+
$this->form_fields['info'] = array(
|
70 |
+
'type' => 'title',
|
71 |
+
'description' => 'Please add Printful API key to the
|
72 |
+
<a href="' . admin_url( 'admin.php?page=printful-dashboard&tab=settings' ) . '">Printful Integration settings section</a>
|
73 |
+
to enable rate calculation.',
|
74 |
+
);
|
75 |
+
}
|
76 |
+
}
|
77 |
+
|
78 |
+
/**
|
79 |
+
* Enable only Printful shipping method for Printful packages
|
80 |
+
* @param array $package
|
81 |
+
*/
|
82 |
+
public function woocommerce_load_shipping_methods( $package = array() ) {
|
83 |
+
|
84 |
+
$this->printful_package = false;
|
85 |
+
if ( $package && ! empty( $package['printful'] ) ) {
|
86 |
+
if ( $this->enabled == 'yes' ) {
|
87 |
+
$this->printful_package = true;
|
88 |
+
if ( $this->override_defaults ) {
|
89 |
+
//Remove default methods if we process Printful package
|
90 |
+
WC()->shipping()->unregister_shipping_methods();
|
91 |
+
}
|
92 |
+
WC()->shipping()->register_shipping_method( $this );
|
93 |
+
}
|
94 |
+
} else if ( ! $package ) {
|
95 |
+
//Show Printful tab on Shipping rate settings
|
96 |
+
WC()->shipping()->register_shipping_method( $this );
|
97 |
+
}
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Remove non-Printful methods for Printful packages on WC < 2.6
|
102 |
+
* @param $methods
|
103 |
+
*
|
104 |
+
* @return array
|
105 |
+
*/
|
106 |
+
public function woocommerce_shipping_methods( $methods ) {
|
107 |
+
|
108 |
+
if ( $this->override_defaults && $this->printful_package && version_compare( WC()->version, '2.6', '<' ) ) {
|
109 |
+
//For WC < 2.6 woocommerce_shipping_methods is executed after woocommerce_load_shipping_methods
|
110 |
+
//So we need to clean up unnecessary methods from there
|
111 |
+
return array();
|
112 |
+
}
|
113 |
+
|
114 |
+
return $methods;
|
115 |
+
}
|
116 |
+
|
117 |
+
/**
|
118 |
+
* Split Printul products to a separate package if there are any
|
119 |
+
* @param array $packages
|
120 |
+
*
|
121 |
+
* @return array
|
122 |
+
*/
|
123 |
+
public function woocommerce_cart_shipping_packages( $packages = array() ) {
|
124 |
+
|
125 |
+
//Printful rates are turned off, do not split products
|
126 |
+
if ( $this->enabled !== 'yes' ) {
|
127 |
+
return $packages;
|
128 |
+
}
|
129 |
+
|
130 |
+
$return_packages = array();
|
131 |
+
|
132 |
+
foreach ( $packages as $package ) {
|
133 |
+
|
134 |
+
$ids = array();
|
135 |
+
foreach ( $package['contents'] as $key => $item ) {
|
136 |
+
$ids[ $key ] = $item['variation_id'] ? $item['variation_id'] : $item['product_id'];
|
137 |
+
}
|
138 |
+
|
139 |
+
$printful_ids = array();
|
140 |
+
if ( $ids ) {
|
141 |
+
asort( $ids );
|
142 |
+
$values = implode( ',', array_unique( $ids ) );
|
143 |
+
$key = 'printful_productids_' . md5( $values );
|
144 |
+
$printful_ids = get_transient( $key );
|
145 |
+
if ( ! is_array( $printful_ids ) ) {
|
146 |
+
$printful_ids = array();
|
147 |
+
try {
|
148 |
+
$client = Printful_Integration::instance()->get_client();
|
149 |
+
$status = $client->get( 'sync/variants', array(
|
150 |
+
'external_ids' => $values,
|
151 |
+
) );
|
152 |
+
if ( ! empty( $status['sync_variants'] ) ) {
|
153 |
+
foreach ( $status['sync_variants'] as $variant ) {
|
154 |
+
if ( $variant['synced'] ) {
|
155 |
+
$printful_ids[] = $variant['external_id'];
|
156 |
+
}
|
157 |
+
}
|
158 |
+
}
|
159 |
+
set_transient( $key, $printful_ids, 1800 );
|
160 |
+
} catch ( PrintfulException $e ) {
|
161 |
+
$this->set_error( $e );
|
162 |
+
|
163 |
+
//Failed to get Printful status, return default packages
|
164 |
+
return $packages;
|
165 |
+
}
|
166 |
+
}
|
167 |
+
}
|
168 |
+
$new_contents = array(
|
169 |
+
'printful' => array(),
|
170 |
+
'virtual' => array(),
|
171 |
+
'woocommerce' => array(),
|
172 |
+
);
|
173 |
+
|
174 |
+
foreach ( $ids as $key => $external_id ) {
|
175 |
+
$item = $package['contents'][ $key ];
|
176 |
+
if ( in_array( $external_id, $printful_ids ) ) {
|
177 |
+
$new_contents['printful'][ $key ] = $item;
|
178 |
+
} else if ( $item['data']->is_virtual() || $item['data']->is_downloadable() ) {
|
179 |
+
$new_contents['virtual'][ $key ] = $item;
|
180 |
+
} else {
|
181 |
+
$new_contents['woocommerce'][ $key ] = $item;
|
182 |
+
}
|
183 |
+
}
|
184 |
+
|
185 |
+
//Put virtual products together with any other package
|
186 |
+
if ( $new_contents['virtual'] ) {
|
187 |
+
if ( $new_contents['printful'] && ! $new_contents['woocommerce'] ) {
|
188 |
+
$new_contents['printful'] += $new_contents['virtual'];
|
189 |
+
} else {
|
190 |
+
$new_contents['woocommerce'] += $new_contents['virtual'];
|
191 |
+
}
|
192 |
+
unset ( $new_contents['virtual'] );
|
193 |
+
}
|
194 |
+
|
195 |
+
foreach ( $new_contents as $key => $contents ) {
|
196 |
+
if ( $contents ) {
|
197 |
+
$new_package = $package;
|
198 |
+
$new_package['contents_cost'] = 0;
|
199 |
+
$new_package['contents'] = $contents;
|
200 |
+
foreach ( $contents as $item ) {
|
201 |
+
if ( $item['data']->needs_shipping() ) {
|
202 |
+
if ( isset( $item['line_total'] ) ) {
|
203 |
+
$new_package['contents_cost'] += $item['line_total'];
|
204 |
+
}
|
205 |
+
}
|
206 |
+
}
|
207 |
+
if ( $key == 'printful' ) {
|
208 |
+
$new_package['printful'] = true;
|
209 |
+
}
|
210 |
+
$return_packages[] = $new_package;
|
211 |
+
}
|
212 |
+
}
|
213 |
+
}
|
214 |
+
|
215 |
+
return $return_packages;
|
216 |
+
}
|
217 |
+
|
218 |
+
/**
|
219 |
+
* @param array $package
|
220 |
+
*
|
221 |
+
* @return bool
|
222 |
+
*/
|
223 |
+
public function calculate_shipping( $package = array() ) {
|
224 |
+
|
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 |
+
);
|
237 |
+
|
238 |
+
|
239 |
+
if ( $request['recipient']['country_code'] == 'US' &&
|
240 |
+
( empty( $request['recipient']['zip'] ) ||
|
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 |
+
) );
|
276 |
+
//Cache locally, since WC < 2.6 had problems with caching rates form multiple packages internally
|
277 |
+
set_transient( $key, $response, 1800 );
|
278 |
+
}
|
279 |
+
|
280 |
+
foreach ( $response as $rate ) {
|
281 |
+
$rateData = array(
|
282 |
+
'id' => $this->id . '_' . $rate['id'],
|
283 |
+
'label' => $rate['name'],
|
284 |
+
'cost' => $rate['rate'],
|
285 |
+
'calc_tax' => 'per_order',
|
286 |
+
);
|
287 |
+
$this->add_rate( $rateData );
|
288 |
+
}
|
289 |
+
} catch ( PrintfulException $e ) {
|
290 |
+
$this->set_error( $e );
|
291 |
+
return false;
|
292 |
+
}
|
293 |
+
|
294 |
+
return false;
|
295 |
+
}
|
296 |
+
|
297 |
+
/**
|
298 |
+
* @param $error
|
299 |
+
*/
|
300 |
+
private function set_error( $error ) {
|
301 |
+
|
302 |
+
if ( $this->show_warnings ) {
|
303 |
+
$this->last_error = $error;
|
304 |
+
add_filter( 'woocommerce_cart_no_shipping_available_html', array( $this, 'show_error' ) );
|
305 |
+
add_filter( 'woocommerce_no_shipping_available_html', array( $this, 'show_error' ) );
|
306 |
+
}
|
307 |
+
}
|
308 |
+
|
309 |
+
/**
|
310 |
+
* @param $data
|
311 |
+
*
|
312 |
+
* @return string
|
313 |
+
*/
|
314 |
+
public function show_error( $data ) {
|
315 |
+
|
316 |
+
$error = $this->last_error;
|
317 |
+
$message = $error->getMessage();
|
318 |
+
if ( $error instanceof PrintfulApiException && $error->getCode() == 401 ) {
|
319 |
+
$message = 'Invalid API key';
|
320 |
+
}
|
321 |
+
|
322 |
+
return '<p>ERROR: ' . htmlspecialchars( $message ) . '</p>';
|
323 |
+
}
|
324 |
}
|
includes/class-printful-size-chart-tab.php
ADDED
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
if ( ! defined( 'ABSPATH' ) ) exit;
|
3 |
+
|
4 |
+
class Printful_Size_Chart_Tab {
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Is meta boxes saved once?
|
8 |
+
*
|
9 |
+
* @var boolean
|
10 |
+
*/
|
11 |
+
private static $saved_meta_boxes = false;
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Printful_Size_Chart_Tab constructor.
|
15 |
+
*/
|
16 |
+
public static function init() {
|
17 |
+
$size_chart = new self;
|
18 |
+
add_filter( 'woocommerce_product_tabs', array( $size_chart, 'init_size_chart_tab' ) );
|
19 |
+
add_action( 'add_meta_boxes', array($size_chart, 'init_metabox'));
|
20 |
+
add_action( 'save_post', array( $size_chart, 'save_size_chart' ), 1, 2 );
|
21 |
+
}
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Initialize meta boxes
|
25 |
+
*/
|
26 |
+
public function init_metabox() {
|
27 |
+
add_meta_box( 'pf_size_chart', __( 'Size chart', 'printful' ), array($this, 'size_chart_metabox'), 'product', 'normal' );
|
28 |
+
}
|
29 |
+
|
30 |
+
/**
|
31 |
+
* @param $tabs
|
32 |
+
*
|
33 |
+
* @return mixed
|
34 |
+
*/
|
35 |
+
public function init_size_chart_tab( $tabs ) {
|
36 |
+
|
37 |
+
if ( strlen( $this->get_size_chart_content() ) > 0 ) {
|
38 |
+
$tabs['size_chart'] = array(
|
39 |
+
'title' => __( 'Size Chart', 'printful' ),
|
40 |
+
'priority' => 50,
|
41 |
+
'callback' => array( $this, 'size_chart_tab_content' ),
|
42 |
+
);
|
43 |
+
}
|
44 |
+
|
45 |
+
return $tabs;
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Display the size chart content
|
50 |
+
*/
|
51 |
+
public function size_chart_tab_content() {
|
52 |
+
|
53 |
+
echo '<h2>' . __( 'Size Chart', 'printful' ) . '</h2>';
|
54 |
+
echo $this->get_size_chart_content();
|
55 |
+
}
|
56 |
+
|
57 |
+
/**
|
58 |
+
* @return mixed
|
59 |
+
*/
|
60 |
+
public function get_size_chart_content() {
|
61 |
+
|
62 |
+
global $post;
|
63 |
+
return htmlspecialchars_decode(get_post_meta( $post->ID, 'pf_size_chart', true ));
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* @param $meta_id
|
68 |
+
*/
|
69 |
+
public function size_chart_metabox( $meta_id ) {
|
70 |
+
|
71 |
+
$settings = array(
|
72 |
+
'textarea_name' => 'pf_size_chart',
|
73 |
+
'tinymce' => array(
|
74 |
+
'theme_advanced_buttons1' => 'bold,italic,strikethrough,separator,bullist,numlist,separator,blockquote,separator,justifyleft,justifycenter,justifyright,separator,link,unlink,separator,undo,redo,separator',
|
75 |
+
'theme_advanced_buttons2' => '',
|
76 |
+
),
|
77 |
+
'editor_css' => '<style>#wp-pf_size_chart-editor-container .wp-editor-area{height:175px; width:100%;} .wp-editor-area{height:175px; width:100%;}</style>',
|
78 |
+
);
|
79 |
+
|
80 |
+
$content = get_post_meta( $meta_id->ID, 'pf_size_chart', true );
|
81 |
+
|
82 |
+
wp_editor( htmlspecialchars_decode( $content ), 'pf_size_chart_editor', apply_filters( 'woocommerce_product_short_description_editor_settings', $settings ) );
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* @param $post_id
|
87 |
+
* @param $post
|
88 |
+
*/
|
89 |
+
public function save_size_chart($post_id, $post ) {
|
90 |
+
|
91 |
+
// $post_id and $post are required
|
92 |
+
if ( empty( $post_id ) || empty( $post ) || self::$saved_meta_boxes ) {
|
93 |
+
return;
|
94 |
+
}
|
95 |
+
|
96 |
+
// Dont' save meta boxes for revisions or autosaves
|
97 |
+
if ( defined( 'DOING_AUTOSAVE' ) || is_int( wp_is_post_revision( $post ) ) || is_int( wp_is_post_autosave( $post ) ) ) {
|
98 |
+
return;
|
99 |
+
}
|
100 |
+
|
101 |
+
// Check the nonce
|
102 |
+
if ( empty( $_POST['woocommerce_meta_nonce'] ) || ! wp_verify_nonce( $_POST['woocommerce_meta_nonce'], 'woocommerce_save_data' ) ) {
|
103 |
+
return;
|
104 |
+
}
|
105 |
+
|
106 |
+
// Check the post being saved == the $post_id to prevent triggering this call for other save_post events
|
107 |
+
if ( empty( $_POST['post_ID'] ) || $_POST['post_ID'] != $post_id ) {
|
108 |
+
return;
|
109 |
+
}
|
110 |
+
|
111 |
+
// Check user has permission to edit
|
112 |
+
if ( ! current_user_can( 'edit_post', $post_id ) ) {
|
113 |
+
return;
|
114 |
+
}
|
115 |
+
|
116 |
+
// Check the post type
|
117 |
+
if ( $post->post_type != 'product' ) {
|
118 |
+
return;
|
119 |
+
}
|
120 |
+
|
121 |
+
//check if not empty
|
122 |
+
if ( empty( $_POST['pf_size_chart'] ) ) {
|
123 |
+
return;
|
124 |
+
}
|
125 |
+
|
126 |
+
// We need this save event to run once to avoid potential endless loops.
|
127 |
+
self::$saved_meta_boxes = true;
|
128 |
+
|
129 |
+
//save
|
130 |
+
update_post_meta($post_id, 'pf_size_chart', htmlspecialchars($_POST['pf_size_chart']));
|
131 |
+
}
|
132 |
+
}
|
includes/class-printful-taxes.php
ADDED
@@ -0,0 +1,188 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
|
11 |
+
if ( Printful_Integration::instance()->get_option( 'calculate_tax' ) == 'yes' ) {
|
12 |
+
//Update tax options if taxes are enabled
|
13 |
+
if ( get_option( 'woocommerce_calc_taxes' ) != 'yes' ) {
|
14 |
+
update_option( 'woocommerce_calc_taxes', 'yes' );
|
15 |
+
}
|
16 |
+
if ( get_option( 'woocommerce_tax_based_on' ) != 'shipping' ) {
|
17 |
+
update_option( 'woocommerce_tax_based_on', 'shipping' );
|
18 |
+
}
|
19 |
+
|
20 |
+
//Override tax rates calculated by Woocommerce
|
21 |
+
$taxes = new self;
|
22 |
+
add_filter( 'woocommerce_matched_tax_rates', array($taxes, 'calculate_tax' ), 10, 6 );
|
23 |
+
}
|
24 |
+
}
|
25 |
+
|
26 |
+
/**
|
27 |
+
* @param $matched_tax_rates
|
28 |
+
* @param $country
|
29 |
+
* @param $state
|
30 |
+
* @param $postcode
|
31 |
+
* @param $city
|
32 |
+
* @param $tax_class
|
33 |
+
*
|
34 |
+
* @return mixed
|
35 |
+
*/
|
36 |
+
public function calculate_tax( $matched_tax_rates, $country, $state, $postcode, $city, $tax_class ) {
|
37 |
+
|
38 |
+
//if a tax rate is already matched, avoid adding extra one
|
39 |
+
if ( ! empty( $matched_tax_rates ) ) {
|
40 |
+
return $matched_tax_rates;
|
41 |
+
}
|
42 |
+
|
43 |
+
$countries = $this->get_tax_countries();
|
44 |
+
if ( isset( $countries[ $country ][ $state ] ) && !empty($postcode) ) { //only make the request if country, state and zip are set
|
45 |
+
$key = 'printful_tax_rate_' . $country . '-' . $state . '-' . $city . '-' . $postcode;
|
46 |
+
$rate = get_transient( $key );
|
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 |
+
|
101 |
+
if ( empty( $matched_tax_rates ) ) {
|
102 |
+
return true;
|
103 |
+
}
|
104 |
+
|
105 |
+
foreach ( $matched_tax_rates as $mr ) {
|
106 |
+
if ( floatval( $mr['rate'] ) == floatval( $rate['rate'] ) && $mr['shipping'] == $rate['shipping'] ) {
|
107 |
+
return false;
|
108 |
+
}
|
109 |
+
}
|
110 |
+
|
111 |
+
return true;
|
112 |
+
}
|
113 |
+
|
114 |
+
/**
|
115 |
+
* Gets list of countries and states where Printful needs to calculate sales tax
|
116 |
+
* @return array|mixed
|
117 |
+
*/
|
118 |
+
private function get_tax_countries() {
|
119 |
+
|
120 |
+
$countries = get_transient( 'printful_tax_countries' );
|
121 |
+
if ( ! $countries ) {
|
122 |
+
$countries = array();
|
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 |
+
if ( ! empty( $countries ) ) {
|
133 |
+
set_transient( 'printful_tax_countries', $countries, 6 * 3600 );
|
134 |
+
}
|
135 |
+
} catch ( Exception $e ) {
|
136 |
+
//Default to CA if can't get the actual state list
|
137 |
+
return array( 'US' => array( 'CA' => 1 ) );
|
138 |
+
}
|
139 |
+
}
|
140 |
+
|
141 |
+
return $countries;
|
142 |
+
}
|
143 |
+
|
144 |
+
/**
|
145 |
+
* Creates dummy tax rate ID to display Printful tax rates in the cart summary.
|
146 |
+
*
|
147 |
+
* @param $cc
|
148 |
+
* @param $state
|
149 |
+
* @param bool $includeShipping
|
150 |
+
*
|
151 |
+
* @return int|null|string
|
152 |
+
*/
|
153 |
+
private function get_printful_rate_id( $cc, $state, $includeShipping = false ) {
|
154 |
+
global $wpdb;
|
155 |
+
|
156 |
+
$includeShipping = (int) $includeShipping;
|
157 |
+
|
158 |
+
$states = WC()->countries->get_states( $cc );
|
159 |
+
$tax_title = ( isset( $states[ $state ] ) ? $states[ $state ] . ' ' : '' ) . 'Sales Tax';
|
160 |
+
$id = $wpdb->get_var(
|
161 |
+
$wpdb->prepare( "SELECT tax_rate_id FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_class='printful'
|
162 |
+
and tax_rate_country = %s AND tax_rate_state = %s AND tax_rate_shipping = %s LIMIT 1",
|
163 |
+
$cc,
|
164 |
+
$state,
|
165 |
+
$includeShipping
|
166 |
+
) );
|
167 |
+
if ( empty( $id ) ) {
|
168 |
+
$wpdb->insert(
|
169 |
+
$wpdb->prefix . "woocommerce_tax_rates",
|
170 |
+
array(
|
171 |
+
'tax_rate_country' => $cc,
|
172 |
+
'tax_rate_state' => $state,
|
173 |
+
'tax_rate' => 0,
|
174 |
+
'tax_rate_name' => $tax_title,
|
175 |
+
'tax_rate_priority' => 1,
|
176 |
+
'tax_rate_compound' => 0,
|
177 |
+
'tax_rate_shipping' => $includeShipping,
|
178 |
+
'tax_rate_class' => 'printful',
|
179 |
+
)
|
180 |
+
);
|
181 |
+
$id = $wpdb->insert_id;
|
182 |
+
}
|
183 |
+
|
184 |
+
return $id;
|
185 |
+
}
|
186 |
+
|
187 |
+
|
188 |
+
}
|
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>
|
includes/templates/connect.php
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<div class="printful-connect">
|
2 |
+
|
3 |
+
<div class="printful-connect-inner">
|
4 |
+
|
5 |
+
<h1>Connect to 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>To connect your store to Printful, fix the following errors:</p>
|
13 |
+
<div class="printful-notice">
|
14 |
+
<ul>
|
15 |
+
<?php
|
16 |
+
foreach ( $issues as $issue ) {
|
17 |
+
echo '<li>' . $issue . '</li>';
|
18 |
+
}
|
19 |
+
?>
|
20 |
+
</ul>
|
21 |
+
</div>
|
22 |
+
<?php
|
23 |
+
$url = '#';
|
24 |
+
} else {
|
25 |
+
?><p class="connect-description">You're almost done! Just 2 more steps to have your WooCommerce store connected to Printful for automatic order fulfillment.</p><?php
|
26 |
+
$url = Printful_Base::get_printful_host() . 'dashboard/woocommerce/plugin-connect?website=' . urlencode( trailingslashit( get_site_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">Connect</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 admin_url( 'admin-ajax.php?action=ajax_force_check_connect_status' ); ?>');
|
37 |
+
});
|
38 |
+
</script>
|
39 |
+
</div>
|
40 |
+
</div>
|
includes/templates/error.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<p class="printful-error"><b>Error:</b> <?php echo wp_kses_post($error); ?></p>
|
includes/templates/footer.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
</div>
|
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>
|
includes/templates/inline-script.php
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
<script type="text/javascript">
|
2 |
+
<?php echo $script; ?>
|
3 |
+
</script>
|
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">Order</th>
|
10 |
+
<th class="col-date">Date</th>
|
11 |
+
<th class="col-from">From</th>
|
12 |
+
<th class="col-status">Status</th>
|
13 |
+
<th class="col-total">Total</th>
|
14 |
+
<th class="col-actions">Actions</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="' . 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">Open in Printful</a>
|
47 |
+
</td>
|
48 |
+
</tr>
|
49 |
+
|
50 |
+
<?php endforeach; ?>
|
51 |
+
|
52 |
+
</tbody>
|
53 |
+
<tfoot>
|
54 |
+
<tr>
|
55 |
+
<th class="col-order">Order</th>
|
56 |
+
<th class="col-date">Date</th>
|
57 |
+
<th class="col-from">From</th>
|
58 |
+
<th class="col-status">Status</th>
|
59 |
+
<th class="col-total">Total</th>
|
60 |
+
<th class="col-actions">Actions</th>
|
61 |
+
</tr>
|
62 |
+
</tfoot>
|
63 |
+
</table>
|
64 |
+
|
65 |
+
<?php else: ?>
|
66 |
+
<div class="printful-latest-orders">
|
67 |
+
<p>Once your store gets some Printful product orders, they will be shown here!</p>
|
68 |
+
</div>
|
69 |
+
<?php endif; ?>
|
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" target="_blank" class="printful-quick-links-item">
|
5 |
+
<span class="dashicons dashicons-cart"></span>
|
6 |
+
<h4>Orders</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>File library</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>Stores</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>Reports</h4>
|
19 |
+
</a>
|
20 |
+
<a href="<?php echo esc_url(Printful_Base::get_printful_host()); ?>dashboard/customer" target="_blank" class="printful-quick-links-item">
|
21 |
+
<span class="dashicons dashicons-admin-users"></span>
|
22 |
+
<h4>My Account</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>Billing</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>Notifications</h4>
|
31 |
+
</a>
|
32 |
+
</div>
|
includes/templates/setting-group.php
ADDED
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<div class="printful-setting-group">
|
2 |
+
|
3 |
+
<h2><?php echo esc_html($title); ?></h2>
|
4 |
+
<p><?php echo esc_html($description); ?></p>
|
5 |
+
|
6 |
+
<?php if ( !empty( $settings ) ) : ?>
|
7 |
+
|
8 |
+
<table class="form-table">
|
9 |
+
<tbody>
|
10 |
+
<?php foreach($settings as $key => $setting) : ?>
|
11 |
+
|
12 |
+
<?php
|
13 |
+
if($setting['type'] == 'title') {
|
14 |
+
continue;
|
15 |
+
}
|
16 |
+
?>
|
17 |
+
|
18 |
+
<tr valign="top">
|
19 |
+
|
20 |
+
<th scope="row" class="titledesc">
|
21 |
+
<span class="woocommerce-help-tip"></span>
|
22 |
+
<label for="<?php echo esc_attr($key); ?>"><?php echo esc_html($setting['title']); ?></label>
|
23 |
+
</th>
|
24 |
+
|
25 |
+
<td class="forminp">
|
26 |
+
<fieldset>
|
27 |
+
<legend class="screen-reader-text"><span><?php echo esc_html($setting['title']); ?></span></legend>
|
28 |
+
|
29 |
+
<?php if ( $setting['type'] == 'text' ) : ?>
|
30 |
+
|
31 |
+
<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']); ?>" placeholder="">
|
32 |
+
|
33 |
+
<?php elseif ($setting['type'] == 'checkbox') : ?>
|
34 |
+
|
35 |
+
<label for="<?php echo esc_attr($key); ?>">
|
36 |
+
<input class="" type="checkbox" name="<?php echo esc_attr($key); ?>" id="<?php echo esc_attr($key); ?>" value="<?php echo esc_attr($setting['value']); ?>" <?php if($setting['value'] == 'yes') { echo 'checked="checked"'; } ?>><?php echo esc_html($setting['label']); ?></label><br>
|
37 |
+
|
38 |
+
<?php elseif ($setting['type'] == 'checkbox-group') : ?>
|
39 |
+
|
40 |
+
<?php foreach ( $setting['items'] as $checkbox_key => $item ) : ?>
|
41 |
+
|
42 |
+
<label for="<?php echo esc_attr($checkbox_key); ?>" class="checkbox-group-item">
|
43 |
+
<input class="" type="checkbox" name="<?php echo esc_attr($key) . '[]'; ?>" id="<?php echo esc_attr($checkbox_key); ?>" value="<?php echo esc_attr($checkbox_key); ?>" <?php if ( $item['value'] == 'yes' ) {echo 'checked="checked"';} ?>><?php echo wp_kses_post( $item['label'] ); ?></label><br>
|
44 |
+
|
45 |
+
<?php endforeach; ?>
|
46 |
+
|
47 |
+
<?php endif; ?>
|
48 |
+
|
49 |
+
</fieldset>
|
50 |
+
</td>
|
51 |
+
</tr>
|
52 |
+
|
53 |
+
<?php endforeach; ?>
|
54 |
+
</tbody>
|
55 |
+
</table>
|
56 |
+
|
57 |
+
<?php endif; ?>
|
58 |
+
|
59 |
+
</div>
|
includes/templates/setting-submit.php
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<p class="printful-submit">
|
2 |
+
<input name="save" class="button-primary woocommerce-save-button <?php if($disabled) { echo 'disabled'; } ?>" type="submit" value="Save changes" <?php if($disabled) { echo 'disabled'; } ?>/>
|
3 |
+
<input type="hidden" id="_wpnonce" name="_wpnonce" value="<?php echo esc_attr($nonce); ?>">
|
4 |
+
<?php wp_referer_field(true); ?>
|
5 |
+
<span class="loader-wrap">
|
6 |
+
<img src="<?php echo esc_url( admin_url( 'images/spinner-2x.gif' ) ) ?>" class="loader" width="20px" height="20px" alt="loader"/>
|
7 |
+
<span class="pass">
|
8 |
+
<span class="dashicons dashicons-yes"></span>
|
9 |
+
Saved successfully
|
10 |
+
</span>
|
11 |
+
<span class="fail">
|
12 |
+
<span class="dashicons dashicons-no"></span>
|
13 |
+
Saving settings failed
|
14 |
+
</span>
|
15 |
+
</span>
|
16 |
+
</p>
|
17 |
+
<script type="text/javascript">
|
18 |
+
jQuery(document).ready(function () {
|
19 |
+
Printful_Settings.init_submit();
|
20 |
+
});
|
21 |
+
</script>
|
includes/templates/shipping-notification.php
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
1 |
+
<div class="printful-setting-group">
|
2 |
+
<h2>Printful Shipping</h2>
|
3 |
+
<p>To enable/disable Printful shipping for your store go to <a href="<?php echo admin_url('admin.php?page=wc-settings&tab=shipping§ion=printful_shipping'); ?>">WooCommerce Shipping settings</a>.</p>
|
4 |
+
</div>
|
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($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']);
|
8 |
+
?>
|
9 |
+
</b>
|
10 |
+
today
|
11 |
+
</div>
|
12 |
+
<div class="printful-stats-item">
|
13 |
+
<h4>
|
14 |
+
$<?php echo 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']);
|
21 |
+
?>
|
22 |
+
</b>
|
23 |
+
last 7 days
|
24 |
+
</div>
|
25 |
+
<div class="printful-stats-item">
|
26 |
+
<h4>
|
27 |
+
$<?php echo 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']);
|
34 |
+
?>
|
35 |
+
</b> last 28 days
|
36 |
+
</div>
|
37 |
+
<div class="printful-stats-item">
|
38 |
+
<h4>
|
39 |
+
$<?php echo 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>PROFIT</b> last 28 days
|
43 |
+
</div>
|
44 |
+
</div>
|
includes/templates/status-report.php
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<div class="support-report-wrap">
|
2 |
+
<p>Copy the box content below and add it to your support message</p>
|
3 |
+
<textarea class="support-report"><?php echo esc_html($status_report); ?></textarea>
|
4 |
+
<button class="button button-primary button-large support-report-btn">Copy</button>
|
5 |
+
<script type="text/javascript">
|
6 |
+
var copyTextareaBtn = document.querySelector('.support-report-btn');
|
7 |
+
|
8 |
+
copyTextareaBtn.addEventListener('click', function() {
|
9 |
+
var copyTextarea = document.querySelector('.support-report');
|
10 |
+
copyTextarea.select();
|
11 |
+
|
12 |
+
try {
|
13 |
+
document.execCommand('copy');
|
14 |
+
} catch (err) {
|
15 |
+
//do nothing
|
16 |
+
}
|
17 |
+
});
|
18 |
+
</script>
|
19 |
+
</div>
|
includes/templates/status-table.php
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( $checklist['overall_status'] ) {
|
2 |
+
?>
|
3 |
+
<div class="notice notice-success">
|
4 |
+
<p>Looks like the everything is set up correctly and Printful integration should work as intended.</p>
|
5 |
+
</div>
|
6 |
+
<?php
|
7 |
+
} else {
|
8 |
+
?>
|
9 |
+
<div class="notice notice-error">
|
10 |
+
<p>There are errors with your store setup that may cause the Printful integration to not work as intended!</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">Name</td>
|
20 |
+
<td class="col-desc">Description</td>
|
21 |
+
<td class="col-status">Status</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">OK</span>';
|
35 |
+
} else if ( $item['status'] == 0 ) {
|
36 |
+
echo '<span class="warning">WARNING*</span>';
|
37 |
+
} else {
|
38 |
+
echo '<span class="fail">FAIL</span>';
|
39 |
+
}
|
40 |
+
?>
|
41 |
+
</td>
|
42 |
+
</tr>
|
43 |
+
<?php endforeach; ?>
|
44 |
+
</tbody>
|
45 |
+
<tfoot>
|
46 |
+
<tr>
|
47 |
+
<td class="col-name">Name</td>
|
48 |
+
<td class="col-desc">Description</td>
|
49 |
+
<td class="col-status">Status</td>
|
50 |
+
</tr>
|
51 |
+
</tfoot>
|
52 |
+
</table>
|
53 |
+
|
54 |
+
<p class="asterisk">* Warnings are issued when the test was unable to come to a definite conclusion or if the result was passable, but not ideal.</p>
|
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>Need help? Get in touch!</h3>
|
5 |
+
<p>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.<br/>We'd love to hear from you.</p>
|
6 |
+
<div class="btn-wrap">
|
7 |
+
<a href="<?php echo esc_url(Printful_Base::get_printful_host()); ?>support" class="button button-primary button-large" target="_blank">Contact support</a>
|
8 |
+
</div>
|
9 |
+
</div>
|
10 |
+
|
11 |
+
<div class="support-info-block">
|
12 |
+
<h3>Read our FAQs</h3>
|
13 |
+
<p>Getting started made easy – read the FAQs to jumpstart your business.<br/>Whether you're a video tutorial fan or prefer the written answers – we've got it covered!</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">See Printful FAQ</a>
|
16 |
+
</div>
|
17 |
+
</div>
|
18 |
+
|
19 |
+
<div class="support-info-block">
|
20 |
+
<h3>Integration help</h3>
|
21 |
+
<p>Are you experiencing technical issues? Solve them on your own by reading these helpful guides and video tutorials.</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">Integration help</a>
|
24 |
+
</div>
|
25 |
+
</div>
|
26 |
+
|
27 |
+
</div>
|
printful-shipping.php
CHANGED
@@ -1,56 +1,114 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
Plugin Name: Printful Integration for WooCommerce
|
4 |
-
Plugin URI: https://wordpress.org/plugins/printful-shipping-for-woocommerce/
|
5 |
-
Description: Calculate correct shipping and tax rates for your Printful-Woocommerce integration.
|
6 |
-
Version:
|
7 |
-
Author: Printful
|
8 |
-
Author URI: http://www.
|
9 |
-
License: GPL2 http://www.gnu.org/licenses/gpl-2.0.html
|
10 |
-
*/
|
11 |
-
|
12 |
-
if ( ! defined( 'ABSPATH' ) ) exit;
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
if (!class_exists('WC_Integration')) {
|
32 |
-
return;
|
33 |
-
}
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
Plugin Name: Printful Integration for WooCommerce
|
4 |
+
Plugin URI: https://wordpress.org/plugins/printful-shipping-for-woocommerce/
|
5 |
+
Description: Calculate correct shipping and tax rates for your Printful-Woocommerce integration.
|
6 |
+
Version: 2.0
|
7 |
+
Author: Printful
|
8 |
+
Author URI: http://www.printful.com
|
9 |
+
License: GPL2 http://www.gnu.org/licenses/gpl-2.0.html
|
10 |
+
*/
|
11 |
+
|
12 |
+
if ( ! defined( 'ABSPATH' ) ) exit;
|
13 |
+
|
14 |
+
class Printful_Base {
|
15 |
+
|
16 |
+
const VERSION = '2.0';
|
17 |
+
const PF_HOST = 'https://www.printful.com/';
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Construct the plugin.
|
21 |
+
*/
|
22 |
+
public function __construct() {
|
23 |
+
add_action( 'plugins_loaded', array( $this, 'init' ) );
|
24 |
+
}
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Initialize the plugin.
|
28 |
+
*/
|
29 |
+
public function init() {
|
30 |
+
|
31 |
+
if (!class_exists('WC_Integration')) {
|
32 |
+
return;
|
33 |
+
}
|
34 |
+
|
35 |
+
//Register API endpoint
|
36 |
+
add_filter('woocommerce_api_classes', array($this, 'add_api_resource'));
|
37 |
+
|
38 |
+
//Add settings to WooCommerce Index response (legacy - v2)
|
39 |
+
add_filter('woocommerce_api_index', array($this, 'add_extra_info_to_api_index'));
|
40 |
+
|
41 |
+
//load required classes
|
42 |
+
require_once 'includes/class-printful-integration.php';
|
43 |
+
require_once 'includes/class-printful-carriers.php';
|
44 |
+
require_once 'includes/class-printful-taxes.php';
|
45 |
+
require_once 'includes/class-printful-shipping.php';
|
46 |
+
require_once 'includes/class-printful-request-log.php';
|
47 |
+
require_once 'includes/class-printful-admin.php';
|
48 |
+
require_once 'includes/class-printful-admin-dashboard.php';
|
49 |
+
require_once 'includes/class-printful-admin-settings.php';
|
50 |
+
require_once 'includes/class-printful-admin-status.php';
|
51 |
+
require_once 'includes/class-printful-admin-support.php';
|
52 |
+
require_once 'includes/class-printful-size-chart-tab.php';
|
53 |
+
require_once 'includes/class-printful-size-chart-tab.php';
|
54 |
+
|
55 |
+
//launch init
|
56 |
+
Printful_Taxes::init();
|
57 |
+
Printful_Shipping::init();
|
58 |
+
Printful_Request_log::init();
|
59 |
+
Printful_Admin::init();
|
60 |
+
Printful_Size_Chart_Tab::init();
|
61 |
+
|
62 |
+
//hook ajax callbacks
|
63 |
+
add_action( 'wp_ajax_save_printful_settings', array( 'Printful_Admin_Settings', 'save_printful_settings' ) );
|
64 |
+
add_action( 'wp_ajax_ajax_force_check_connect_status', array( 'Printful_Integration', 'ajax_force_check_connect_status' ) );
|
65 |
+
add_action( 'wp_ajax_get_printful_stats', array( 'Printful_Admin_Dashboard', 'render_stats_ajax' ) );
|
66 |
+
add_action( 'wp_ajax_get_printful_orders', array( 'Printful_Admin_Dashboard', 'render_orders_ajax' ) );
|
67 |
+
add_action( 'wp_ajax_get_printful_status_checklist', array( 'Printful_Admin_Status', 'render_status_table_ajax' ) );
|
68 |
+
add_action( 'wp_ajax_get_printful_status_report', array( 'Printful_Admin_Support', 'render_status_report_ajax' ) );
|
69 |
+
add_action( 'wp_ajax_get_printful_carriers', array( 'Printful_Admin_Settings', 'render_carriers_ajax' ) );
|
70 |
+
}
|
71 |
+
|
72 |
+
/**
|
73 |
+
* Added API endpoints
|
74 |
+
* @param $endpoints
|
75 |
+
* @return array
|
76 |
+
*/
|
77 |
+
public function add_api_resource($endpoints) {
|
78 |
+
require_once 'includes/class-printful-api-resource.php';
|
79 |
+
$endpoints[]= 'Printful_API_Resource';
|
80 |
+
return $endpoints;
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* @param $available
|
85 |
+
* Include plugin version in WC API Index
|
86 |
+
* @return mixed
|
87 |
+
*/
|
88 |
+
public static function add_extra_info_to_api_index($available) {
|
89 |
+
$available['printful_plugin_version'] = self::VERSION;
|
90 |
+
$available['locale'] = get_locale();
|
91 |
+
return $available;
|
92 |
+
}
|
93 |
+
|
94 |
+
/**
|
95 |
+
* @return string
|
96 |
+
*/
|
97 |
+
public static function get_asset_url() {
|
98 |
+
return trailingslashit(plugin_dir_url(__FILE__)) . 'assets/';
|
99 |
+
}
|
100 |
+
|
101 |
+
/**
|
102 |
+
* @return string
|
103 |
+
*/
|
104 |
+
public static function get_printful_host() {
|
105 |
+
if ( defined( 'PF_DEV_HOST' ) ) {
|
106 |
+
return PF_DEV_HOST;
|
107 |
+
}
|
108 |
+
|
109 |
+
return self::PF_HOST;
|
110 |
+
}
|
111 |
+
|
112 |
+
}
|
113 |
+
|
114 |
+
new Printful_Base(); //let's go
|
readme.txt
CHANGED
@@ -1,142 +1,169 @@
|
|
1 |
-
=== Printful Integration for WooCommerce ===
|
2 |
-
Contributors: girts_u
|
3 |
-
Tags: woocommerce, printful, drop shipping, shipping, shipping rates, fulfillment, printing, fedex, carriers, checkout, t-shirts
|
4 |
-
Requires at least: 3.8
|
5 |
-
Tested up to: 4.8
|
6 |
-
Stable tag:
|
7 |
-
License: GPLv2 or later
|
8 |
-
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
9 |
-
|
10 |
-
Calculate live shipping rates and tax rates based on actual Printful shipping costs
|
11 |
-
|
12 |
-
== Description ==
|
13 |
-
|
14 |
-
Display actual live shipping rates from carriers like FedEx on your WooCommerce checkout page. This plugin will return a list of available shipping rates specific to the shipping address your customer provides when checking out. These rates are identical to the list you get when you submit an order manually via Printful dashboard.
|
15 |
-
|
16 |
-
This plugin will also automatically calculate taxes where it is required for Printful so that your originally intended profit margin stays intact.
|
17 |
-
|
18 |
-
= Known Limitations =
|
19 |
-
|
20 |
-
* Works with WooCommmerce 2.1 and up
|
21 |
-
|
22 |
-
== Installation ==
|
23 |
-
1. Upload 'printful-shipping' to the '/wp-content/plugins/' directory
|
24 |
-
1. Activate the plugin through the 'Plugins' menu in WordPress
|
25 |
-
1.
|
26 |
-
1. Enable shipping rate calculation in WooCommerce->Settings->Shipping->Printful Shipping tab
|
27 |
-
1. To automatically calculate taxes please check 'Enable taxes and tax calculations' under WooCommerce Tax settings.
|
28 |
-
1. Then go to
|
29 |
-
|
30 |
-
== Frequently Asked Questions ==
|
31 |
-
|
32 |
-
= How do I get Printful API key? =
|
33 |
-
|
34 |
-
Go to https://www.
|
35 |
-
|
36 |
-
== Screenshots ==
|
37 |
-
|
38 |
-
1.
|
39 |
-
2.
|
40 |
-
3.
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
Fixed bug that caused tax rates to become invisible on checkout since WC 3.0
|
46 |
|
47 |
-
= 1.2.7 =
|
48 |
Do not calculate shipping rates for US addresses while ZIP or state is not entered
|
49 |
|
50 |
-
= 1.2.6 =
|
51 |
Include shipping rates in tax calculation for states that require that
|
52 |
-
|
53 |
-
= 1.2.5 =
|
54 |
-
Added option to allow Woocommerce default rates together with Printful rates for Printful products
|
55 |
-
|
56 |
-
= 1.2.4 =
|
57 |
-
Prevent virtual products from requiring shipping rate when bought together with Printful products
|
58 |
-
|
59 |
-
= 1.2.3 =
|
60 |
-
Fixed issue introduced in 1.2.2
|
61 |
-
|
62 |
-
= 1.2.2 =
|
63 |
-
Fixed PHP warning on Woocommerce 2.6
|
64 |
-
|
65 |
-
= 1.2.1 =
|
66 |
-
Fixed bug
|
67 |
-
|
68 |
-
= 1.2 =
|
69 |
-
Support calculating shipping rates for both Printful and non-Printful products at the same time
|
70 |
-
|
71 |
-
= 1.1.2 =
|
72 |
-
Removed check for Curl extension
|
73 |
-
|
74 |
-
= 1.1.1 =
|
75 |
-
Ignore virtual and downloadable products when calculating shipping rates
|
76 |
-
|
77 |
-
= 1.1 =
|
78 |
-
Added tax rate calculation
|
79 |
-
|
80 |
-
= 1.0.2 =
|
81 |
-
Added option to disable SSL
|
82 |
-
|
83 |
-
= 1.0.1 =
|
84 |
-
Minor improvements
|
85 |
-
|
86 |
-
= 1.0 =
|
87 |
-
First release
|
88 |
-
|
89 |
-
== Changelog ==
|
90 |
-
|
91 |
-
=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
92 |
* Fixed bug that caused tax rates to become invisible on checkout since WC 3.0
|
93 |
|
94 |
-
= 1.2.7 =
|
95 |
* Do not calculate shipping rates for US addresses while ZIP or state is not entered
|
96 |
|
97 |
-
= 1.2.6 =
|
98 |
* Include shipping rates in tax calculation for states that require that
|
99 |
-
|
100 |
-
= 1.2.5 =
|
101 |
-
* Added option to allow Woocommerce default rates together with Printful rates for Printful products
|
102 |
-
|
103 |
-
= 1.2.4 =
|
104 |
-
* Prevent virtual products from requiring shipping rate when bought together with Printful products
|
105 |
-
|
106 |
-
= 1.2.3 =
|
107 |
-
* Fixed issue introduced in 1.2.2
|
108 |
-
|
109 |
-
= 1.2.2 =
|
110 |
-
* Fixed PHP warning on Woocommerce 2.6 due to changed method signature
|
111 |
-
* Fixed conflict with "Multiple Packages for WooCommerce" plugin
|
112 |
-
|
113 |
-
= 1.2.1 =
|
114 |
-
* Fixed bug that could have show error message when calculating shipping rates
|
115 |
-
|
116 |
-
= 1.2 =
|
117 |
-
* Support calculating shipping rates for both Printful and non-Printful products at the same time (non-Printful
|
118 |
-
products will get default rates provided by Woocommerce)
|
119 |
-
* Added caching to tax rates
|
120 |
-
* Improved compatibility with Woocommerce 2.6
|
121 |
-
|
122 |
-
= 1.1.2 =
|
123 |
-
* Removed check for Curl extension (since we already used wp_remote_get and it is no longer necessary)
|
124 |
-
|
125 |
-
= 1.1.1 =
|
126 |
-
* Ignore virtual and downloadable products when calculating shipping rates
|
127 |
-
|
128 |
-
= 1.1 =
|
129 |
-
* Added option to calculate sales tax rates for locations where it is required for Printful orders
|
130 |
-
* Added automatic conversion of shipping rates to the currency used by Woocommerce
|
131 |
-
* Printful API client library updated to use Wordpress internal wp_remote_get method instead of CURL directly
|
132 |
-
* Changed plugin code structure for easier implementation of new features in the future
|
133 |
-
|
134 |
-
= 1.0.2 =
|
135 |
-
* Added option to disable SSL for users that do not have a valid CA certificates in their PHP installation
|
136 |
-
|
137 |
-
= 1.0.1 =
|
138 |
-
* Removed CURLOPT_FOLLOWLOCATION that caused problems on some hosting environments
|
139 |
-
* Added option to display reason status messages if the rate API request has failed
|
140 |
-
|
141 |
-
= 1.0 =
|
142 |
-
* First release
|
1 |
+
=== Printful Integration for WooCommerce ===
|
2 |
+
Contributors: girts_u, kievins
|
3 |
+
Tags: woocommerce, printful, drop shipping, shipping, shipping rates, fulfillment, printing, fedex, carriers, checkout, t-shirts
|
4 |
+
Requires at least: 3.8
|
5 |
+
Tested up to: 4.8
|
6 |
+
Stable tag: 2.0
|
7 |
+
License: GPLv2 or later
|
8 |
+
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
9 |
+
|
10 |
+
Calculate live shipping rates and tax rates based on actual Printful shipping costs
|
11 |
+
|
12 |
+
== Description ==
|
13 |
+
|
14 |
+
Display actual live shipping rates from carriers like FedEx on your WooCommerce checkout page. This plugin will return a list of available shipping rates specific to the shipping address your customer provides when checking out. These rates are identical to the list you get when you submit an order manually via Printful dashboard.
|
15 |
+
|
16 |
+
This plugin will also automatically calculate taxes where it is required for Printful so that your originally intended profit margin stays intact.
|
17 |
+
|
18 |
+
= Known Limitations =
|
19 |
+
|
20 |
+
* Works with WooCommmerce 2.1 and up
|
21 |
+
|
22 |
+
== Installation ==
|
23 |
+
1. Upload 'printful-shipping' to the '/wp-content/plugins/' directory
|
24 |
+
1. Activate the plugin through the 'Plugins' menu in WordPress
|
25 |
+
1. Click the "Connect" button or add your Printful API key manually to Printful->Settings tab
|
26 |
+
1. Enable shipping rate calculation in WooCommerce->Settings->Shipping->Printful Shipping tab
|
27 |
+
1. To automatically calculate taxes please check 'Enable taxes and tax calculations' under WooCommerce Tax settings.
|
28 |
+
1. Then go to Printful->Settings tab and check 'Calculated for all products shipped to North Carolina and California'.
|
29 |
+
|
30 |
+
== Frequently Asked Questions ==
|
31 |
+
|
32 |
+
= How do I get Printful API key? =
|
33 |
+
|
34 |
+
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.
|
35 |
+
|
36 |
+
== Screenshots ==
|
37 |
+
|
38 |
+
1. Connect to Printful
|
39 |
+
2. Printful dashboard
|
40 |
+
3. Integration settings
|
41 |
+
4. Plugin status page
|
42 |
+
5. Support page
|
43 |
+
6. Shipping live rates in cart
|
44 |
+
|
45 |
+
== Upgrade Notice ==
|
46 |
+
|
47 |
+
= 2.0 =
|
48 |
+
New major plugin version
|
49 |
+
* All new Printful dashboard
|
50 |
+
* Connect to Printful with a single click
|
51 |
+
* View your Printful profits and latest Printful product orders in WordPress admin
|
52 |
+
* Edit your shipping carriers from Printful dashboard
|
53 |
+
* Improved sales tax compatibility with existing tax rates
|
54 |
+
* New status page - see if your integration is running smoothly
|
55 |
+
* New support page - all info about finding help in one place
|
56 |
+
* Size chart tab - when pushing products from Printful, the size chart will be placed in a separate tab
|
57 |
+
* Improved logging of API requests coming to and from Printful
|
58 |
+
|
59 |
+
= 1.2.8 =
|
60 |
Fixed bug that caused tax rates to become invisible on checkout since WC 3.0
|
61 |
|
62 |
+
= 1.2.7 =
|
63 |
Do not calculate shipping rates for US addresses while ZIP or state is not entered
|
64 |
|
65 |
+
= 1.2.6 =
|
66 |
Include shipping rates in tax calculation for states that require that
|
67 |
+
|
68 |
+
= 1.2.5 =
|
69 |
+
Added option to allow Woocommerce default rates together with Printful rates for Printful products
|
70 |
+
|
71 |
+
= 1.2.4 =
|
72 |
+
Prevent virtual products from requiring shipping rate when bought together with Printful products
|
73 |
+
|
74 |
+
= 1.2.3 =
|
75 |
+
Fixed issue introduced in 1.2.2
|
76 |
+
|
77 |
+
= 1.2.2 =
|
78 |
+
Fixed PHP warning on Woocommerce 2.6
|
79 |
+
|
80 |
+
= 1.2.1 =
|
81 |
+
Fixed bug
|
82 |
+
|
83 |
+
= 1.2 =
|
84 |
+
Support calculating shipping rates for both Printful and non-Printful products at the same time
|
85 |
+
|
86 |
+
= 1.1.2 =
|
87 |
+
Removed check for Curl extension
|
88 |
+
|
89 |
+
= 1.1.1 =
|
90 |
+
Ignore virtual and downloadable products when calculating shipping rates
|
91 |
+
|
92 |
+
= 1.1 =
|
93 |
+
Added tax rate calculation
|
94 |
+
|
95 |
+
= 1.0.2 =
|
96 |
+
Added option to disable SSL
|
97 |
+
|
98 |
+
= 1.0.1 =
|
99 |
+
Minor improvements
|
100 |
+
|
101 |
+
= 1.0 =
|
102 |
+
First release
|
103 |
+
|
104 |
+
== Changelog ==
|
105 |
+
|
106 |
+
= 2.0 =
|
107 |
+
New major plugin version
|
108 |
+
* All new Printful dashboard
|
109 |
+
* Connect to Printful with a single click
|
110 |
+
* View your Printful profits and latest Printful product orders in WordPress admin
|
111 |
+
* Edit your shipping carriers from Printful dashboard
|
112 |
+
* Improved sales tax compatibility with existing tax rates
|
113 |
+
* New status page - see if your integration is running smoothly
|
114 |
+
* New support page - all info about finding help in one place
|
115 |
+
* Size chart tab - when pushing products from Printful, the size chart will be placed in a separate tab
|
116 |
+
* Improved logging of API requests coming to and from Printful
|
117 |
+
|
118 |
+
= 1.2.8 =
|
119 |
* Fixed bug that caused tax rates to become invisible on checkout since WC 3.0
|
120 |
|
121 |
+
= 1.2.7 =
|
122 |
* Do not calculate shipping rates for US addresses while ZIP or state is not entered
|
123 |
|
124 |
+
= 1.2.6 =
|
125 |
* Include shipping rates in tax calculation for states that require that
|
126 |
+
|
127 |
+
= 1.2.5 =
|
128 |
+
* Added option to allow Woocommerce default rates together with Printful rates for Printful products
|
129 |
+
|
130 |
+
= 1.2.4 =
|
131 |
+
* Prevent virtual products from requiring shipping rate when bought together with Printful products
|
132 |
+
|
133 |
+
= 1.2.3 =
|
134 |
+
* Fixed issue introduced in 1.2.2
|
135 |
+
|
136 |
+
= 1.2.2 =
|
137 |
+
* Fixed PHP warning on Woocommerce 2.6 due to changed method signature
|
138 |
+
* Fixed conflict with "Multiple Packages for WooCommerce" plugin
|
139 |
+
|
140 |
+
= 1.2.1 =
|
141 |
+
* Fixed bug that could have show error message when calculating shipping rates
|
142 |
+
|
143 |
+
= 1.2 =
|
144 |
+
* Support calculating shipping rates for both Printful and non-Printful products at the same time (non-Printful
|
145 |
+
products will get default rates provided by Woocommerce)
|
146 |
+
* Added caching to tax rates
|
147 |
+
* Improved compatibility with Woocommerce 2.6
|
148 |
+
|
149 |
+
= 1.1.2 =
|
150 |
+
* Removed check for Curl extension (since we already used wp_remote_get and it is no longer necessary)
|
151 |
+
|
152 |
+
= 1.1.1 =
|
153 |
+
* Ignore virtual and downloadable products when calculating shipping rates
|
154 |
+
|
155 |
+
= 1.1 =
|
156 |
+
* Added option to calculate sales tax rates for locations where it is required for Printful orders
|
157 |
+
* Added automatic conversion of shipping rates to the currency used by Woocommerce
|
158 |
+
* Printful API client library updated to use Wordpress internal wp_remote_get method instead of CURL directly
|
159 |
+
* Changed plugin code structure for easier implementation of new features in the future
|
160 |
+
|
161 |
+
= 1.0.2 =
|
162 |
+
* Added option to disable SSL for users that do not have a valid CA certificates in their PHP installation
|
163 |
+
|
164 |
+
= 1.0.1 =
|
165 |
+
* Removed CURLOPT_FOLLOWLOCATION that caused problems on some hosting environments
|
166 |
+
* Added option to display reason status messages if the rate API request has failed
|
167 |
+
|
168 |
+
= 1.0 =
|
169 |
+
* First release
|