Changeset 3759386
- Timestamp:
- 10/22/2026 06:21:05 AM (13 days ago)
- Location:
- custom-checkbox-ultimate-for-woocommerce
- Files:
-
- 8 added
- 32 edited
- 1 copied
-
assets/screenshot-1.png (modified) ( previous)
-
assets/screenshot-2.png (modified) ( previous)
-
assets/screenshot-3.png (modified) ( previous)
-
assets/screenshot-4.png (modified) ( previous)
-
tags/1.4 (copied) (copied from custom-checkbox-ultimate-for-woocommerce/trunk )
-
tags/1.4/LICENSE.txt (added)
-
tags/1.4/assets/css/icwcu-admin-styling.css (modified) (1 diff)
-
tags/1.4/assets/css/icwcu-services-style.css (modified) (1 diff)
-
tags/1.4/assets/js/custom-checkbox-frontend.js (modified) (1 diff)
-
tags/1.4/assets/js/custom-services-meta-box.js (modified) (1 diff)
-
tags/1.4/custom-checkbox-ultimate-for-woocommerce.php (modified) (4 diffs)
-
tags/1.4/includes/class-icwcu-cart.php (modified) (1 diff)
-
tags/1.4/includes/class-icwcu-help.php (added)
-
tags/1.4/includes/class-icwcu-init.php (modified) (1 diff)
-
tags/1.4/includes/class-icwcu-meta-box.php (modified) (1 diff)
-
tags/1.4/includes/class-icwcu-order.php (modified) (1 diff)
-
tags/1.4/includes/class-icwcu-settings.php (modified) (1 diff)
-
tags/1.4/includes/enqueue.php (modified) (1 diff)
-
tags/1.4/includes/helpers.php (modified) (2 diffs)
-
tags/1.4/includes/plugin-info.php (modified) (1 diff)
-
tags/1.4/languages (added)
-
tags/1.4/languages/custom-checkbox-ultimate-for-woocommerce.pot (added)
-
tags/1.4/readme.txt (modified) (1 diff)
-
trunk/LICENSE.txt (added)
-
trunk/assets/css/icwcu-admin-styling.css (modified) (1 diff)
-
trunk/assets/css/icwcu-services-style.css (modified) (1 diff)
-
trunk/assets/js/custom-checkbox-frontend.js (modified) (1 diff)
-
trunk/assets/js/custom-services-meta-box.js (modified) (1 diff)
-
trunk/custom-checkbox-ultimate-for-woocommerce.php (modified) (4 diffs)
-
trunk/includes/class-icwcu-cart.php (modified) (1 diff)
-
trunk/includes/class-icwcu-help.php (added)
-
trunk/includes/class-icwcu-init.php (modified) (1 diff)
-
trunk/includes/class-icwcu-meta-box.php (modified) (1 diff)
-
trunk/includes/class-icwcu-order.php (modified) (1 diff)
-
trunk/includes/class-icwcu-settings.php (modified) (1 diff)
-
trunk/includes/enqueue.php (modified) (1 diff)
-
trunk/includes/helpers.php (modified) (2 diffs)
-
trunk/includes/plugin-info.php (modified) (1 diff)
-
trunk/languages (added)
-
trunk/languages/custom-checkbox-ultimate-for-woocommerce.pot (added)
-
trunk/readme.txt (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
custom-checkbox-ultimate-for-woocommerce/tags/1.4/assets/css/icwcu-admin-styling.css
r3264079 r3759386 1 div#icwcu_custom_services_meta_box h2 { 2 position: relative; 3 display: inline-block; 4 } 5 6 div#icwcu_custom_services_meta_box h2 svg { 7 float: left; 8 height: 20px; 9 } 10 .icwcu-custom-service-field svg { 11 vertical-align: middle; 12 margin-right: 5px; 13 } 14 .icwcu-custom-service-field textarea{ 15 vertical-align:middle; 16 } 17 18 .menu-icon-ic-woo-ultimate svg { 19 vertical-align: middle; 20 margin-right: 10px; 21 } 22 .icwcu-custom-service-field label { 23 width: 144px; 24 display: inline-block; 25 padding: 0px 20px; 26 } 27 .icwcu-custom-service-field { 28 border: 1px solid #ddd; 29 margin: 10px; 30 } 31 button#icwcu-add-more-fields:hover { 32 background: #167fae; 33 } 34 button#icwcu-add-more-fields { 35 margin: 10px; 36 background: #03A9F4; 37 color: #fff; 38 border: 0; 39 } 40 button#icwcu-add-moreradio-fields:hover { 41 background: #167fae; 42 } 43 button#icwcu-add-moreradio-fields { 44 margin: 10px; 45 background: #03A9F4; 46 color: #fff; 47 border: 0; 48 } 49 50 51 .icwcu-admin-notice-wrapper { 52 padding: 20px; } 53 .icwcu-admin-notice-wrapper h2 { 54 margin-top: 0; } 55 .icwcu-admin-notice-wrapper p { 56 font-size: 14px; } 57 .icwcu-admin-notice-wrapper .icwcu-admin-notice-primary-button { 58 padding: 5px 20px; 59 background: #67bc45; 60 color: #fff; 61 font-weight: normal; 62 text-decoration: none; 63 margin-top: 5px; 64 display: inline-block; 65 border-radius: 50px; } 66 67 .is-style-outline > .wp-block-button__link:not(.has-text-color), 68 .wp-block-button__link.is-style-outline:not(.has-text-color) { 69 color: var(--wp--preset--color--primary); } 70 71 .icwcu--getting-started--wrapper .icwcu-header-left > .fs-notice { 72 display: none !important; } 73 74 .icwcu-options-features > li span { 75 color: green; 76 margin-right: 20px; } 77 78 .icwcu-compare-table table td span { 79 font-size: 21px; 80 background: #f0f0f1; 81 border-radius: 100%; 82 padding: 5px; 83 color: #d72b2b; } 84 85 .icwcu-compare-table table td span.dashicons.dashicons-yes { 86 text-align: center; 87 color: #4caf50; } 88 89 .icwcu-about-container.icwcu-admin-card.icwcu-admin-card-header { 90 background: linear-gradient(45deg, #3F51B5, #E91E63); 91 justify-content: center; 92 align-items: center; } 93 94 .icwcu-about-side { 95 margin-top: 30px; } 96 #icwcu-admin-about-page p, #icwcu-admin-about-page { 97 font-size: 17px; 98 line-height: 1.7; 99 margin: 0 0 15px; } 100 101 .subscribe-panel li span { 102 margin-top: 6px; 103 margin-right: 10px; 104 color: #3fb345; } 105 106 #icwcu-admin-about-page { 107 margin: 20px; 108 max-width: 100%; } 109 110 .icwcu-about-container { 111 padding: 40px 60px; 112 border-bottom: 1px solid #ddd; 113 border-radius: 5px; 114 margin: 30px 0; 115 background: #fff; 116 box-shadow: 0 0 50px #dddddd7d; } 117 118 a.icwcu-admin-button.premium-button span.dashicons{ 119 vertical-align: middle; 120 } 121 a.icwcu-admin-button.premium-button { 122 background: #2196F3; 123 margin-left: 5px; } 124 125 h3.icwcu-admin-card-title { 126 font-size: 1.4rem !important; 127 text-transform: capitalize; 128 color: #fff; 129 margin-bottom: 0; 130 padding: 0; } 131 132 a.icwcu-admin-button { 133 padding: 12px 36px; 134 text-decoration: inherit; 135 background: #3e3541; 136 color: #fff; 137 border-radius: 50px; 138 margin-top: 20px; 139 display: inline-block; 140 font-size: 16px; 141 line-height: 1.75; 142 display: flex; 143 align-items: center; 144 gap: 4px; 145 display: inline-block; } 146 147 @keyframes spin { 148 100% { 149 -webkit-transform: rotate(360deg); 150 transform: rotate(360deg); } } 151 a.icwcu-admin-button.icwcu-importing-icwcu span { 152 animation: spin 4s linear infinite; } 153 154 a.icwcu-admin-button.primary.large { 155 background: #e68836; 156 margin-right: 9px; } 157 158 .feature-list { 159 display: grid; 160 grid-gap: 50px; 161 grid-template-columns: 3fr 1fr; 162 grid-template-rows: 1fr; 163 max-width: 100%; } 164 165 .icwcu-about-container.icwcu-about-content { 166 margin: 0; } 167 168 .icwcu-options-features > li { 169 display: -webkit-inline-flex; 170 display: -ms-inline-flexbox; 171 display: inline-flex; 172 -webkit-align-items: center; 173 -ms-flex-align: center; 174 align-items: center; 175 width: 100%; 176 float: left; 177 margin-bottom: 15px; 178 font-weight: 500; } 179 ul.icwcu-options-features b { 180 font-weight: 900; 181 margin-right: 10px; 182 } 183 .icwcu-options-features > li img { 184 margin-right: 5px; } 185 186 .icwcu-options-features { 187 list-style: none; 188 padding: 0; 189 margin: 0 0 30px; } 190 .icwcu-about-container.icwcu-compare-table, 191 .icwcu-admin-card.member { 192 display: none; 193 } 194 .icwcu-admin-card-title.is-sub { 195 margin-left: -30px; 196 margin-right: -30px; 197 padding: 0 30px 30px; 198 border-bottom: 1px solid #ebf2f5; 199 font-size: 2rem; 200 text-transform: capitalize; 201 margin-top: 0; 202 color: #000; } 203 204 .icwcu-about-side .icwcu-admin-card { 205 background-color: #fff; 206 padding: 30px; 207 margin-bottom: 30px; 208 background-repeat: no-repeat; 209 background-position: 100% 100%; 210 border-radius: 15px; 211 border: 1px solid #ebf2f5; 212 background: -webkit-linear-gradient(315deg, #0dcad2, #0060f3); 213 background: -o-linear-gradient(315deg, #0dcad2, #0060f3); 214 background: linear-gradient(135deg, #0dcad2, #0060f3); 215 color: #fff; } 216 217 h4.icwcu-admin-card-title.is-small { 218 color: #fff; 219 margin: 0; } 220 221 .icwcu-about-side .icwcu-admin-card a.icwcu-admin-button.large.icwcu-block.is-default { 222 background: #fff; 223 color: #000; } 224 225 .icwcu-admin-card.cart-two { 226 background: #fff; 227 color: #000; 228 margin: 0; } 229 230 #icwcu-admin-about-page .icwcu-about-side .icwcu-admin-card h3 { 231 font-size: 1.5rem; 232 color: #fff; } 233 234 #icwcu-admin-about-page .icwcu-admin-card.cart-two h3 { 235 color: #010101; } 236 237 #icwcu-admin-about-page div#setting-error-tgmpa { 238 display: none; } 239 240 .cart-two h3 { 241 color: #000; } 242 243 .icwcu-header-left h2 { 244 font-size: 3rem; 245 text-transform: capitalize; 246 margin: 0; 247 color: #fff; } 248 249 #icwcu-admin-about-page .icwcu-header-left p { 250 color: #fff; } 251 252 .admin-grid-2 { 253 display: grid; 254 grid-gap: 4%; 255 grid-template-columns: 48% 48%; 256 grid-template-rows: 1fr; 257 max-width: 100%; } 258 259 .admin-grid-3 { 260 display: grid; 261 grid-gap: 50px; 262 grid-template-columns: 1fr 1fr 1fr; 263 grid-template-rows: 1fr; 264 max-width: 100%; } 265 266 #subcription-email { 267 width: 500px; 268 /* justify-content: center; */ 269 display: flex; 270 padding: 10px; 271 border: 1px solid #f0f0f1; 272 border-radius: 50px; 273 position: relative; 274 display: grid; 275 /* grid-gap: 50px; */ 276 grid-template-columns: 2fr 1fr; 277 grid-template-rows: 1fr; 278 max-width: 100%; 279 box-shadow: 0 0 23px #0000001a; 280 margin: 22px auto; } 281 282 #subcription-email button.wp-block-search__button.has-icon { 283 background: #2960f3; 284 color: #fff; 285 border-radius: 50px; 286 border: none; 287 cursor: pointer; } 288 289 #subcription-email input[type="text"] { 290 border: none; 291 padding: 10px 25px; 292 background: #f1f1f1; 293 border-radius: 50px; } 294 295 #subcription-email input[type="text"] { 296 border: none; 297 padding: 10px 15px; 298 background: #fff; 299 border-radius: 50px; } 300 301 #icwcu-admin-about-page h2 { 302 font-size: 2.5rem; 303 line-height: 1.5; 304 margin: 0; } 305 306 #icwcu-admin-about-page h3 { 307 font-size: 2rem; 308 line-height: 1.5; 309 margin-bottom: 0; } 310 311 #icwcu-admin-about-page, 312 #icwcu-admin-about-page p { 313 color: #838383; 314 font-size: 15px; 315 line-height: 1.7; } 316 317 #icwcu-admin-about-page img { 318 max-width: 100%; 319 vertical-align: middle; } 320 321 .admin-col > img { 322 box-shadow: 0 15px 24px #0000001c; } 323 324 .icwcu-about-container.icwcu-admin-card.icwcu-admin-card-header { 325 text-align: center; } 326 327 .icwcu-card-header-button-group { 328 text-align: center; 329 display: flex; 330 align-items: center; 331 justify-content: center; } 332 333 a.icwcu-template-kit-admin-button:hover, 334 #icwcu-admin-about-page button.wp-block-search__button.has-icon:hover, 335 a.icwcu-template-kit-admin-button.primary.large:hover { 336 background: #4caf50; } 337 338 a.icwcu-admin-button.premium-button:hover { 339 background: #2960f3; } 340 341 .admin-col { 342 position: relative; } 343 344 .icwcu-compare-table-subtitle { 345 font-size: 16px; } 346 .icwcu-compare-table-subtitle a { 347 text-decoration: none; } 348 .icwcu-compare-table-subtitle a i { 349 font-size: 15px; 350 line-height: 1.7; } 351 352 .icwcu-compare-table table { 353 border: 1px solid #e5e4e4; 354 border-collapse: collapse; 355 border-spacing: 0; 356 margin-top: 2em; 357 width: 100%; } 358 359 .icwcu-compare-table table th { 360 background-color: #F0F0F1; 361 border-bottom: 1px solid #e5e4e4; } 362 363 .icwcu-compare-table table th, 364 .icwcu-compare-table table td { 365 padding: 1em; 366 text-align: center; } 367 368 .icwcu-compare-table table tr:nth-child(2n) td { 369 background: #fbfbfb; } 370 371 .icwcu--getting-started--video-wrapper { 372 min-width: 600px; } 373 .icwcu--getting-started--video-wrapper video { 374 width: 100%; } 375 376 @media screen and (max-width: 782px) { 377 #icwcu-admin-about-page .admin-grid-3, #icwcu-admin-about-page .admin-grid-2 { 378 grid-template-columns: 1fr; } 379 .icwcu-header-left.admin-col { 380 text-align: center; } 381 .icwcu-header-left h2 { 382 font-size: 2rem; } 383 .feature-list { 384 grid-template-columns: 1fr; } 385 #icwcu-admin-about-page, #setting-error-tgmpa { 386 margin: 20px; } 387 .icwcu-options-features > li { 388 width: 100%; } } 389 390 391 .icwcu-tab{ 392 cursor: pointer; 393 padding:10px 20px; 394 margin:0px 2px; 395 background:#000; 396 display:inline-block; 397 color:#fff; 398 border-radius:3px 3px 0px 0px; 399 box-shadow: 0 0.5rem 0.8rem #00000080; 400 } 401 input.icwcu-radio { 402 display: none; 403 } 404 .icwcu-panels{ 405 overflow:hidden; 406 padding:20px; 407 } 408 .icwcu-panel{ 409 display:none; 410 animation: fadein .8s; 411 } 412 @keyframes fadein { 413 from { 414 opacity:0; 415 } 416 to { 417 opacity:1; 418 } 419 } 420 .icwcu-panel-title{ 421 font-size:1.5em; 422 font-weight:bold 423 } 424 .radio{ 425 display:none; 426 } 427 #one:checked ~ .icwcu-panels #one-panel, 428 #two:checked ~ .icwcu-panels #two-panel, 429 #three:checked ~ .icwcu-panels #three-panel{ 430 display:block 431 } 432 #one:checked ~ .icwcu-tabs #one-tab, 433 #two:checked ~ .icwcu-tabs #two-tab, 434 #three:checked ~ .icwcu-tabs #three-tab{ 435 background:#fffffff6; 436 color:#000; 437 border-top: 3px solid #000; 438 } 1 #icwcu_custom_services_meta_box .icwcu-tab { display: inline-block; padding: 12px; cursor: pointer; font-weight: 600; } 2 #icwcu_custom_services_meta_box .icwcu-tab-radio { margin-left: 12px; } 3 #icwcu_custom_services_meta_box .icwcu-editor-panel { display: none; padding: 12px; } 4 #icwcu_custom_services_meta_box #icwcu-tab-checkbox:checked ~ #icwcu-panel-checkbox, 5 #icwcu_custom_services_meta_box #icwcu-tab-radio:checked ~ #icwcu-panel-radio { display: block; } 6 #icwcu_custom_services_meta_box .icwcu-custom-service-field { border: 1px solid #dcdcde; padding: 8px 16px; margin-bottom: 12px; } 7 #icwcu_custom_services_meta_box label { display: inline-block; min-width: 155px; } 8 #icwcu_custom_services_meta_box input[type=text], 9 #icwcu_custom_services_meta_box input[type=number], 10 #icwcu_custom_services_meta_box textarea { width: min(100%, 360px); vertical-align: middle; } 11 .icwcu-guide { max-width: 980px; } 12 .icwcu-guide p, .icwcu-guide li { font-size: 14px; line-height: 1.8; } 13 .icwcu-guide h2 { margin-top: 30px; } 14 @media (max-width: 600px) { #icwcu_custom_services_meta_box label { display: block; } } -
custom-checkbox-ultimate-for-woocommerce/tags/1.4/assets/css/icwcu-services-style.css
r3264079 r3759386 1 h4.icwcu-checkbox-title { 2 margin-bottom: 10px; 3 margin-top: 0; 4 } 5 .icwcu-custom-services table { 6 width: 100%; 7 } 8 .icwcu-custom-services { 9 padding-bottom: 20px; 10 } 11 .icwcu-tooltip { 12 position: relative; 13 display: inline-block; 14 cursor: pointer; 15 color: #0073e6; 16 font-weight: bold; 17 vertical-align: bottom; 18 padding: 0 10px; 19 } 20 21 .icwcu-tooltiptext { 22 visibility: hidden; 23 width: auto; 24 min-width: 50px; 25 max-height: 500px; 26 background-color: #333333c7; 27 color: #fff; 28 border-radius: 6px; 29 padding: 5px; 30 position: absolute; 31 z-index: 2; 32 left: 100%; 33 top: 50%; 34 transform: translateY(-50%); 35 opacity: 0; 36 transition: opacity 0.3s ease; 37 font-size: 12px; 38 pointer-events: none; 39 } 40 .icwcu-tooltiptext::before { 41 content: ''; 42 position: absolute; 43 top: 50%; 44 left: -9px; 45 transform: translateY(-50%); 46 border-width: 5px; 47 border-style: solid; 48 border-color: transparent #333333c7 transparent transparent; 49 } 50 input.icwcu-radio { 51 margin-right: 5px; 52 } 53 /* Tooltip arrow */ 54 /* 55 .icwcu-tooltiptext::after { 56 content: ''; 57 position: absolute; 58 top: 50%; 59 right: -10px; 60 transform: translateY(-50%); 61 border-width: 5px; 62 border-style: solid; 63 border-color: transparent transparent transparent #333333c7; 64 }*/ 65 66 /* Show tooltip when hovering over the trigger or tooltip content */ 67 .icwcu-tooltip:hover .icwcu-tooltiptext, 68 .icwcu-tooltip .icwcu-tooltiptext:hover { 69 visibility: visible; 70 opacity: 1; 71 pointer-events: auto; /* Allow interaction with tooltip when visible */ 72 } 73 74 /* Hide tooltip when mouse leaves both the trigger and tooltip content */ 75 .icwcu-tooltip:not(:hover) .icwcu-tooltiptext:not(:hover) { 76 visibility: hidden; 77 opacity: 0; 78 pointer-events: none; /* Disable interaction again */ 79 } 80 81 82 .icwcu-checkbox { 83 position: relative; 84 margin: 0 1rem 0 0; 85 cursor: pointer; 86 z-index: 1; 87 } 88 89 .icwcu-checkbox:before { 90 transition: all .3s ease-in-out; 91 content: ""; 92 position: absolute; 93 left: 0; 94 z-index: 1; 95 width: 1rem; 96 height: 1rem; 97 border: 2px solid #f2f2f2; 98 } 99 100 .icwcu-checkbox:checked:before { 101 transform: rotate(-45deg); 102 height: .5rem; 103 border-color: green; /* Replace $green with actual value */ 104 border-top-style: none; 105 border-right-style: none; 106 } 107 108 .icwcu-checkbox:after { 109 content: ""; 110 position: absolute; 111 left: 0; 112 width: 1.1rem; 113 height: 1.1rem; 114 background: #ffffff; 115 cursor: pointer; 116 border: 1px solid #ddd; 117 } 118 span.icwcu-currency { 119 margin-left: 20px; 120 } 1 .icwcu-custom-services { border: 1px solid #ddd; padding: 16px; margin: 0 0 18px; min-width: 0; } 2 .icwcu-custom-services legend { font-weight: 600; padding: 0 6px; } 3 .icwcu-custom-services .icwcu-service { display: block; margin: 10px 0; overflow-wrap: anywhere; } 4 .icwcu-custom-services input { margin-right: 6px; } 5 .icwcu-custom-services .icwcu-currency { white-space: nowrap; margin-left: 8px; } 6 .icwcu-custom-services .icwcu-service-description { display: block; font-size: .85em; margin: 3px 0 0 24px; } -
custom-checkbox-ultimate-for-woocommerce/tags/1.4/assets/js/custom-checkbox-frontend.js
r3264079 r3759386 1 jQuery(document).ready(function($) { 2 document.querySelectorAll('.icwcu-radio').forEach((radio) => { 1 /* Lets customers click a selected radio service again to unselect it (add-ons are optional). */ 2 document.addEventListener('DOMContentLoaded', function () { 3 document.querySelectorAll('input.icwcu-radio[name="icwcu_radio_service"]').forEach(function (radio) { 4 radio.addEventListener('mousedown', function () { 5 this.dataset.wasChecked = this.checked ? '1' : ''; 6 }); 3 7 radio.addEventListener('click', function () { 4 const group = this.getAttribute('data-group');5 document.querySelectorAll(`.icwcu-radio[data-group="${group}"]`).forEach((btn) => {6 if (btn !== this) btn.checked = false;7 } );8 if (this.dataset.wasChecked === '1') { 9 this.checked = false; 10 this.dataset.wasChecked = ''; 11 } 8 12 }); 9 13 }); 10 }); 14 }); -
custom-checkbox-ultimate-for-woocommerce/tags/1.4/assets/js/custom-services-meta-box.js
r3264079 r3759386 1 jQuery(document).ready(function($) { 2 var fieldIndex = customServicesMetaBox.initialFieldCount; 3 var fieldIndexradio = customRadioServicesMetaBox.initialRadioFieldCount; 4 5 $('#icwcu-add-more-fields').on('click', function() { 6 fieldIndex++; 7 $('#icwcu-custom-services-fields').append( 8 '<div class="icwcu-custom-service-field" data-index="' + fieldIndex + '">' + 9 '<p>' + 10 '<label for="service_name_' + fieldIndex + '">' + fieldIndex + '. Service Name</label>' + 11 '<input type="text" id="service_name_' + fieldIndex + '" name="icwcu_custom_services[' + fieldIndex + '][name]" value="" />' + 12 '<label for="service_price_' + fieldIndex + '"> Service Price ' + customServicesMetaBox.currencySymbol + ' </label>' + 13 '<input type="number" step="0.01" id="service_price_' + fieldIndex + '" name="icwcu_custom_services[' + fieldIndex + '][price]" value="" />' + 14 '<label for="service_desc_' + fieldIndex + '"> Service Description: ' + ' </label>' + 15 '<textarea id="service_desc_' + fieldIndex + '" name="icwcu_custom_services[' + fieldIndex + '][desc]" value="" ></textarea>' + 16 '</p>' + 17 '</div>' 18 ); 19 }); 20 $('#icwcu-add-moreradio-fields').on('click', function() { 21 fieldIndexradio++; 22 $('#icwcu-custom-radio-services-fields').append( 23 '<div class="icwcu-custom-service-field" data-index="' + fieldIndexradio + '">' + 24 '<p>' + 25 '<label for="radioservice_name_' + fieldIndexradio + '">' + fieldIndexradio + '. Service Name</label>' + 26 '<input type="text" id="radioservice_name_' + fieldIndexradio + '" name="icwcu_custom_servicesradio[' + fieldIndexradio + '][name]" value="" />' + 27 '<label for="radioservice_price_' + fieldIndexradio + '"> Service Price ' + customServicesMetaBox.currencySymbol + ' </label>' + 28 '<input type="number" step="0.01" id="radioservice_price_' + fieldIndexradio + '" name="icwcu_custom_servicesradio[' + fieldIndexradio + '][price]" value="" />' + 29 '<label for="radioservice_desc_' + fieldIndexradio + '"> Service Description: ' + ' </label>' + 30 '<textarea id="radioservice_desc_' + fieldIndexradio + '" name="icwcu_custom_servicesradio[' + fieldIndexradio + '][desc]" value="" ></textarea>' + 31 '</p>' + 32 '</div>' 33 ); 1 document.addEventListener('DOMContentLoaded', function () { 2 document.querySelectorAll('#icwcu_custom_services_meta_box .icwcu-add-fields').forEach(function (button) { 3 button.addEventListener('click', function () { 4 var fields = button.previousElementSibling; 5 var index = fields.children.length + 1; 6 var row = document.createElement('div'); 7 row.className = 'icwcu-custom-service-field'; 8 row.dataset.index = index; 9 ['name', 'price', 'desc'].forEach(function (key) { 10 var p = document.createElement('p'); 11 var label = document.createElement('label'); 12 var input = document.createElement(key === 'desc' ? 'textarea' : 'input'); 13 input.id = fields.dataset.meta + '-' + index + '-' + key; 14 input.name = fields.dataset.meta + '[' + index + '][' + key + ']'; 15 if (key !== 'desc') input.type = key === 'price' ? 'number' : 'text'; 16 if (key === 'price') { input.min = '0'; input.step = 'any'; } 17 label.htmlFor = input.id; 18 label.textContent = icwcuFields[key]; 19 p.appendChild(label); 20 p.appendChild(input); 21 row.appendChild(p); 22 }); 23 fields.appendChild(row); 24 row.querySelector('input').focus(); 25 }); 34 26 }); 35 27 }); -
custom-checkbox-ultimate-for-woocommerce/tags/1.4/custom-checkbox-ultimate-for-woocommerce.php
r3264079 r3759386 4 4 * Plugin URI: https://www.insertcart.com/insertcart-custom-checkbox-ultimate-for-woocommerce-documentation/ 5 5 * Description: Add customizable checkbox options on WooCommerce product pages for additional services and charges. 6 * Version: 1. 36 * Version: 1.4 7 7 * Requires at least: 6.3 8 8 * Requires PHP: 7.2 … … 12 12 * License URI: https://www.gnu.org/licenses/gpl-2.0.html 13 13 * Text Domain: custom-checkbox-ultimate-for-woocommerce 14 * Domain Path: /languages 14 15 * Requires Plugins: woocommerce 16 * WC requires at least: 8.0 17 * WC tested up to: 10.3 15 18 */ 16 19 … … 19 22 } 20 23 21 define( 'ICWCU_PLUGIN_VERSION', '1.3' ); 24 define( 'ICWCU_PLUGIN_VERSION', '1.4' ); 25 define( 'ICWCU_PLUGIN_FILE', __FILE__ ); 26 define( 'ICWCU_DOCUMENTATION_URL', 'https://www.insertcart.com/insertcart-custom-checkbox-ultimate-for-woocommerce-documentation/' ); 22 27 23 28 // Include our enqueue file (even if empty) 24 require_once 'includes/enqueue.php';29 require_once plugin_dir_path( __FILE__ ) . 'includes/enqueue.php'; 25 30 26 31 // Initialize our plugin classes 27 require_once 'includes/class-icwcu-init.php'; 28 $icwcu_init = new ICWCU_Init(); 32 require_once plugin_dir_path( __FILE__ ) . 'includes/class-icwcu-init.php'; 33 add_action( 'plugins_loaded', 'icwcu_initialize' ); 34 function icwcu_initialize() { 35 if ( class_exists( 'WooCommerce' ) ) { 36 new ICWCU_Init(); 37 } else { 38 add_action( 'admin_notices', 'icwcu_dependency_notice' ); 39 } 40 } 41 function icwcu_dependency_notice() { 42 if ( current_user_can( 'activate_plugins' ) ) { 43 echo '<div class="notice notice-warning"><p>' . esc_html__( 'Custom Checkbox Ultimate requires WooCommerce to be installed and active.', 'custom-checkbox-ultimate-for-woocommerce' ) . '</p></div>'; 44 } 45 } 46 require_once __DIR__ . '/includes/class-icwcu-help.php'; 47 ICWCU_Help::hooks(); 29 48 30 49 // Create a Plugin menu in the dashboard … … 34 53 esc_html__( 'IC Woo Ultimate', 'custom-checkbox-ultimate-for-woocommerce' ), 35 54 esc_html__( 'IC Woo Ultimate', 'custom-checkbox-ultimate-for-woocommerce' ), 36 'manage_ options',55 'manage_woocommerce', 37 56 'ic-woo-ultimate', 38 57 'icwcu_about_page', 39 'da ta:image/svg+xml;base64,' . base64_encode( icwcu_get_custom_svg_icon() ),58 'dashicons-yes-alt', 40 59 100 41 60 ); 42 61 } 43 62 function icwcu_about_page() { 63 if ( ! current_user_can( 'manage_woocommerce' ) ) { 64 wp_die( esc_html__( 'You cannot access this setup guide.', 'custom-checkbox-ultimate-for-woocommerce' ), '', array( 'response' => 403 ) ); 65 } 44 66 include_once plugin_dir_path( __FILE__ ) . 'includes/plugin-info.php'; 45 67 } 68 69 // Declare compatibility with WooCommerce High-Performance Order Storage (HPOS) and Cart/Checkout blocks. 70 add_action( 'before_woocommerce_init', 'icwcu_declare_wc_compatibility' ); 71 function icwcu_declare_wc_compatibility() { 72 if ( class_exists( '\Automattic\WooCommerce\Utilities\FeaturesUtil' ) ) { 73 \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', ICWCU_PLUGIN_FILE, true ); 74 \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'cart_checkout_blocks', ICWCU_PLUGIN_FILE, true ); 75 } 76 } -
custom-checkbox-ultimate-for-woocommerce/tags/1.4/includes/class-icwcu-cart.php
r3264079 r3759386 5 5 6 6 class ICWCU_Cart { 7 8 /** 9 * Original product prices, so add-ons are never added twice 10 * when cart totals are calculated more than once per request. 11 * 12 * @var array 13 */ 14 private $base_prices = array(); 15 7 16 public function __construct() { 8 add_filter( 'woocommerce_add_cart_item_data', array( $this, 'add_custom_fields_to_cart_item' ), 10, 2 ); 9 add_filter( 'woocommerce_add_cart_item_data', array( $this, 'add_customradio_fields_to_cart_item' ), 10, 2 ); 10 add_filter( 'woocommerce_get_item_data', array( $this, 'display_custom_fields_in_cart' ), 10, 2 ); 11 12 add_action( 'woocommerce_before_calculate_totals', array( $this, 'adjust_cart_item_price' ), 10, 1 ); 13 add_action( 'woocommerce_before_calculate_totals', array( $this, 'adjustradio_cart_item_price' ), 10, 1 ); 14 //add_action( 'woocommerce_cart_calculate_fees', array( $this, 'icwcu_adjust_cart_total' ), 10, 1 ); 15 16 add_action( 'woocommerce_after_calculate_totals', array( $this, 'adjust_cart_item_priceless' ), 10, 1 ); 17 add_action( 'woocommerce_after_calculate_totals', array( $this, 'adjustradio_cart_item_priceless' ), 10, 1 ); 18 // add_action( 'woocommerce_cart_item_price', array( $this, 'adjust_cart_item_price' ), 10, 1 ); 17 add_filter( 'woocommerce_add_cart_item_data', array( $this, 'add_services_to_cart_item' ), 10, 2 ); 18 add_filter( 'woocommerce_get_item_data', array( $this, 'display_services_in_cart' ), 10, 2 ); 19 add_action( 'woocommerce_before_calculate_totals', array( $this, 'adjust_cart_item_prices' ), 20, 1 ); 20 } 19 21 20 } 21 22 public function add_custom_fields_to_cart_item( $cart_item_data, $product_id ) { 23 if ( ! isset( $_POST['icwcu_custom_checkbox_nonce'] ) || ! wp_verify_nonce( sanitize_text_field(wp_unslash( $_POST['icwcu_custom_checkbox_nonce']) ), 'icwcu_custom_checkbox_nonce_action' ) ) { 24 return $cart_item_data; 25 } 26 $custom_services = get_post_meta( $product_id, 'icwcu_custom_services', true ); 27 if ( ! is_array( $custom_services ) ) { 28 $custom_services = array(); 29 } 30 31 if ( ! empty( $custom_services ) ) { 32 foreach ( $custom_services as $service ) { 33 $service_key = sanitize_title( $service['name'] ); 34 if ( isset( $_POST[ $service_key ] ) ) { 35 $price = floatval( wp_unslash( $_POST[ $service_key ] ) ); 36 $cart_item_data[ $service_key ] = ( $price >= 0 ) ? $price : 0; 37 } 38 } 39 } 40 return $cart_item_data; 41 } 42 43 public function add_customradio_fields_to_cart_item( $cart_item_data, $product_id ) { 44 if ( ! isset( $_POST['icwcu_custom_radio_nonce'] ) || ! wp_verify_nonce( sanitize_text_field(wp_unslash( $_POST['icwcu_custom_radio_nonce']) ), 'icwcu_custom_radio_nonce_action' ) ) { 45 return $cart_item_data; 46 } 47 $custom_servicesradio = get_post_meta( $product_id, 'icwcu_custom_servicesradio', true ); 48 if ( ! is_array( $custom_servicesradio ) ) { 49 $custom_servicesradio = array(); 50 } 51 52 if ( ! empty( $custom_servicesradio ) ) { 53 foreach ( $custom_servicesradio as $serviceradio ) { 54 $service_keyradio = sanitize_title( $serviceradio['name'] ); 55 if ( isset( $_POST[ $service_keyradio ] ) ) { 56 $price = floatval( wp_unslash( $_POST[ $service_keyradio ] ) ); 57 $cart_item_data[ $service_keyradio ] = ( $price >= 0 ) ? $price : 0; 58 } 22 /** 23 * Save the selected services on the cart item. 24 * Prices are always read from the product settings, never from the request. 25 */ 26 public function add_services_to_cart_item( $cart_item_data, $product_id ) { 27 $selected = array(); 59 28 29 // Checkbox services. 30 if ( isset( $_POST['icwcu_custom_checkbox_nonce'] ) 31 && is_string( $_POST['icwcu_custom_checkbox_nonce'] ) 32 && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['icwcu_custom_checkbox_nonce'] ) ), 'icwcu_custom_checkbox_nonce_action' ) 33 && ! empty( $_POST['icwcu_services'] ) && is_array( $_POST['icwcu_services'] ) ) { 60 34 35 $services = icwcu_get_product_services( $product_id, 'checkbox' ); 36 $posted = array_map( 'sanitize_title', array_filter( wp_unslash( $_POST['icwcu_services'] ), 'is_string' ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Non-string entries are discarded and every remaining entry passes through sanitize_title. 61 37 62 } 63 } 64 return $cart_item_data; 65 } 66 67 public function display_custom_fields_in_cart( $item_data, $cart_item ) { 68 $custom_services = get_post_meta( $cart_item['product_id'], 'icwcu_custom_services', true ); 69 $custom_servicesradio = get_post_meta( $cart_item['product_id'], 'icwcu_custom_servicesradio', true ); 70 71 if ( ! empty( $custom_services ) ) { 72 foreach ( $custom_services as $service ) { 73 $service_key = sanitize_title( $service['name'] ); 74 if ( isset( $cart_item[ $service_key ] ) ) { 75 $item_data[] = array( 76 'name' => $service['name'], 77 'value' => get_woocommerce_currency_symbol() . esc_html( $cart_item[ $service_key ] ) 38 foreach ( array_unique( $posted ) as $key ) { 39 if ( isset( $services[ $key ] ) ) { 40 $selected[] = array( 41 'name' => $services[ $key ]['name'], 42 'price' => $services[ $key ]['price'], 78 43 ); 79 44 } 80 45 } 81 46 } 82 83 if ( ! empty( $custom_servicesradio ) ) { 84 foreach ( $custom_servicesradio as $serviceradio ) { 85 $service_keyradio = sanitize_title( $serviceradio['name'] ); 86 if ( isset( $cart_item[ $service_keyradio ] ) ) { 87 $item_data[] = array( 88 'name' => $serviceradio['name'], 89 'value' => get_woocommerce_currency_symbol() . esc_html( $cart_item[ $service_keyradio ] ) 47 48 // Radio service (only one can be chosen). 49 if ( isset( $_POST['icwcu_custom_radio_nonce'] ) 50 && is_string( $_POST['icwcu_custom_radio_nonce'] ) 51 && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['icwcu_custom_radio_nonce'] ) ), 'icwcu_custom_radio_nonce_action' ) 52 && ! empty( $_POST['icwcu_radio_service'] ) && is_string( $_POST['icwcu_radio_service'] ) ) { 53 54 $services = icwcu_get_product_services( $product_id, 'radio' ); 55 $key = sanitize_title( wp_unslash( $_POST['icwcu_radio_service'] ) ); 56 57 if ( isset( $services[ $key ] ) ) { 58 $selected[] = array( 59 'name' => $services[ $key ]['name'], 60 'price' => $services[ $key ]['price'], 61 ); 62 } 63 } 64 65 if ( ! empty( $selected ) ) { 66 $cart_item_data['icwcu_services'] = $selected; 67 } 68 69 return $cart_item_data; 70 } 71 72 /** 73 * Get the services selected for a cart item. 74 * 75 * @param array $cart_item Cart item. 76 * @return array List of array( 'name' => string, 'price' => float ). 77 */ 78 public static function get_cart_item_services( $cart_item ) { 79 if ( ! empty( $cart_item['icwcu_services'] ) && is_array( $cart_item['icwcu_services'] ) ) { 80 return array_values( array_filter( $cart_item['icwcu_services'], function( $service ) { 81 return is_array( $service ) && isset( $service['name'], $service['price'] ) && is_string( $service['name'] ) && is_numeric( $service['price'] ) && is_finite( (float) $service['price'] ) && $service['price'] >= 0; 82 } ) ); 83 } 84 85 // Items added to the cart by version 1.3 or older. 86 $selected = array(); 87 if ( empty( $cart_item['product_id'] ) ) { 88 return $selected; 89 } 90 foreach ( array( 'checkbox', 'radio' ) as $type ) { 91 foreach ( icwcu_get_product_services( $cart_item['product_id'], $type ) as $key => $service ) { 92 if ( isset( $cart_item[ $key ] ) && is_numeric( $cart_item[ $key ] ) ) { 93 $selected[] = array( 94 'name' => $service['name'], 95 'price' => $service['price'], 90 96 ); 97 if ( 'radio' === $type ) { 98 break; 99 } 91 100 } 92 101 } 93 102 } 94 103 return $selected; 104 } 105 106 /** 107 * Show the selected services under the product name in cart and checkout. 108 */ 109 public function display_services_in_cart( $item_data, $cart_item ) { 110 foreach ( self::get_cart_item_services( $cart_item ) as $service ) { 111 $item_data[] = array( 112 'name' => $service['name'], 113 'value' => icwcu_format_price( $service['price'] ), 114 ); 115 } 95 116 return $item_data; 96 117 } 97 98 118 99 100 private function adjust_cart_item_price_generic( $cart, $meta_key, $add = true ) { 101 if ( is_admin() && ! defined( 'DOING_AJAX' ) ) { 119 /** 120 * Add the selected services to the item price. 121 */ 122 public function adjust_cart_item_prices( $cart ) { 123 if ( is_admin() && ! wp_doing_ajax() ) { 102 124 return; 103 125 } 104 foreach ( $cart->get_cart() as $cart_item ) { 105 $services = get_post_meta( $cart_item['product_id'], $meta_key, true ); 106 if ( ! is_array( $services ) ) { 107 $services = array(); 126 127 foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) { 128 $services = self::get_cart_item_services( $cart_item ); 129 if ( empty( $services ) || empty( $cart_item['data'] ) ) { 130 continue; 108 131 } 109 if ( ! empty( $services ) ) { 110 $additional_cost = 0; 111 foreach ( $services as $service ) { 112 $service_key = sanitize_title( $service['name'] ); 113 if ( isset( $cart_item[ $service_key ] ) ) { 114 $additional_cost += floatval( $cart_item[ $service_key ] ); 115 } 116 } 117 if ( $additional_cost > 0 ) { 118 $new_price = $add ? 119 $cart_item['data']->get_price() + $additional_cost : 120 $cart_item['data']->get_price() - $additional_cost; 121 $cart_item['data']->set_price( $new_price ); 122 } 132 133 $extra = 0; 134 foreach ( $services as $service ) { 135 $extra += (float) $service['price']; 123 136 } 137 if ( $extra <= 0 ) { 138 continue; 139 } 140 141 $product = $cart_item['data']; 142 $id = spl_object_hash( $product ); 143 if ( ! isset( $this->base_prices[ $id ] ) ) { 144 $this->base_prices[ $id ] = (float) $product->get_price( 'edit' ); 145 } 146 147 $product->set_price( $this->base_prices[ $id ] + $extra ); 124 148 } 125 149 } 126 127 public function adjust_cart_item_price( $cart ) {128 $this->adjust_cart_item_price_generic( $cart, 'icwcu_custom_services', true );129 }130 131 public function adjustradio_cart_item_price( $cart ) {132 $this->adjust_cart_item_price_generic( $cart, 'icwcu_custom_servicesradio', true );133 }134 135 public function adjust_cart_item_priceless( $cart ) {136 $this->adjust_cart_item_price_generic( $cart, 'icwcu_custom_services', false );137 }138 139 public function adjustradio_cart_item_priceless( $cart ) {140 $this->adjust_cart_item_price_generic( $cart, 'icwcu_custom_servicesradio', false );141 }142 143 /********144 ** Substract from item price145 ****/146 147 148 149 150 public function icwcu_adjust_cart_total( $cart ) {151 if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {152 return;153 }154 155 $additional_cost = 0;156 157 foreach ( $cart->get_cart() as $cart_item ) {158 $custom_services = get_post_meta( $cart_item['product_id'], 'icwcu_custom_services', true );159 if ( is_array( $custom_services ) && ! empty( $custom_services ) ) {160 foreach ( $custom_services as $service ) {161 $service_key = sanitize_title( $service['name'] );162 if ( isset( $cart_item[ $service_key ] ) ) {163 $additional_cost += floatval( $cart_item[ $service_key ] );164 }165 }166 }167 168 $custom_servicesradio = get_post_meta( $cart_item['product_id'], 'icwcu_custom_servicesradio', true );169 if ( is_array( $custom_servicesradio ) && ! empty( $custom_servicesradio ) ) {170 foreach ( $custom_servicesradio as $serviceradio ) {171 $service_keyradio = sanitize_title( $serviceradio['name'] );172 if ( isset( $cart_item[ $service_keyradio ] ) ) {173 $additional_cost += floatval( $cart_item[ $service_keyradio ] );174 }175 }176 }177 }178 179 if ( $additional_cost > 0 ) {180 WC()->cart->add_fee( __( 'Additional Services', 'custom-checkbox-ultimate-for-woocommerce' ), $additional_cost );181 }182 }183 184 150 } -
custom-checkbox-ultimate-for-woocommerce/tags/1.4/includes/class-icwcu-init.php
r3264079 r3759386 7 7 public function __construct() { 8 8 // Include required class files and helpers 9 require_once 'class-icwcu-meta-box.php';10 require_once 'class-icwcu-cart.php';11 require_once 'class-icwcu-order.php';12 require_once 'class-icwcu-settings.php';13 require_once 'helpers.php';9 require_once __DIR__ . '/class-icwcu-meta-box.php'; 10 require_once __DIR__ . '/class-icwcu-cart.php'; 11 require_once __DIR__ . '/class-icwcu-order.php'; 12 require_once __DIR__ . '/class-icwcu-settings.php'; 13 require_once __DIR__ . '/helpers.php'; 14 14 15 15 // Instantiate the classes to initialize functionality -
custom-checkbox-ultimate-for-woocommerce/tags/1.4/includes/class-icwcu-meta-box.php
r3264079 r3759386 1 1 <?php 2 if ( ! defined( 'ABSPATH' ) ) { 3 exit; 4 } 2 defined( 'ABSPATH' ) || exit; 5 3 4 /** Product editor and classic product form fields. */ 6 5 class ICWCU_Meta_Box { 7 6 public function __construct() { 8 7 add_action( 'add_meta_boxes', array( $this, 'add_custom_services_meta_box' ) ); 9 add_action( 'save_post ', array( $this, 'save_custom_services_meta_box_data' ) );8 add_action( 'save_post_product', array( $this, 'save_custom_services_meta_box_data' ) ); 10 9 add_action( 'admin_enqueue_scripts', array( $this, 'meta_box_scripts' ) ); 11 10 add_action( 'woocommerce_before_add_to_cart_button', array( $this, 'add_custom_fields' ) ); 12 11 } 13 14 12 public function add_custom_services_meta_box() { 15 add_meta_box( 16 'icwcu_custom_services_meta_box', 17 icwcu_get_custom_svg_icon() . 'Custom Services - IC Woo Ultimate', 18 array( $this, 'display_custom_services_meta_box' ), 19 'product', 20 'normal', 21 'high' 22 ); 13 add_meta_box( 'icwcu_custom_services_meta_box', __( 'Custom Services - IC Woo Ultimate', 'custom-checkbox-ultimate-for-woocommerce' ), array( $this, 'display_custom_services_meta_box' ), 'product', 'normal', 'high' ); 23 14 } 24 25 15 public function display_custom_services_meta_box( $post ) { 26 $custom_services = get_post_meta( $post->ID, 'icwcu_custom_services', true ); 27 if ( ! is_array( $custom_services ) ) { 28 $custom_services = array(); 16 wp_nonce_field( 'icwcu_custom_services_meta_box_nonce', 'icwcu_custom_services_meta_box_nonce' ); 17 $help_url = current_user_can( 'manage_woocommerce' ) ? ICWCU_Help::url() : ICWCU_DOCUMENTATION_URL; 18 echo '<p>' . esc_html__( 'Add optional services per product unit. Use checkboxes for multiple choices and radio buttons for one choice.', 'custom-checkbox-ultimate-for-woocommerce' ) . ' <a href="' . esc_url( $help_url ) . '">' . esc_html__( 'Setup guide', 'custom-checkbox-ultimate-for-woocommerce' ) . '</a></p>'; 19 echo '<input class="icwcu-tab-radio" id="icwcu-tab-checkbox" name="icwcu-editor-tab" type="radio" checked><label class="icwcu-tab" for="icwcu-tab-checkbox">' . esc_html__( 'Checkbox Type', 'custom-checkbox-ultimate-for-woocommerce' ) . '</label>'; 20 echo '<input class="icwcu-tab-radio" id="icwcu-tab-radio" name="icwcu-editor-tab" type="radio"><label class="icwcu-tab" for="icwcu-tab-radio">' . esc_html__( 'Radio Type', 'custom-checkbox-ultimate-for-woocommerce' ) . '</label>'; 21 foreach ( array( 'checkbox', 'radio' ) as $type ) { 22 $meta = 'radio' === $type ? 'icwcu_custom_servicesradio' : 'icwcu_custom_services'; 23 $title_meta = 'radio' === $type ? 'icwcu_custom_radioservices_title' : 'icwcu_custom_services_title'; 24 $saved = get_post_meta( $post->ID, $meta, true ); 25 $services = is_array( $saved ) ? array_values( array_filter( $saved, 'is_array' ) ) : array(); 26 echo '<div class="icwcu-editor-panel" id="icwcu-panel-' . esc_attr( $type ) . '">'; 27 echo '<p><label for="' . esc_attr( $title_meta ) . '">' . esc_html__( 'Custom Services Title:', 'custom-checkbox-ultimate-for-woocommerce' ) . '</label> <input type="text" id="' . esc_attr( $title_meta ) . '" name="' . esc_attr( $title_meta ) . '" value="' . esc_attr( get_post_meta( $post->ID, $title_meta, true ) ) . '"></p>'; 28 echo '<div class="icwcu-service-fields" data-meta="' . esc_attr( $meta ) . '">'; 29 for ( $i = 0; $i < max( 5, count( $services ) ); $i++ ) { 30 $service = isset( $services[ $i ] ) ? $services[ $i ] : array(); 31 echo '<div class="icwcu-custom-service-field" data-index="' . esc_attr( $i + 1 ) . '">'; 32 foreach ( self::labels() as $key => $label ) { 33 $field = $meta . '[' . ( $i + 1 ) . '][' . $key . ']'; 34 $id = $meta . '-' . ( $i + 1 ) . '-' . $key; 35 $value = isset( $service[ $key ] ) && is_scalar( $service[ $key ] ) ? $service[ $key ] : ''; 36 echo '<p><label for="' . esc_attr( $id ) . '">' . esc_html( $label ) . '</label> '; 37 if ( 'desc' === $key ) { 38 echo '<textarea id="' . esc_attr( $id ) . '" name="' . esc_attr( $field ) . '">' . esc_textarea( $value ) . '</textarea>'; 39 } else { 40 echo '<input type="' . ( 'price' === $key ? 'number' : 'text' ) . '" ' . ( 'price' === $key ? 'min="0" step="any" ' : '' ) . 'id="' . esc_attr( $id ) . '" name="' . esc_attr( $field ) . '" value="' . esc_attr( $value ) . '">'; 41 } 42 echo '</p>'; 43 } 44 echo '</div>'; 45 } 46 echo '</div><button type="button" class="button icwcu-add-fields">+ ' . esc_html__( 'Add More Fields', 'custom-checkbox-ultimate-for-woocommerce' ) . '</button></div>'; 29 47 } 30 $custom_servicesradio = get_post_meta( $post->ID, 'icwcu_custom_servicesradio', true );31 if ( ! is_array( $custom_servicesradio ) ) {32 $custom_servicesradio = array();33 }34 35 wp_nonce_field( 'icwcu_custom_services_meta_box_nonce', 'icwcu_custom_services_meta_box_nonce' );36 $custom_services_title = get_post_meta( $post->ID, 'icwcu_custom_services_title', true );37 $custom_radioservices_title = get_post_meta( $post->ID, 'icwcu_custom_radioservices_title', true );38 39 echo '<input class="radio icwcu-radio" id="one" name="group" type="radio" checked>40 <input class="radio icwcu-radio" id="two" name="group" type="radio">41 <div class="icwcu-tabs">42 <label class="icwcu-tab" id="one-tab" for="one">'.esc_html__( 'Checkbox Type', 'custom-checkbox-ultimate-for-woocommerce' ).'</label>43 <label class="icwcu-tab" id="two-tab" for="two">'.esc_html__( 'Radio Type', 'custom-checkbox-ultimate-for-woocommerce' ).'</label>44 </div>';45 46 echo '<div class="icwcu-panels">';47 48 // Checkbox Type Panel49 echo '<div class="icwcu-panel" id="one-panel">';50 echo '<div id="icwcu-custom-services-title">';51 echo '<p><label for="icwcu_custom_services_title">' . esc_html__( 'Custom Services Title:', 'custom-checkbox-ultimate-for-woocommerce' ) . ' </label>';52 echo '<input type="text" id="icwcu_custom_services_title" name="icwcu_custom_services_title" value="' . esc_attr( sanitize_text_field($custom_services_title) ) . '" /></p>';53 echo '</div>';54 55 $default_fields = 5;56 $field_count = max( count( $custom_services ), $default_fields );57 58 echo '<div id="icwcu-custom-services-fields">';59 for ( $i = 1; $i <= $field_count; $i++ ) {60 $service_name = isset( $custom_services[$i]['name'] ) ? $custom_services[$i]['name'] : '';61 // $service_price = isset( $custom_services[$i]['price'] ) ? $custom_services[$i]['price'] : '';62 $service_price = isset( $custom_services[$i]['price'] ) ? floatval( $custom_services[$i]['price'] ) : 0;63 $service_desc = isset( $custom_services[$i]['desc'] ) ? $custom_services[$i]['desc'] : '';64 65 echo '<div class="icwcu-custom-service-field" data-index="' . esc_html( $i ) . '">';66 echo '<p><label for="service_name_' . esc_html( $i ) . '">' . esc_html( $i ) . '. ' . esc_html__( 'Service Name:', 'custom-checkbox-ultimate-for-woocommerce' ) . ' </label>';67 echo '<input type="text" id="service_name_' . esc_html( $i ) . '" name="icwcu_custom_services[' . esc_html( $i ) . '][name]" value="' . esc_attr(sanitize_text_field( $service_name) ) . '" />';68 echo '<label for="service_price_' . esc_html( $i ) . '">' . esc_html__( 'Service Price', 'custom-checkbox-ultimate-for-woocommerce' ) . ' ' . esc_html( get_woocommerce_currency_symbol() ) . ' </label>';69 echo '<input type="number" step="0.01" id="service_price_' . esc_html( $i ) . '" name="icwcu_custom_services[' . esc_html( $i ) . '][price]" value="' . esc_attr( $service_price ) . '" />';70 echo '<label for="service_desc_' . esc_html( $i ) . '">' . esc_html__( 'Service Description:', 'custom-checkbox-ultimate-for-woocommerce' ) . ' </label>';71 echo '<textarea id="service_desc_' . esc_html( $i ) . '" name="icwcu_custom_services[' . esc_html( $i ) . '][desc]">' . esc_textarea( sanitize_textarea_field($service_desc) ) . '</textarea></p>';72 echo '</div>';73 }74 echo '</div>';75 echo '<button type="button" id="icwcu-add-more-fields" class="button">+ ' . esc_html__( 'Add More Fields', 'custom-checkbox-ultimate-for-woocommerce' ) . '</button>';76 echo '</div>';77 78 // Radio Type Panel79 echo '<div class="icwcu-panel" id="two-panel">';80 echo '<div id="icwcu-custom-services-title">';81 echo '<p><label for="icwcu_custom_radioservices_title">' . esc_html__( 'Custom Radio Services Title:', 'custom-checkbox-ultimate-for-woocommerce' ) . ' </label>';82 echo '<input type="text" id="icwcu_custom_radioservices_title" name="icwcu_custom_radioservices_title" value="' . esc_attr( sanitize_text_field($custom_radioservices_title) ) . '" /></p>';83 echo '</div>';84 85 $default_fieldsradio = 5;86 $field_countradio = max( count( $custom_servicesradio ), $default_fieldsradio );87 88 echo '<div id="icwcu-custom-radio-services-fields">';89 for ( $i = 1; $i <= $field_countradio; $i++ ) {90 $radioservice_name = isset( $custom_servicesradio[$i]['name'] ) ? $custom_servicesradio[$i]['name'] : '';91 // $radioservice_price = isset( $custom_servicesradio[$i]['price'] ) ? $custom_servicesradio[$i]['price'] : '';92 $radioservice_price = isset( $custom_servicesradio[$i]['price'] ) ? floatval( $custom_servicesradio[$i]['price'] ) : 0;93 $radioservice_desc = isset( $custom_servicesradio[$i]['desc'] ) ? $custom_servicesradio[$i]['desc'] : '';94 echo '<div class="icwcu-custom-service-field" data-index="' . esc_html( $i ) . '">';95 echo '<p><label for="radioservice_name_' . esc_html( $i ) . '">' . esc_html( $i ) . '. ' . esc_html__( 'Service Name:', 'custom-checkbox-ultimate-for-woocommerce' ) . ' </label>';96 echo '<input type="text" id="radioservice_name_' . esc_html( $i ) . '" name="icwcu_custom_servicesradio[' . esc_html( $i ) . '][name]" value="' . esc_attr( sanitize_text_field($radioservice_name) ) . '" />';97 echo '<label for="radioservice_price_' . esc_html( $i ) . '">' . esc_html__( 'Service Price', 'custom-checkbox-ultimate-for-woocommerce' ) . ' ' . esc_html( get_woocommerce_currency_symbol() ) . ' </label>';98 echo '<input type="number" step="0.01" id="radioservice_price_' . esc_html( $i ) . '" name="icwcu_custom_servicesradio[' . esc_html( $i ) . '][price]" value="' . esc_attr( $radioservice_price ) . '" />';99 echo '<label for="radioservice_desc_' . esc_html( $i ) . '">' . esc_html__( 'Service Description:', 'custom-checkbox-ultimate-for-woocommerce' ) . ' </label>';100 echo '<textarea id="radioservice_desc_' . esc_html( $i ) . '" name="icwcu_custom_servicesradio[' . esc_html( $i ) . '][desc]">' . esc_textarea( sanitize_textarea_field($radioservice_desc )) . '</textarea></p>';101 echo '</div>';102 }103 echo '</div>';104 echo '<button type="button" id="icwcu-add-moreradio-fields" class="button">+ ' . esc_html__( 'Add More Fields', 'custom-checkbox-ultimate-for-woocommerce' ) . '</button>';105 echo '</div>';106 107 echo '</div>'; // End of panels108 48 } 109 49 public static function labels() { 50 return array( 'name' => __( 'Service Name:', 'custom-checkbox-ultimate-for-woocommerce' ), 'price' => __( 'Service Price:', 'custom-checkbox-ultimate-for-woocommerce' ), 'desc' => __( 'Service Description:', 'custom-checkbox-ultimate-for-woocommerce' ) ); 51 } 110 52 public function meta_box_scripts( $hook ) { 111 if ( 'post.php' != $hook && 'post-new.php' != $hook ) { 53 $screen = get_current_screen(); 54 if ( ! in_array( $hook, array( 'post.php', 'post-new.php' ), true ) || ! $screen || 'product' !== $screen->post_type ) { 112 55 return; 113 56 } 114 115 wp_enqueue_script( 'icwcu-custom-services-meta-box', plugin_dir_url( __FILE__ ) . '../assets/js/custom-services-meta-box.js', array( 'jquery' ), ICWCU_PLUGIN_VERSION, true ); 116 $initial_field_count = max( 5, count( (array) get_post_meta( get_the_ID(), 'icwcu_custom_services', true ) ) ); 117 $initial_radiofield_count = max( 5, count( (array) get_post_meta( get_the_ID(), 'icwcu_custom_servicesradio', true ) ) ); 118 wp_localize_script( 'icwcu-custom-services-meta-box', 'customServicesMetaBox', array( 119 'initialFieldCount' => $initial_field_count, 120 'currencySymbol' => get_woocommerce_currency_symbol(), 121 ) ); 122 wp_localize_script( 'icwcu-custom-services-meta-box', 'customRadioServicesMetaBox', array( 123 'initialRadioFieldCount' => $initial_radiofield_count 124 ) ); 57 wp_enqueue_script( 'icwcu-custom-services-meta-box', plugins_url( 'assets/js/custom-services-meta-box.js', ICWCU_PLUGIN_FILE ), array(), ICWCU_PLUGIN_VERSION, true ); 58 wp_localize_script( 'icwcu-custom-services-meta-box', 'icwcuFields', self::labels() ); 125 59 } 126 127 60 public function save_custom_services_meta_box_data( $post_id ) { 128 if ( ! isset( $_POST['icwcu_custom_services_meta_box_nonce'] ) || 129 ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['icwcu_custom_services_meta_box_nonce'] ) ), 'icwcu_custom_services_meta_box_nonce' ) ) { 61 if ( ! isset( $_POST['icwcu_custom_services_meta_box_nonce'] ) || ! is_string( $_POST['icwcu_custom_services_meta_box_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['icwcu_custom_services_meta_box_nonce'] ) ), 'icwcu_custom_services_meta_box_nonce' ) ) { 130 62 return; 131 63 } 132 if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE) {64 if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) || wp_is_post_revision( $post_id ) || 'product' !== get_post_type( $post_id ) || ! current_user_can( 'edit_post', $post_id ) ) { 133 65 return; 134 66 } 135 if ( isset( $_POST['post_type'] ) && 'product' === $_POST['post_type']) {136 if ( ! current_user_can( 'edit_post', $post_id) ) {137 return;67 foreach ( array( 'icwcu_custom_services', 'icwcu_custom_servicesradio' ) as $meta ) { 68 if ( isset( $_POST[ $meta ] ) ) { 69 update_post_meta( $post_id, $meta, icwcu_sanitize_services( wp_unslash( $_POST[ $meta ] ) ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- icwcu_sanitize_services validates each row and sanitizes all allowed fields. 138 70 } 139 } else { 71 } 72 foreach ( array( 'icwcu_custom_services_title', 'icwcu_custom_radioservices_title' ) as $meta ) { 73 if ( isset( $_POST[ $meta ] ) && is_string( $_POST[ $meta ] ) ) { 74 update_post_meta( $post_id, $meta, sanitize_text_field( wp_unslash( $_POST[ $meta ] ) ) ); 75 } 76 } 77 } 78 public function add_custom_fields() { 79 global $product; 80 if ( ! $product instanceof WC_Product ) { 140 81 return; 141 82 } 142 143 if ( isset( $_POST['icwcu_custom_services'] ) ) { 144 $custom_services = wp_unslash( $_POST['icwcu_custom_services'] ); 145 if ( is_array( $custom_services ) ) { 146 array_walk_recursive( $custom_services, 'icwcu_sanitize_service_item' ); 147 // Filter out empty services 148 $custom_services = array_filter( $custom_services, function( $service ) { 149 return ! empty( $service['name'] ); 150 } ); 151 } else { 152 $custom_services = array(); 83 foreach ( array( 'checkbox', 'radio' ) as $type ) { 84 $services = icwcu_get_product_services( $product->get_id(), $type ); 85 if ( ! $services ) { 86 continue; 153 87 } 154 update_post_meta( $post_id, 'icwcu_custom_services', $custom_services ); 155 } 156 157 if ( isset( $_POST['icwcu_custom_servicesradio'] ) ) { 158 $custom_servicesradio = wp_unslash( $_POST['icwcu_custom_servicesradio'] ); 159 if ( is_array( $custom_servicesradio ) ) { 160 array_walk_recursive( $custom_servicesradio, 'icwcu_sanitize_service_item' ); 161 $custom_servicesradio = array_filter( $custom_servicesradio, function( $service ) { 162 return ! empty( $service['name'] ); 163 } ); 164 } else { 165 $custom_servicesradio = array(); 88 $nonce = 'icwcu_custom_' . $type . '_nonce'; 89 wp_nonce_field( $nonce . '_action', $nonce, false ); 90 $title_key = 'radio' === $type ? 'icwcu_custom_radioservices_title' : 'icwcu_custom_services_title'; 91 $title = get_post_meta( $product->get_id(), $title_key, true ); 92 echo '<fieldset class="icwcu-custom-services"><legend>' . esc_html( $title ? $title : __( 'Additional Services', 'custom-checkbox-ultimate-for-woocommerce' ) ) . '</legend>'; 93 if ( 'radio' === $type ) { 94 echo '<label class="icwcu-service"><input type="radio" name="icwcu_radio_service" value="" checked> ' . esc_html__( 'No additional radio service', 'custom-checkbox-ultimate-for-woocommerce' ) . '</label>'; 166 95 } 167 update_post_meta( $post_id, 'icwcu_custom_servicesradio', $custom_servicesradio ); 168 } 169 170 171 if ( isset( $_POST['icwcu_custom_services_title'] ) ) { 172 update_post_meta( 173 $post_id, 174 'icwcu_custom_services_title', 175 sanitize_text_field( wp_unslash( $_POST['icwcu_custom_services_title'] ) ) 176 ); 177 } 178 if ( isset( $_POST['icwcu_custom_radioservices_title'] ) ) { 179 update_post_meta( 180 $post_id, 181 'icwcu_custom_radioservices_title', 182 sanitize_text_field( wp_unslash( $_POST['icwcu_custom_radioservices_title'] ) ) 183 ); 184 } 185 } 186 187 public function add_custom_fields() { 188 global $post; 189 $custom_services = get_post_meta( $post->ID, 'icwcu_custom_services', true ); 190 $custom_servicesradio = get_post_meta( $post->ID, 'icwcu_custom_servicesradio', true ); 191 192 wp_nonce_field( 'icwcu_custom_checkbox_nonce_action', 'icwcu_custom_checkbox_nonce' ); 193 wp_nonce_field( 'icwcu_custom_radio_nonce_action', 'icwcu_custom_radio_nonce' ); 194 if ( ! is_array( $custom_services ) ) { 195 $custom_services = array(); 196 } 197 if ( ! is_array( $custom_servicesradio ) ) { 198 $custom_servicesradio = array(); 199 } 200 201 if ( ! empty( $custom_services ) ) { 202 echo '<div class="icwcu-custom-services">'; 203 $custom_services_title = get_post_meta( $post->ID, 'icwcu_custom_services_title', true ); 204 if ( ! empty( $custom_services_title ) ) { 205 echo '<h4 class="icwcu-checkbox-title">' . esc_html( $custom_services_title ) . '</h4><table>'; 206 } else { 207 echo '<h4 class="icwcu-checkbox-title">' . esc_html__( 'Additional Services', 'custom-checkbox-ultimate-for-woocommerce' ) . '</h4><table>'; 96 foreach ( $services as $key => $service ) { 97 $id = 'icwcu-' . $type . '-' . $key; 98 $name = 'radio' === $type ? 'icwcu_radio_service' : 'icwcu_services[]'; 99 echo '<div class="icwcu-service"><label for="' . esc_attr( $id ) . '"><input id="' . esc_attr( $id ) . '" type="' . esc_attr( $type ) . '" name="' . esc_attr( $name ) . '" value="' . esc_attr( $key ) . '" aria-describedby="' . esc_attr( $id . '-description' ) . '"> ' . esc_html( $service['name'] ) . '</label> <span class="icwcu-currency">' . wp_kses_post( wc_price( wc_get_price_to_display( $product, array( 'price' => $service['price'] ) ) ) ) . '</span><small id="' . esc_attr( $id . '-description' ) . '" class="icwcu-service-description">' . esc_html( $service['desc'] ) . '</small></div>'; 208 100 } 209 foreach ( $custom_services as $service ) { 210 if ( ! empty( $service['name'] ) && ! empty( $service['price'] ) ) { 211 echo '<tr><td><label><input type="checkbox" class="icwcu-checkbox" name="' . esc_html( $service['name'] ) . '" value="' . esc_attr( $service['price'] ) . '">' . esc_html( $service['name'] ); 212 213 if ( ! empty( $service['desc'] ) ) { 214 // echo '<span class="icwcu-tooltip">'.wp_kses(icwcu_get_frontend_svg_icon()).'<span class="icwcu-tooltiptext">' . esc_html( $service['desc'] ) . '</span></span></td>'; 215 echo '<span class="icwcu-tooltip">' . 216 wp_kses( icwcu_get_frontend_svg_icon(), array( 217 'svg' => array( 218 'xmlns' => array(), 219 'width' => array(), 220 'height' => array(), 221 'fill' => array(), 222 'class' => array(), 223 'viewBox' => array(), 224 ), 225 'path' => array( 226 'd' => array(), 227 ), 228 ) ) . 229 '<span class="icwcu-tooltiptext">' . esc_html( $service['desc'] ) . '</span></span>'; 230 231 } 232 233 echo '<td></td><td><span class="icwcu-currency">' . esc_html( get_woocommerce_currency_symbol() ) . esc_attr( $service['price'] ) . '</span></td>'; 234 echo '</label></tr>'; 235 } 236 } 237 echo '</table></div>'; 238 } 239 240 if ( ! empty( $custom_servicesradio ) ) { 241 echo '<div class="icwcu-custom-services">'; 242 $custom_services_title = get_post_meta( $post->ID, 'icwcu_custom_radioservices_title', true ); 243 if ( ! empty( $custom_services_title ) ) { 244 echo '<h4 class="icwcu-checkbox-title">' . esc_html( $custom_services_title ) . '</h4><table>'; 245 } else { 246 echo '<h4 class="icwcu-checkbox-title">' . esc_html__( 'Additional Services', 'custom-checkbox-ultimate-for-woocommerce' ) . '</h4><table>'; 247 } 248 foreach ( $custom_servicesradio as $serviceradio ) { 249 if ( ! empty( $serviceradio['name'] ) && ! empty( $serviceradio['price'] ) ) { 250 echo '<tr><td><label><input type="radio" class="icwcu-radio" data-group="icwcupayment" name="' . esc_html( sanitize_title( $serviceradio['name'] ) ) . '" value="' . esc_attr( $serviceradio['price'] ) . '">' . esc_html( $serviceradio['name'] ); 251 252 if ( ! empty( $serviceradio['desc'] ) ) { 253 // echo '<span class="icwcu-tooltip">'.wp_kses(icwcu_get_frontend_svg_icon()).'<span class="icwcu-tooltiptext">' . esc_html( $serviceradio['desc'] ) . '</span></span></td>'; 254 echo '<span class="icwcu-tooltip">' . 255 wp_kses( icwcu_get_frontend_svg_icon(), array( 256 'svg' => array( 257 'xmlns' => array(), 258 'width' => array(), 259 'height' => array(), 260 'fill' => array(), 261 'class' => array(), 262 'viewBox' => array(), 263 ), 264 'path' => array( 265 'd' => array(), 266 ), 267 ) ) . 268 '<span class="icwcu-tooltiptext">' . esc_html( $serviceradio['desc'] ) . '</span></span>'; 269 270 } 271 272 echo '<td></td><td><span class="icwcu-currency">' . esc_html( get_woocommerce_currency_symbol() ) . esc_attr( $serviceradio['price'] ) . '</span></td>'; 273 echo '</label></tr>'; 274 } 275 } 276 echo '</table></div>'; 101 echo '</fieldset>'; 277 102 } 278 103 } -
custom-checkbox-ultimate-for-woocommerce/tags/1.4/includes/class-icwcu-order.php
r3264079 r3759386 6 6 class ICWCU_Order { 7 7 public function __construct() { 8 add_action( 'woocommerce_checkout_create_order_line_item', array( $this, 'add_custom_fields_to_order_items' ), 10, 4 );9 add_action( 'woocommerce_checkout_create_order_line_item', array( $this, 'add_ customradio_fields_to_order_items' ), 10, 4 );8 // Runs for both classic checkout and the Checkout block (Store API). 9 add_action( 'woocommerce_checkout_create_order_line_item', array( $this, 'add_services_to_order_item' ), 10, 4 ); 10 10 } 11 12 public function add_custom_fields_to_order_items( $item, $cart_item_key, $values, $order ) { 13 $custom_services = get_post_meta( $values['product_id'], 'icwcu_custom_services', true ); 14 if ( ! is_array( $custom_services ) ) { 15 $custom_services = array(); 16 } 17 18 if ( ! empty( $custom_services ) ) { 19 foreach ( $custom_services as $service ) { 20 $service_key = sanitize_title( $service['name'] ); 21 if ( isset( $values[ $service_key ] ) ) { 22 $item->add_meta_data( $service['name'], get_woocommerce_currency_symbol() . $values[ $service_key ] ); 23 } 24 } 25 } 26 } 27 28 public function add_customradio_fields_to_order_items( $item, $cart_item_key, $values, $order ) { 29 $custom_servicesradio = get_post_meta( $values['product_id'], 'icwcu_custom_servicesradio', true ); 30 if ( ! is_array( $custom_servicesradio ) ) { 31 $custom_servicesradio = array(); 32 } 33 34 if ( ! empty( $custom_servicesradio ) ) { 35 foreach ( $custom_servicesradio as $serviceradio ) { 36 $service_keyradio = sanitize_title( $serviceradio['name'] ); 37 if ( isset( $values[ $service_keyradio ] ) ) { 38 $item->add_meta_data( $serviceradio['name'], get_woocommerce_currency_symbol() . $values[ $service_keyradio ] ); 39 } 40 } 11 12 public function add_services_to_order_item( $item, $cart_item_key, $values, $order ) { 13 foreach ( ICWCU_Cart::get_cart_item_services( $values ) as $service ) { 14 $item->add_meta_data( $service['name'], icwcu_format_price( $service['price'] ) ); 41 15 } 42 16 } -
custom-checkbox-ultimate-for-woocommerce/tags/1.4/includes/class-icwcu-settings.php
r3264079 r3759386 47 47 global $post; 48 48 49 if ( is_product()) {49 if ( function_exists( 'is_product' ) && is_product() && $post instanceof WP_Post ) { 50 50 $custom_servicesradio = get_post_meta( $post->ID, 'icwcu_custom_servicesradio', true ); 51 51 52 52 if ( ! empty( $custom_servicesradio ) ) { 53 wp_enqueue_script( 'icwcu-radio-script', plugin_dir_url( __FILE__ ) . '../assets/js/custom-checkbox-frontend.js', array( 'jquery' ), ICWCU_PLUGIN_VERSION, true);53 wp_enqueue_script( 'icwcu-radio-script', plugin_dir_url( __FILE__ ) . '../assets/js/custom-checkbox-frontend.js', array(), ICWCU_PLUGIN_VERSION, array( 'in_footer' => true, 'strategy' => 'defer' ) ); 54 54 } 55 55 } -
custom-checkbox-ultimate-for-woocommerce/tags/1.4/includes/enqueue.php
r3264079 r3759386 1 1 <?php 2 /* 3 Plugin Name: Custom Checkbox Ultimate for WooCommerce 4 * 5 */ 2 defined( 'ABSPATH' ) || exit; 6 3 7 if ( ! defined( 'ABSPATH' ) ) { 8 exit; // Exit if accessed directly 9 } 10 /* 11 Enqueue Styles 12 */ 13 // Enqueue custom SVG icon 14 add_action('admin_enqueue_scripts', 'icwcu_enqueue_custom_svg_icon'); 15 function icwcu_enqueue_custom_svg_icon() { 16 wp_enqueue_style('icwcu-custom-svg-icon', plugin_dir_url(__FILE__) . '../assets/css/icwcu-admin-styling.css', array(), ICWCU_PLUGIN_VERSION ); 17 4 add_action( 'admin_enqueue_scripts', 'icwcu_enqueue_admin_styles' ); 5 function icwcu_enqueue_admin_styles( $hook ) { 6 $screen = get_current_screen(); 7 if ( 'toplevel_page_ic-woo-ultimate' === $hook || ( $screen && 'product' === $screen->post_type ) ) { 8 wp_enqueue_style( 'icwcu-admin-style', plugins_url( 'assets/css/icwcu-admin-styling.css', ICWCU_PLUGIN_FILE ), array(), ICWCU_PLUGIN_VERSION ); 9 } 18 10 } 19 11 20 // Enqueue custom CSS for frontend product page 21 add_action('wp_enqueue_scripts', 'icwcu_enqueue_custom_css'); 12 add_action( 'wp_enqueue_scripts', 'icwcu_enqueue_custom_css' ); 22 13 function icwcu_enqueue_custom_css() { 23 // Check if WooCommerce is active 24 if (class_exists('WooCommerce') && function_exists('is_product') && is_product()) { 25 wp_enqueue_style('icwcu-custom-services-style', plugin_dir_url(__FILE__) . '../assets/css/icwcu-services-style.css', array(),ICWCU_PLUGIN_VERSION ); 14 if ( function_exists( 'is_product' ) && is_product() ) { 15 wp_enqueue_style( 'icwcu-custom-services-style', plugins_url( 'assets/css/icwcu-services-style.css', ICWCU_PLUGIN_FILE ), array(), ICWCU_PLUGIN_VERSION ); 26 16 } 27 17 } 28 function icwcu_enqueue_admin_styles($hook) {29 // Check if we are on the plugin's dashboard page30 if ( 'toplevel_page_ic-woo-ultimate' != $hook ) {31 return;32 }33 34 // Enqueue the admin CSS file35 wp_enqueue_style(36 'icwcu-admin-style',37 plugin_dir_url(__FILE__) . '../assets/css/icwcu-admin-styling.css', // Path to your CSS file38 [],39 ICWCU_PLUGIN_VERSION // Version40 );41 }42 add_action( 'admin_enqueue_scripts', 'icwcu_enqueue_admin_styles' ); -
custom-checkbox-ultimate-for-woocommerce/tags/1.4/includes/helpers.php
r3264079 r3759386 1 1 <?php 2 /*3 * Plugin Name: Custom Checkbox Ultimate for WooCommerce4 *5 */6 2 if ( ! defined( 'ABSPATH' ) ) { 7 3 exit; … … 49 45 } 50 46 } 47 48 if ( ! function_exists( 'icwcu_get_product_services' ) ) { 49 /** 50 * Get the saved services of a product. 51 * 52 * @param int $product_id Product ID. 53 * @param string $type 'checkbox' or 'radio'. 54 * @return array Services keyed by slug: array( 'name' => string, 'price' => float, 'desc' => string ). 55 */ 56 function icwcu_get_product_services( $product_id, $type = 'checkbox' ) { 57 $meta_key = ( 'radio' === $type ) ? 'icwcu_custom_servicesradio' : 'icwcu_custom_services'; 58 $services = get_post_meta( $product_id, $meta_key, true ); 59 $result = array(); 60 61 if ( ! is_array( $services ) ) { 62 return $result; 63 } 64 65 foreach ( icwcu_sanitize_services( $services ) as $index => $service ) { 66 if ( ! is_array( $service ) || empty( $service['name'] ) ) { 67 continue; 68 } 69 $key = sanitize_title( $service['name'] ); 70 if ( '' === $key ) { 71 continue; 72 } 73 if ( isset( $result[ $key ] ) ) { 74 $key .= '-' . sanitize_key( (string) $index ); 75 } 76 $result[ $key ] = array( 77 'name' => (string) $service['name'], 78 'price' => isset( $service['price'] ) ? max( 0, (float) $service['price'] ) : 0, 79 'desc' => isset( $service['desc'] ) ? (string) $service['desc'] : '', 80 ); 81 } 82 83 return $result; 84 } 85 } 86 87 /** Normalize submitted rows, removing gaps and rejecting malformed fields. */ 88 function icwcu_sanitize_services( $rows ) { 89 $result = array(); 90 if ( ! is_array( $rows ) ) { 91 return $result; 92 } 93 foreach ( $rows as $row ) { 94 if ( ! is_array( $row ) || ! isset( $row['name'] ) || ! is_string( $row['name'] ) ) { 95 continue; 96 } 97 $name = sanitize_text_field( $row['name'] ); 98 if ( '' === trim( $name ) ) { 99 continue; 100 } 101 $price = isset( $row['price'] ) && is_scalar( $row['price'] ) ? (float) wc_format_decimal( $row['price'] ) : 0; 102 $result[ count( $result ) + 1 ] = array( 103 'name' => $name, 104 'price' => is_finite( $price ) ? max( 0, $price ) : 0, 105 'desc' => isset( $row['desc'] ) && is_string( $row['desc'] ) ? sanitize_textarea_field( $row['desc'] ) : '', 106 ); 107 } 108 return $result; 109 } 110 111 if ( ! function_exists( 'icwcu_format_price' ) ) { 112 /** 113 * Format a price as plain text (safe for cart item data and order item meta). 114 * 115 * @param float $price Price. 116 * @return string 117 */ 118 function icwcu_format_price( $price ) { 119 return html_entity_decode( wp_strip_all_tags( wc_price( $price ) ), ENT_QUOTES, 'UTF-8' ); 120 } 121 } -
custom-checkbox-ultimate-for-woocommerce/tags/1.4/includes/plugin-info.php
r3264079 r3759386 1 1 <?php 2 if ( ! defined( 'ABSPATH' ) ) { 3 exit; 4 } 2 defined( 'ABSPATH' ) || exit; 5 3 ?> 6 <div class="wrap"> 7 <h1><?php esc_html_e( 'Custom Checkbox Ultimate for WooCommerce Documentation', 'custom-checkbox-ultimate-for-woocommerce' ); ?></h1> 8 9 <h2><?php esc_html_e( 'Introduction', 'custom-checkbox-ultimate-for-woocommerce' ); ?></h2> 10 <p><?php esc_html_e( 'Custom Checkbox Ultimate for WooCommerce is a plugin that allows you to add customizable checkbox options on WooCommerce product pages for additional services and charges.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></p> 11 12 <h2><?php esc_html_e( 'Installation', 'custom-checkbox-ultimate-for-woocommerce' ); ?></h2> 4 <div class="wrap icwcu-guide"> 5 <h1><?php esc_html_e( 'Custom Checkbox Ultimate: setup guide', 'custom-checkbox-ultimate-for-woocommerce' ); ?></h1> 6 <p><?php esc_html_e( 'Offer optional services such as gift wrapping, assembly, or premium packaging. Configure each product in the classic WooCommerce product editor.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></p> 7 <h2><?php esc_html_e( '1. Configure a product', 'custom-checkbox-ultimate-for-woocommerce' ); ?></h2> 13 8 <ol> 14 <li><?php esc_html_e( 'Navigate to Plugins > Add New in your WordPress dashboard.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></li> 15 <li><?php esc_html_e( 'Click on "Upload Plugin" and select the plugin ZIP file.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></li> 16 <li><?php esc_html_e( 'Click "Install Now" and then "Activate" to enable the plugin.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></li> 9 <li><?php esc_html_e( 'Install and activate WooCommerce, then open Products > All Products and edit a simple or variable product.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></li> 10 <li><?php esc_html_e( 'Find Custom Services - IC Woo Ultimate below the editor. If it is hidden, enable it in Screen Options.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></li> 11 <li><?php esc_html_e( 'In Checkbox Type, enter a group title and services customers can combine. In Radio Type, enter alternatives where customers choose at most one.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></li> 12 <li><?php esc_html_e( 'For each row, enter a service name, a non-negative price in the store currency, and an optional description. Zero-priced services are supported. Blank names are ignored.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></li> 13 <li><?php esc_html_e( 'Use Add More Fields for additional rows. To remove a service, clear its name. Update or publish the product.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></li> 17 14 </ol> 18 19 <h2><?php esc_html_e( 'Setup', 'custom-checkbox-ultimate-for-woocommerce' ); ?></h2> 20 <ol> 21 <li><?php esc_html_e( 'Go to WooCommerce > Settings > Custom Checkbox Ultimate.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></li> 22 <li><?php esc_html_e( 'Configure the default settings for your checkboxes, such as label text, default state, and additional costs.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></li> 23 <li><?php esc_html_e( 'Save your settings.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></li> 24 </ol> 25 26 <h2><?php esc_html_e( 'Usage', 'custom-checkbox-ultimate-for-woocommerce' ); ?></h2> 27 <ol> 28 <li><?php esc_html_e( 'Edit the product where you want to add a custom checkbox.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></li> 29 <li><?php esc_html_e( 'In the product data section, navigate to the "Custom Checkbox" tab.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></li> 30 <li><?php esc_html_e( 'Enable the checkbox option and customize the label, description, and additional cost as needed.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></li> 31 <li><?php esc_html_e( 'Update or publish the product to apply the changes.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></li> 32 </ol> 33 34 <h2><?php esc_html_e( 'Support', 'custom-checkbox-ultimate-for-woocommerce' ); ?></h2> 35 <p><?php esc_html_e( 'For further assistance, please visit our support page or contact us at support@insertcart.com.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></p> 15 <h2><?php esc_html_e( '2. Test the customer journey', 'custom-checkbox-ultimate-for-woocommerce' ); ?></h2> 16 <p><?php esc_html_e( 'Open the product page, select services, choose a variation if applicable, and add the product to the cart. Radio services include a no-service option. Selections are optional and are not preselected.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></p> 17 <p><?php esc_html_e( 'The product headline price does not change before adding to cart. In the cart, selected service costs are added to each product unit. For a product priced at 20 with a service priced at 5, quantity 2 has a subtotal of 50 before tax, discounts, and shipping. Taxes and coupons follow the product settings in WooCommerce.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></p> 18 <h2><?php esc_html_e( '3. Review orders', 'custom-checkbox-ultimate-for-woocommerce' ); ?></h2> 19 <p><?php esc_html_e( 'Selected service names and unit costs appear beneath the product in the cart and checkout. After checkout, find them on the order line item in WooCommerce > Orders. They also appear in standard WooCommerce order details and emails. Refunds use the product line amount; services are not separate fee lines.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></p> 20 <h2><?php esc_html_e( 'Compatibility and limitations', 'custom-checkbox-ultimate-for-woocommerce' ); ?></h2> 21 <p><?php esc_html_e( 'Variable products share services configured on the parent product. Existing selections work with classic and block cart/checkout and with HPOS. Service selection requires the standard WooCommerce product add-to-cart form; the block Add to Cart form and direct Store API selection are not provided. Shop-loop quick add does not collect services. Grouped, external, subscription, bundle, and custom product forms need separate testing.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></p> 22 <h2><?php esc_html_e( 'Troubleshooting and privacy', 'custom-checkbox-ultimate-for-woocommerce' ); ?></h2> 23 <p><?php esc_html_e( 'If options are missing, confirm that service names are saved and the theme renders the standard add-to-cart hook. If selections are missing from the cart, refresh the product page and exclude cart and checkout from full-page caching. Test theme and add-on conflicts on a staging site. After changing prices, remove the old cart line and add the product again.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></p> 24 <p><?php esc_html_e( 'The plugin sends no analytics or service data to an external server. Configuration is stored with products; selections are stored in WooCommerce carts and order line metadata. Deactivation preserves configuration and orders.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></p> 25 <p><a class="button" href="<?php echo esc_url( admin_url( 'edit.php?post_type=product' ) ); ?>"><?php esc_html_e( 'Open products', 'custom-checkbox-ultimate-for-woocommerce' ); ?></a> <a class="button" href="<?php echo esc_url( ICWCU_DOCUMENTATION_URL ); ?>" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'Online documentation', 'custom-checkbox-ultimate-for-woocommerce' ); ?></a> <a href="https://wordpress.org/support/plugin/custom-checkbox-ultimate-for-woocommerce/"><?php esc_html_e( 'Support forum', 'custom-checkbox-ultimate-for-woocommerce' ); ?></a></p> 36 26 </div> 37 -
custom-checkbox-ultimate-for-woocommerce/tags/1.4/readme.txt
r3264079 r3759386 1 1 === Custom Checkbox Ultimate for WooCommerce === 2 2 Contributors: sandy786 3 Tags: WooCommerce, checkbox, additional services, customizable options, e-commerce 4 Plugin URI: https://www.insertcart.com/insertcart-custom-checkbox-ultimate-for-woocommerce-documentation/ 5 Author URI: https://www.insertcart.com/ 3 Tags: woocommerce, checkbox, product add-ons, extra services, radio buttons 6 4 Requires at least: 6.3 7 Tested up to: 6.75 Tested up to: 7.1 8 6 Requires PHP: 7.2 9 Stable tag: 1.3 10 License: GPL v2 or later 7 Requires Plugins: woocommerce 8 Stable tag: 1.4 9 License: GPLv2 or later 11 10 License URI: https://www.gnu.org/licenses/gpl-2.0.html 12 11 13 Add customizable checkbox options on WooCommerce product pages for additional services and charges.12 Add optional checkbox and radio services to WooCommerce products, with per-unit prices and service details in the cart and orders. 14 13 15 14 == Description == 16 Custom Checkbox Ultimate for Woocommerce is a powerful WooCommerce extension that allows store owners to offer additional services and charges to customers through customizable checkboxes on product pages. Enhance your product offerings by enabling customers to select extra features, services, or options, seamlessly integrating additional costs into their purchases.17 15 18 == Key Features == 16 Offer gift wrapping, assembly, premium packaging, and other optional services on WooCommerce product pages. Configure services for each product in the classic product editor. 19 17 20 **Customizable Checkbox Options:** Easily add multiple checkbox fields to your WooCommerce product pages for additional services. 21 **Dynamic Pricing:** Automatically adjust product prices based on selected additional services. 22 **Service Descriptions:** Provide detailed descriptions for each additional service using tooltips for better customer understanding. 23 **Admin-Friendly Interface:** Manage and configure additional services directly from the product edit screen with an intuitive meta box. 24 **Order Integration:** Ensure that selected services and their costs are accurately reflected in the cart, checkout, and order details. 25 **Flexible Field Management:** Add as many custom service fields as needed with ease. 18 * Checkbox services: customers can select several options. 19 * Radio services: customers choose at most one option or no additional service. 20 * Set a name, non-negative price, and description for each service. Free services are supported. 21 * Start with five rows per type and add more when needed. 22 * Service prices come from saved product settings, never customer-submitted prices. 23 * Selected services increase the product unit price and appear in the cart, checkout, order details, and standard emails. 24 * Works with simple products and parent-level services on variable products. 25 * Supports HPOS and classic or block cart/checkout for items added through the standard product form. 26 26 27 Check out our [Documentation](https://www.insertcart.com/insertcart-custom-checkbox-ultimate-for-woocommerce-documentation/), [Support Ticket](https://www.insertcart.com/support-ticket/), and [Contact us](https://www.insertcart.com/contact-us/).27 Descriptions are displayed beside the service options, including for keyboard and touch users. The product headline price does not update as options are selected; the adjusted price appears in the cart. 28 28 29 = =Installation==29 = Documentation and first setup = 30 30 31 Upload the Plugin: 31 Open **IC Woo Ultimate** in the WordPress dashboard for the included setup guide. You can also use **Plugins > Setup guide** or the guide link inside the Custom Services product meta box. The included guide works without an external website. 32 32 33 * Download the plugin zip file. 34 * In your WordPress dashboard, navigate to Plugins > Add New. 35 * Click on the Upload Plugin button at the top. 36 * Choose the downloaded zip file and click Install Now. 37 * Activate the Plugin: 38 * After installation, click on Activate Plugin. 39 * Ensure WooCommerce is Active: 33 [Online documentation](https://www.insertcart.com/insertcart-custom-checkbox-ultimate-for-woocommerce-documentation/) | [Support forum](https://wordpress.org/support/plugin/custom-checkbox-ultimate-for-woocommerce/) | [Contact InsertCart](https://www.insertcart.com/contact-us/) 40 34 41 This plugin requires WooCommerce to be installed and activated. 42 If WooCommerce is not active, you will receive an admin notice prompting you to activate it. 35 = How pricing works = 43 36 44 ==Quick Start== 37 Services are charged per product unit, in the store currency. A product priced at 20 with a service priced at 5 has a unit price of 25. Quantity 2 has a subtotal of 50 before taxes, discounts, and shipping. WooCommerce applies product taxes and coupons to the combined product price. Services are not separate cart fees. 45 38 46 * Adding Custom Services to a Product 39 = Compatibility limits = 47 40 48 Navigate to Products: 41 Service selection uses the standard WooCommerce product add-to-cart form. This release does not provide selection controls for the block Add to Cart form, direct Store API additions, shop-loop quick add, or third-party custom product forms. Variable products share the parent product's services. Test grouped, external, bundled, and subscription products separately. 49 42 50 * In your WordPress dashboard, go to Products > All Products. 51 * Click on the product you want to add additional services to, or create a new product. 52 * Locate the Custom Services Meta Box: 53 * Scroll down to find the Custom Services – IC Woo Ultimate meta box. 43 = Privacy = 54 44 55 ==Configure the Services== 45 No analytics or service data is sent to an external server. Product settings are stored in WordPress product metadata. Selections are stored in WooCommerce carts and order item metadata. Deactivation preserves settings and historical orders. 56 46 57 **Custom Services Title:** Enter a title that will appear above the checkbox options on the product page (e.g., "Add-Ons", "Extra Services"). 58 **Service Fields:** By default, five service fields are available. For each service, provide: 59 **Service Name:** The label for the checkbox (e.g., "Gift Wrapping"). 60 **Service Price:** The additional cost associated with the service. 61 **Service Description: (Optional)** A brief description that appears as a tooltip for more information. 62 **Add More Fields:** Click the **+Add More Fields** button to add additional service options beyond the default five. 63 **Save the Product:** Once configured, click Update or Publish to save your changes. 47 == Installation == 64 48 65 Displaying Custom Services on the Frontend 66 On the product page, customers will see the Custom Services Title followed by a list of checkbox options with their respective prices.67 Selecting a checkbox will automatically update the product price in the cart to include the additional service cost.68 Service descriptions are available as tooltips for more detailed information.69 Managing Orders 70 Selected additional services and their costs are displayed in the cart, checkout, and order details, ensuring transparency and accurate billing.49 1. Install and activate WooCommerce. 50 2. Install Custom Checkbox Ultimate for WooCommerce from Plugins > Add New, or upload the plugin ZIP. 51 3. Activate the plugin and open IC Woo Ultimate for the included guide. 52 4. Edit a product and locate Custom Services - IC Woo Ultimate. If hidden, enable it in Screen Options. 53 5. Configure Checkbox Type and/or Radio Type, then update the product. 54 6. Test the product page, cart, checkout, and a sample order before using it on your live store. 71 55 56 == Frequently Asked Questions == 72 57 58 = Where are the service settings? = 59 Edit a product in the classic product editor and use Custom Services - IC Woo Ultimate. There is no global WooCommerce settings tab for configuring service rows. 60 61 = Can I add more than five services? = 62 Yes. Use Add More Fields in either type. Blank service names are ignored. Clear the name and update the product to remove a service. 63 64 = Are services required or preselected? = 65 No. Checkboxes start unchecked. Radio services include a no-service choice. 66 67 = Can services be free? = 68 Yes. Enter 0 as the service price. Negative prices are not supported. 69 70 = Do services work with variations? = 71 Yes. Configure services on the parent product; they are added to the chosen variation's unit price. Per-variation service definitions are not provided. 72 73 = Why is the product page price unchanged? = 74 This release shows each service price beside its option. The product's combined price is calculated after adding it to the cart. 75 76 = Why are selections missing? = 77 Check that your theme renders the standard add-to-cart form and refresh the product page to obtain a fresh form nonce. Exclude cart and checkout from page caching. Shop-loop quick add and block product forms do not collect selections. 78 79 = What happens when a service price changes? = 80 New additions use the saved product price. Existing 1.4 cart selections retain their recorded service amount; remove and re-add the product to use new settings. Legacy 1.3 cart entries are resolved against current settings. Historical orders retain their stored line item details. 73 81 74 82 == Screenshots == 75 83 76 1. Checkbox you can add fields and services name with price and description. 77 2. Radio button you can select only one of theme. 78 3. Pricing will display on WooCommerce Product Page. 79 4. Check screen option in product page to hide and show plugin settings 80 81 82 ===Frequently Asked Questions=== 83 84 = Does this plugin work with all WooCommerce themes? = 85 Yes, the plugin is designed to be compatible with most WooCommerce-compatible themes. However, styling may vary based on your theme. You can use custom CSS to adjust the appearance as needed. 86 87 = Can I add more than five custom service fields? = 88 Absolutely! While the plugin initializes with five fields by default, you can add as many additional service fields as required using the + Add More Fields button in the meta box. 89 90 = Is it possible to set different prices for different user roles? = 91 By default, the plugin does not support role-based pricing. However, with custom development or additional extensions, this functionality can be achieved. 92 93 = How are the additional service costs handled during checkout? = 94 Selected additional services are added to the cart item data, ensuring that the total price reflects the added costs. These details are visible in the cart, checkout, and order summaries. 95 96 = What happens if WooCommerce is deactivated? = 97 If WooCommerce is not active, the plugin will display an admin notice prompting you to activate WooCommerce, as it is a required dependency. 98 99 = More Features = 100 Working on more features and settings like Shortcodes, Styling options etc & more. [Contact us](https://www.insertcart.com/contact-us/) for more requests. 84 1. Checkbox service fields with names, prices, and descriptions, rendered with a sample product. 85 2. Radio service fields with alternative choices and an additional row. 86 3. Product service options, descriptions, prices, and the no-service radio choice. 87 4. The setup guide included in version 1.4. Screenshots use actual plugin output in a local test harness. 101 88 102 89 == Changelog == 103 1.3 104 Initial release of Custom Checkbox Ultimate for WooCommerce. 105 Features include adding customizable checkbox options, dynamic pricing adjustments, service descriptions with tooltips, and order integration. 106 Upgrade Notice 107 1.0 108 Initial release. 109 License 110 This plugin is licensed under the GPL v2 or later. You can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or any later version. 90 91 = 1.4 = 92 * Security: use saved service prices instead of submitted form prices; validate malformed requests and product editor fields. 93 * Fix checkbox names containing spaces or capital letters and group radio choices correctly. 94 * Keep add-on prices on cart lines and avoid adding charges twice during repeated totals calculations. 95 * Preserve existing services when saved rows contain gaps and support free services. 96 * Improve accessible service descriptions and provide an explicit no-radio-service option. 97 * Add an accurate built-in setup guide, product help link, Plugins list links, and a one-time introduction for store managers. 98 * Guard against missing WooCommerce and limit admin styling to plugin/product screens. 99 * Declare HPOS and Cart/Checkout block compatibility for selections made on standard product forms. 100 * Verified locally with WordPress 7.1.3 and WooCommerce 10.3.5. 101 102 = 1.3 = 103 * Initial release with checkbox and radio services, service descriptions, and cart/order integration. 111 104 112 105 == Upgrade Notice == 113 106 114 No major requirements 107 = 1.4 = 108 Security and reliability update. Service prices now come from saved product settings. Includes a setup guide and improved cart/order handling. Test on staging before updating your live store. -
custom-checkbox-ultimate-for-woocommerce/trunk/assets/css/icwcu-admin-styling.css
r3264079 r3759386 1 div#icwcu_custom_services_meta_box h2 { 2 position: relative; 3 display: inline-block; 4 } 5 6 div#icwcu_custom_services_meta_box h2 svg { 7 float: left; 8 height: 20px; 9 } 10 .icwcu-custom-service-field svg { 11 vertical-align: middle; 12 margin-right: 5px; 13 } 14 .icwcu-custom-service-field textarea{ 15 vertical-align:middle; 16 } 17 18 .menu-icon-ic-woo-ultimate svg { 19 vertical-align: middle; 20 margin-right: 10px; 21 } 22 .icwcu-custom-service-field label { 23 width: 144px; 24 display: inline-block; 25 padding: 0px 20px; 26 } 27 .icwcu-custom-service-field { 28 border: 1px solid #ddd; 29 margin: 10px; 30 } 31 button#icwcu-add-more-fields:hover { 32 background: #167fae; 33 } 34 button#icwcu-add-more-fields { 35 margin: 10px; 36 background: #03A9F4; 37 color: #fff; 38 border: 0; 39 } 40 button#icwcu-add-moreradio-fields:hover { 41 background: #167fae; 42 } 43 button#icwcu-add-moreradio-fields { 44 margin: 10px; 45 background: #03A9F4; 46 color: #fff; 47 border: 0; 48 } 49 50 51 .icwcu-admin-notice-wrapper { 52 padding: 20px; } 53 .icwcu-admin-notice-wrapper h2 { 54 margin-top: 0; } 55 .icwcu-admin-notice-wrapper p { 56 font-size: 14px; } 57 .icwcu-admin-notice-wrapper .icwcu-admin-notice-primary-button { 58 padding: 5px 20px; 59 background: #67bc45; 60 color: #fff; 61 font-weight: normal; 62 text-decoration: none; 63 margin-top: 5px; 64 display: inline-block; 65 border-radius: 50px; } 66 67 .is-style-outline > .wp-block-button__link:not(.has-text-color), 68 .wp-block-button__link.is-style-outline:not(.has-text-color) { 69 color: var(--wp--preset--color--primary); } 70 71 .icwcu--getting-started--wrapper .icwcu-header-left > .fs-notice { 72 display: none !important; } 73 74 .icwcu-options-features > li span { 75 color: green; 76 margin-right: 20px; } 77 78 .icwcu-compare-table table td span { 79 font-size: 21px; 80 background: #f0f0f1; 81 border-radius: 100%; 82 padding: 5px; 83 color: #d72b2b; } 84 85 .icwcu-compare-table table td span.dashicons.dashicons-yes { 86 text-align: center; 87 color: #4caf50; } 88 89 .icwcu-about-container.icwcu-admin-card.icwcu-admin-card-header { 90 background: linear-gradient(45deg, #3F51B5, #E91E63); 91 justify-content: center; 92 align-items: center; } 93 94 .icwcu-about-side { 95 margin-top: 30px; } 96 #icwcu-admin-about-page p, #icwcu-admin-about-page { 97 font-size: 17px; 98 line-height: 1.7; 99 margin: 0 0 15px; } 100 101 .subscribe-panel li span { 102 margin-top: 6px; 103 margin-right: 10px; 104 color: #3fb345; } 105 106 #icwcu-admin-about-page { 107 margin: 20px; 108 max-width: 100%; } 109 110 .icwcu-about-container { 111 padding: 40px 60px; 112 border-bottom: 1px solid #ddd; 113 border-radius: 5px; 114 margin: 30px 0; 115 background: #fff; 116 box-shadow: 0 0 50px #dddddd7d; } 117 118 a.icwcu-admin-button.premium-button span.dashicons{ 119 vertical-align: middle; 120 } 121 a.icwcu-admin-button.premium-button { 122 background: #2196F3; 123 margin-left: 5px; } 124 125 h3.icwcu-admin-card-title { 126 font-size: 1.4rem !important; 127 text-transform: capitalize; 128 color: #fff; 129 margin-bottom: 0; 130 padding: 0; } 131 132 a.icwcu-admin-button { 133 padding: 12px 36px; 134 text-decoration: inherit; 135 background: #3e3541; 136 color: #fff; 137 border-radius: 50px; 138 margin-top: 20px; 139 display: inline-block; 140 font-size: 16px; 141 line-height: 1.75; 142 display: flex; 143 align-items: center; 144 gap: 4px; 145 display: inline-block; } 146 147 @keyframes spin { 148 100% { 149 -webkit-transform: rotate(360deg); 150 transform: rotate(360deg); } } 151 a.icwcu-admin-button.icwcu-importing-icwcu span { 152 animation: spin 4s linear infinite; } 153 154 a.icwcu-admin-button.primary.large { 155 background: #e68836; 156 margin-right: 9px; } 157 158 .feature-list { 159 display: grid; 160 grid-gap: 50px; 161 grid-template-columns: 3fr 1fr; 162 grid-template-rows: 1fr; 163 max-width: 100%; } 164 165 .icwcu-about-container.icwcu-about-content { 166 margin: 0; } 167 168 .icwcu-options-features > li { 169 display: -webkit-inline-flex; 170 display: -ms-inline-flexbox; 171 display: inline-flex; 172 -webkit-align-items: center; 173 -ms-flex-align: center; 174 align-items: center; 175 width: 100%; 176 float: left; 177 margin-bottom: 15px; 178 font-weight: 500; } 179 ul.icwcu-options-features b { 180 font-weight: 900; 181 margin-right: 10px; 182 } 183 .icwcu-options-features > li img { 184 margin-right: 5px; } 185 186 .icwcu-options-features { 187 list-style: none; 188 padding: 0; 189 margin: 0 0 30px; } 190 .icwcu-about-container.icwcu-compare-table, 191 .icwcu-admin-card.member { 192 display: none; 193 } 194 .icwcu-admin-card-title.is-sub { 195 margin-left: -30px; 196 margin-right: -30px; 197 padding: 0 30px 30px; 198 border-bottom: 1px solid #ebf2f5; 199 font-size: 2rem; 200 text-transform: capitalize; 201 margin-top: 0; 202 color: #000; } 203 204 .icwcu-about-side .icwcu-admin-card { 205 background-color: #fff; 206 padding: 30px; 207 margin-bottom: 30px; 208 background-repeat: no-repeat; 209 background-position: 100% 100%; 210 border-radius: 15px; 211 border: 1px solid #ebf2f5; 212 background: -webkit-linear-gradient(315deg, #0dcad2, #0060f3); 213 background: -o-linear-gradient(315deg, #0dcad2, #0060f3); 214 background: linear-gradient(135deg, #0dcad2, #0060f3); 215 color: #fff; } 216 217 h4.icwcu-admin-card-title.is-small { 218 color: #fff; 219 margin: 0; } 220 221 .icwcu-about-side .icwcu-admin-card a.icwcu-admin-button.large.icwcu-block.is-default { 222 background: #fff; 223 color: #000; } 224 225 .icwcu-admin-card.cart-two { 226 background: #fff; 227 color: #000; 228 margin: 0; } 229 230 #icwcu-admin-about-page .icwcu-about-side .icwcu-admin-card h3 { 231 font-size: 1.5rem; 232 color: #fff; } 233 234 #icwcu-admin-about-page .icwcu-admin-card.cart-two h3 { 235 color: #010101; } 236 237 #icwcu-admin-about-page div#setting-error-tgmpa { 238 display: none; } 239 240 .cart-two h3 { 241 color: #000; } 242 243 .icwcu-header-left h2 { 244 font-size: 3rem; 245 text-transform: capitalize; 246 margin: 0; 247 color: #fff; } 248 249 #icwcu-admin-about-page .icwcu-header-left p { 250 color: #fff; } 251 252 .admin-grid-2 { 253 display: grid; 254 grid-gap: 4%; 255 grid-template-columns: 48% 48%; 256 grid-template-rows: 1fr; 257 max-width: 100%; } 258 259 .admin-grid-3 { 260 display: grid; 261 grid-gap: 50px; 262 grid-template-columns: 1fr 1fr 1fr; 263 grid-template-rows: 1fr; 264 max-width: 100%; } 265 266 #subcription-email { 267 width: 500px; 268 /* justify-content: center; */ 269 display: flex; 270 padding: 10px; 271 border: 1px solid #f0f0f1; 272 border-radius: 50px; 273 position: relative; 274 display: grid; 275 /* grid-gap: 50px; */ 276 grid-template-columns: 2fr 1fr; 277 grid-template-rows: 1fr; 278 max-width: 100%; 279 box-shadow: 0 0 23px #0000001a; 280 margin: 22px auto; } 281 282 #subcription-email button.wp-block-search__button.has-icon { 283 background: #2960f3; 284 color: #fff; 285 border-radius: 50px; 286 border: none; 287 cursor: pointer; } 288 289 #subcription-email input[type="text"] { 290 border: none; 291 padding: 10px 25px; 292 background: #f1f1f1; 293 border-radius: 50px; } 294 295 #subcription-email input[type="text"] { 296 border: none; 297 padding: 10px 15px; 298 background: #fff; 299 border-radius: 50px; } 300 301 #icwcu-admin-about-page h2 { 302 font-size: 2.5rem; 303 line-height: 1.5; 304 margin: 0; } 305 306 #icwcu-admin-about-page h3 { 307 font-size: 2rem; 308 line-height: 1.5; 309 margin-bottom: 0; } 310 311 #icwcu-admin-about-page, 312 #icwcu-admin-about-page p { 313 color: #838383; 314 font-size: 15px; 315 line-height: 1.7; } 316 317 #icwcu-admin-about-page img { 318 max-width: 100%; 319 vertical-align: middle; } 320 321 .admin-col > img { 322 box-shadow: 0 15px 24px #0000001c; } 323 324 .icwcu-about-container.icwcu-admin-card.icwcu-admin-card-header { 325 text-align: center; } 326 327 .icwcu-card-header-button-group { 328 text-align: center; 329 display: flex; 330 align-items: center; 331 justify-content: center; } 332 333 a.icwcu-template-kit-admin-button:hover, 334 #icwcu-admin-about-page button.wp-block-search__button.has-icon:hover, 335 a.icwcu-template-kit-admin-button.primary.large:hover { 336 background: #4caf50; } 337 338 a.icwcu-admin-button.premium-button:hover { 339 background: #2960f3; } 340 341 .admin-col { 342 position: relative; } 343 344 .icwcu-compare-table-subtitle { 345 font-size: 16px; } 346 .icwcu-compare-table-subtitle a { 347 text-decoration: none; } 348 .icwcu-compare-table-subtitle a i { 349 font-size: 15px; 350 line-height: 1.7; } 351 352 .icwcu-compare-table table { 353 border: 1px solid #e5e4e4; 354 border-collapse: collapse; 355 border-spacing: 0; 356 margin-top: 2em; 357 width: 100%; } 358 359 .icwcu-compare-table table th { 360 background-color: #F0F0F1; 361 border-bottom: 1px solid #e5e4e4; } 362 363 .icwcu-compare-table table th, 364 .icwcu-compare-table table td { 365 padding: 1em; 366 text-align: center; } 367 368 .icwcu-compare-table table tr:nth-child(2n) td { 369 background: #fbfbfb; } 370 371 .icwcu--getting-started--video-wrapper { 372 min-width: 600px; } 373 .icwcu--getting-started--video-wrapper video { 374 width: 100%; } 375 376 @media screen and (max-width: 782px) { 377 #icwcu-admin-about-page .admin-grid-3, #icwcu-admin-about-page .admin-grid-2 { 378 grid-template-columns: 1fr; } 379 .icwcu-header-left.admin-col { 380 text-align: center; } 381 .icwcu-header-left h2 { 382 font-size: 2rem; } 383 .feature-list { 384 grid-template-columns: 1fr; } 385 #icwcu-admin-about-page, #setting-error-tgmpa { 386 margin: 20px; } 387 .icwcu-options-features > li { 388 width: 100%; } } 389 390 391 .icwcu-tab{ 392 cursor: pointer; 393 padding:10px 20px; 394 margin:0px 2px; 395 background:#000; 396 display:inline-block; 397 color:#fff; 398 border-radius:3px 3px 0px 0px; 399 box-shadow: 0 0.5rem 0.8rem #00000080; 400 } 401 input.icwcu-radio { 402 display: none; 403 } 404 .icwcu-panels{ 405 overflow:hidden; 406 padding:20px; 407 } 408 .icwcu-panel{ 409 display:none; 410 animation: fadein .8s; 411 } 412 @keyframes fadein { 413 from { 414 opacity:0; 415 } 416 to { 417 opacity:1; 418 } 419 } 420 .icwcu-panel-title{ 421 font-size:1.5em; 422 font-weight:bold 423 } 424 .radio{ 425 display:none; 426 } 427 #one:checked ~ .icwcu-panels #one-panel, 428 #two:checked ~ .icwcu-panels #two-panel, 429 #three:checked ~ .icwcu-panels #three-panel{ 430 display:block 431 } 432 #one:checked ~ .icwcu-tabs #one-tab, 433 #two:checked ~ .icwcu-tabs #two-tab, 434 #three:checked ~ .icwcu-tabs #three-tab{ 435 background:#fffffff6; 436 color:#000; 437 border-top: 3px solid #000; 438 } 1 #icwcu_custom_services_meta_box .icwcu-tab { display: inline-block; padding: 12px; cursor: pointer; font-weight: 600; } 2 #icwcu_custom_services_meta_box .icwcu-tab-radio { margin-left: 12px; } 3 #icwcu_custom_services_meta_box .icwcu-editor-panel { display: none; padding: 12px; } 4 #icwcu_custom_services_meta_box #icwcu-tab-checkbox:checked ~ #icwcu-panel-checkbox, 5 #icwcu_custom_services_meta_box #icwcu-tab-radio:checked ~ #icwcu-panel-radio { display: block; } 6 #icwcu_custom_services_meta_box .icwcu-custom-service-field { border: 1px solid #dcdcde; padding: 8px 16px; margin-bottom: 12px; } 7 #icwcu_custom_services_meta_box label { display: inline-block; min-width: 155px; } 8 #icwcu_custom_services_meta_box input[type=text], 9 #icwcu_custom_services_meta_box input[type=number], 10 #icwcu_custom_services_meta_box textarea { width: min(100%, 360px); vertical-align: middle; } 11 .icwcu-guide { max-width: 980px; } 12 .icwcu-guide p, .icwcu-guide li { font-size: 14px; line-height: 1.8; } 13 .icwcu-guide h2 { margin-top: 30px; } 14 @media (max-width: 600px) { #icwcu_custom_services_meta_box label { display: block; } } -
custom-checkbox-ultimate-for-woocommerce/trunk/assets/css/icwcu-services-style.css
r3264079 r3759386 1 h4.icwcu-checkbox-title { 2 margin-bottom: 10px; 3 margin-top: 0; 4 } 5 .icwcu-custom-services table { 6 width: 100%; 7 } 8 .icwcu-custom-services { 9 padding-bottom: 20px; 10 } 11 .icwcu-tooltip { 12 position: relative; 13 display: inline-block; 14 cursor: pointer; 15 color: #0073e6; 16 font-weight: bold; 17 vertical-align: bottom; 18 padding: 0 10px; 19 } 20 21 .icwcu-tooltiptext { 22 visibility: hidden; 23 width: auto; 24 min-width: 50px; 25 max-height: 500px; 26 background-color: #333333c7; 27 color: #fff; 28 border-radius: 6px; 29 padding: 5px; 30 position: absolute; 31 z-index: 2; 32 left: 100%; 33 top: 50%; 34 transform: translateY(-50%); 35 opacity: 0; 36 transition: opacity 0.3s ease; 37 font-size: 12px; 38 pointer-events: none; 39 } 40 .icwcu-tooltiptext::before { 41 content: ''; 42 position: absolute; 43 top: 50%; 44 left: -9px; 45 transform: translateY(-50%); 46 border-width: 5px; 47 border-style: solid; 48 border-color: transparent #333333c7 transparent transparent; 49 } 50 input.icwcu-radio { 51 margin-right: 5px; 52 } 53 /* Tooltip arrow */ 54 /* 55 .icwcu-tooltiptext::after { 56 content: ''; 57 position: absolute; 58 top: 50%; 59 right: -10px; 60 transform: translateY(-50%); 61 border-width: 5px; 62 border-style: solid; 63 border-color: transparent transparent transparent #333333c7; 64 }*/ 65 66 /* Show tooltip when hovering over the trigger or tooltip content */ 67 .icwcu-tooltip:hover .icwcu-tooltiptext, 68 .icwcu-tooltip .icwcu-tooltiptext:hover { 69 visibility: visible; 70 opacity: 1; 71 pointer-events: auto; /* Allow interaction with tooltip when visible */ 72 } 73 74 /* Hide tooltip when mouse leaves both the trigger and tooltip content */ 75 .icwcu-tooltip:not(:hover) .icwcu-tooltiptext:not(:hover) { 76 visibility: hidden; 77 opacity: 0; 78 pointer-events: none; /* Disable interaction again */ 79 } 80 81 82 .icwcu-checkbox { 83 position: relative; 84 margin: 0 1rem 0 0; 85 cursor: pointer; 86 z-index: 1; 87 } 88 89 .icwcu-checkbox:before { 90 transition: all .3s ease-in-out; 91 content: ""; 92 position: absolute; 93 left: 0; 94 z-index: 1; 95 width: 1rem; 96 height: 1rem; 97 border: 2px solid #f2f2f2; 98 } 99 100 .icwcu-checkbox:checked:before { 101 transform: rotate(-45deg); 102 height: .5rem; 103 border-color: green; /* Replace $green with actual value */ 104 border-top-style: none; 105 border-right-style: none; 106 } 107 108 .icwcu-checkbox:after { 109 content: ""; 110 position: absolute; 111 left: 0; 112 width: 1.1rem; 113 height: 1.1rem; 114 background: #ffffff; 115 cursor: pointer; 116 border: 1px solid #ddd; 117 } 118 span.icwcu-currency { 119 margin-left: 20px; 120 } 1 .icwcu-custom-services { border: 1px solid #ddd; padding: 16px; margin: 0 0 18px; min-width: 0; } 2 .icwcu-custom-services legend { font-weight: 600; padding: 0 6px; } 3 .icwcu-custom-services .icwcu-service { display: block; margin: 10px 0; overflow-wrap: anywhere; } 4 .icwcu-custom-services input { margin-right: 6px; } 5 .icwcu-custom-services .icwcu-currency { white-space: nowrap; margin-left: 8px; } 6 .icwcu-custom-services .icwcu-service-description { display: block; font-size: .85em; margin: 3px 0 0 24px; } -
custom-checkbox-ultimate-for-woocommerce/trunk/assets/js/custom-checkbox-frontend.js
r3264079 r3759386 1 jQuery(document).ready(function($) { 2 document.querySelectorAll('.icwcu-radio').forEach((radio) => { 1 /* Lets customers click a selected radio service again to unselect it (add-ons are optional). */ 2 document.addEventListener('DOMContentLoaded', function () { 3 document.querySelectorAll('input.icwcu-radio[name="icwcu_radio_service"]').forEach(function (radio) { 4 radio.addEventListener('mousedown', function () { 5 this.dataset.wasChecked = this.checked ? '1' : ''; 6 }); 3 7 radio.addEventListener('click', function () { 4 const group = this.getAttribute('data-group');5 document.querySelectorAll(`.icwcu-radio[data-group="${group}"]`).forEach((btn) => {6 if (btn !== this) btn.checked = false;7 } );8 if (this.dataset.wasChecked === '1') { 9 this.checked = false; 10 this.dataset.wasChecked = ''; 11 } 8 12 }); 9 13 }); 10 }); 14 }); -
custom-checkbox-ultimate-for-woocommerce/trunk/assets/js/custom-services-meta-box.js
r3264079 r3759386 1 jQuery(document).ready(function($) { 2 var fieldIndex = customServicesMetaBox.initialFieldCount; 3 var fieldIndexradio = customRadioServicesMetaBox.initialRadioFieldCount; 4 5 $('#icwcu-add-more-fields').on('click', function() { 6 fieldIndex++; 7 $('#icwcu-custom-services-fields').append( 8 '<div class="icwcu-custom-service-field" data-index="' + fieldIndex + '">' + 9 '<p>' + 10 '<label for="service_name_' + fieldIndex + '">' + fieldIndex + '. Service Name</label>' + 11 '<input type="text" id="service_name_' + fieldIndex + '" name="icwcu_custom_services[' + fieldIndex + '][name]" value="" />' + 12 '<label for="service_price_' + fieldIndex + '"> Service Price ' + customServicesMetaBox.currencySymbol + ' </label>' + 13 '<input type="number" step="0.01" id="service_price_' + fieldIndex + '" name="icwcu_custom_services[' + fieldIndex + '][price]" value="" />' + 14 '<label for="service_desc_' + fieldIndex + '"> Service Description: ' + ' </label>' + 15 '<textarea id="service_desc_' + fieldIndex + '" name="icwcu_custom_services[' + fieldIndex + '][desc]" value="" ></textarea>' + 16 '</p>' + 17 '</div>' 18 ); 19 }); 20 $('#icwcu-add-moreradio-fields').on('click', function() { 21 fieldIndexradio++; 22 $('#icwcu-custom-radio-services-fields').append( 23 '<div class="icwcu-custom-service-field" data-index="' + fieldIndexradio + '">' + 24 '<p>' + 25 '<label for="radioservice_name_' + fieldIndexradio + '">' + fieldIndexradio + '. Service Name</label>' + 26 '<input type="text" id="radioservice_name_' + fieldIndexradio + '" name="icwcu_custom_servicesradio[' + fieldIndexradio + '][name]" value="" />' + 27 '<label for="radioservice_price_' + fieldIndexradio + '"> Service Price ' + customServicesMetaBox.currencySymbol + ' </label>' + 28 '<input type="number" step="0.01" id="radioservice_price_' + fieldIndexradio + '" name="icwcu_custom_servicesradio[' + fieldIndexradio + '][price]" value="" />' + 29 '<label for="radioservice_desc_' + fieldIndexradio + '"> Service Description: ' + ' </label>' + 30 '<textarea id="radioservice_desc_' + fieldIndexradio + '" name="icwcu_custom_servicesradio[' + fieldIndexradio + '][desc]" value="" ></textarea>' + 31 '</p>' + 32 '</div>' 33 ); 1 document.addEventListener('DOMContentLoaded', function () { 2 document.querySelectorAll('#icwcu_custom_services_meta_box .icwcu-add-fields').forEach(function (button) { 3 button.addEventListener('click', function () { 4 var fields = button.previousElementSibling; 5 var index = fields.children.length + 1; 6 var row = document.createElement('div'); 7 row.className = 'icwcu-custom-service-field'; 8 row.dataset.index = index; 9 ['name', 'price', 'desc'].forEach(function (key) { 10 var p = document.createElement('p'); 11 var label = document.createElement('label'); 12 var input = document.createElement(key === 'desc' ? 'textarea' : 'input'); 13 input.id = fields.dataset.meta + '-' + index + '-' + key; 14 input.name = fields.dataset.meta + '[' + index + '][' + key + ']'; 15 if (key !== 'desc') input.type = key === 'price' ? 'number' : 'text'; 16 if (key === 'price') { input.min = '0'; input.step = 'any'; } 17 label.htmlFor = input.id; 18 label.textContent = icwcuFields[key]; 19 p.appendChild(label); 20 p.appendChild(input); 21 row.appendChild(p); 22 }); 23 fields.appendChild(row); 24 row.querySelector('input').focus(); 25 }); 34 26 }); 35 27 }); -
custom-checkbox-ultimate-for-woocommerce/trunk/custom-checkbox-ultimate-for-woocommerce.php
r3264079 r3759386 4 4 * Plugin URI: https://www.insertcart.com/insertcart-custom-checkbox-ultimate-for-woocommerce-documentation/ 5 5 * Description: Add customizable checkbox options on WooCommerce product pages for additional services and charges. 6 * Version: 1. 36 * Version: 1.4 7 7 * Requires at least: 6.3 8 8 * Requires PHP: 7.2 … … 12 12 * License URI: https://www.gnu.org/licenses/gpl-2.0.html 13 13 * Text Domain: custom-checkbox-ultimate-for-woocommerce 14 * Domain Path: /languages 14 15 * Requires Plugins: woocommerce 16 * WC requires at least: 8.0 17 * WC tested up to: 10.3 15 18 */ 16 19 … … 19 22 } 20 23 21 define( 'ICWCU_PLUGIN_VERSION', '1.3' ); 24 define( 'ICWCU_PLUGIN_VERSION', '1.4' ); 25 define( 'ICWCU_PLUGIN_FILE', __FILE__ ); 26 define( 'ICWCU_DOCUMENTATION_URL', 'https://www.insertcart.com/insertcart-custom-checkbox-ultimate-for-woocommerce-documentation/' ); 22 27 23 28 // Include our enqueue file (even if empty) 24 require_once 'includes/enqueue.php';29 require_once plugin_dir_path( __FILE__ ) . 'includes/enqueue.php'; 25 30 26 31 // Initialize our plugin classes 27 require_once 'includes/class-icwcu-init.php'; 28 $icwcu_init = new ICWCU_Init(); 32 require_once plugin_dir_path( __FILE__ ) . 'includes/class-icwcu-init.php'; 33 add_action( 'plugins_loaded', 'icwcu_initialize' ); 34 function icwcu_initialize() { 35 if ( class_exists( 'WooCommerce' ) ) { 36 new ICWCU_Init(); 37 } else { 38 add_action( 'admin_notices', 'icwcu_dependency_notice' ); 39 } 40 } 41 function icwcu_dependency_notice() { 42 if ( current_user_can( 'activate_plugins' ) ) { 43 echo '<div class="notice notice-warning"><p>' . esc_html__( 'Custom Checkbox Ultimate requires WooCommerce to be installed and active.', 'custom-checkbox-ultimate-for-woocommerce' ) . '</p></div>'; 44 } 45 } 46 require_once __DIR__ . '/includes/class-icwcu-help.php'; 47 ICWCU_Help::hooks(); 29 48 30 49 // Create a Plugin menu in the dashboard … … 34 53 esc_html__( 'IC Woo Ultimate', 'custom-checkbox-ultimate-for-woocommerce' ), 35 54 esc_html__( 'IC Woo Ultimate', 'custom-checkbox-ultimate-for-woocommerce' ), 36 'manage_ options',55 'manage_woocommerce', 37 56 'ic-woo-ultimate', 38 57 'icwcu_about_page', 39 'da ta:image/svg+xml;base64,' . base64_encode( icwcu_get_custom_svg_icon() ),58 'dashicons-yes-alt', 40 59 100 41 60 ); 42 61 } 43 62 function icwcu_about_page() { 63 if ( ! current_user_can( 'manage_woocommerce' ) ) { 64 wp_die( esc_html__( 'You cannot access this setup guide.', 'custom-checkbox-ultimate-for-woocommerce' ), '', array( 'response' => 403 ) ); 65 } 44 66 include_once plugin_dir_path( __FILE__ ) . 'includes/plugin-info.php'; 45 67 } 68 69 // Declare compatibility with WooCommerce High-Performance Order Storage (HPOS) and Cart/Checkout blocks. 70 add_action( 'before_woocommerce_init', 'icwcu_declare_wc_compatibility' ); 71 function icwcu_declare_wc_compatibility() { 72 if ( class_exists( '\Automattic\WooCommerce\Utilities\FeaturesUtil' ) ) { 73 \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', ICWCU_PLUGIN_FILE, true ); 74 \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'cart_checkout_blocks', ICWCU_PLUGIN_FILE, true ); 75 } 76 } -
custom-checkbox-ultimate-for-woocommerce/trunk/includes/class-icwcu-cart.php
r3264079 r3759386 5 5 6 6 class ICWCU_Cart { 7 8 /** 9 * Original product prices, so add-ons are never added twice 10 * when cart totals are calculated more than once per request. 11 * 12 * @var array 13 */ 14 private $base_prices = array(); 15 7 16 public function __construct() { 8 add_filter( 'woocommerce_add_cart_item_data', array( $this, 'add_custom_fields_to_cart_item' ), 10, 2 ); 9 add_filter( 'woocommerce_add_cart_item_data', array( $this, 'add_customradio_fields_to_cart_item' ), 10, 2 ); 10 add_filter( 'woocommerce_get_item_data', array( $this, 'display_custom_fields_in_cart' ), 10, 2 ); 11 12 add_action( 'woocommerce_before_calculate_totals', array( $this, 'adjust_cart_item_price' ), 10, 1 ); 13 add_action( 'woocommerce_before_calculate_totals', array( $this, 'adjustradio_cart_item_price' ), 10, 1 ); 14 //add_action( 'woocommerce_cart_calculate_fees', array( $this, 'icwcu_adjust_cart_total' ), 10, 1 ); 15 16 add_action( 'woocommerce_after_calculate_totals', array( $this, 'adjust_cart_item_priceless' ), 10, 1 ); 17 add_action( 'woocommerce_after_calculate_totals', array( $this, 'adjustradio_cart_item_priceless' ), 10, 1 ); 18 // add_action( 'woocommerce_cart_item_price', array( $this, 'adjust_cart_item_price' ), 10, 1 ); 17 add_filter( 'woocommerce_add_cart_item_data', array( $this, 'add_services_to_cart_item' ), 10, 2 ); 18 add_filter( 'woocommerce_get_item_data', array( $this, 'display_services_in_cart' ), 10, 2 ); 19 add_action( 'woocommerce_before_calculate_totals', array( $this, 'adjust_cart_item_prices' ), 20, 1 ); 20 } 19 21 20 } 21 22 public function add_custom_fields_to_cart_item( $cart_item_data, $product_id ) { 23 if ( ! isset( $_POST['icwcu_custom_checkbox_nonce'] ) || ! wp_verify_nonce( sanitize_text_field(wp_unslash( $_POST['icwcu_custom_checkbox_nonce']) ), 'icwcu_custom_checkbox_nonce_action' ) ) { 24 return $cart_item_data; 25 } 26 $custom_services = get_post_meta( $product_id, 'icwcu_custom_services', true ); 27 if ( ! is_array( $custom_services ) ) { 28 $custom_services = array(); 29 } 30 31 if ( ! empty( $custom_services ) ) { 32 foreach ( $custom_services as $service ) { 33 $service_key = sanitize_title( $service['name'] ); 34 if ( isset( $_POST[ $service_key ] ) ) { 35 $price = floatval( wp_unslash( $_POST[ $service_key ] ) ); 36 $cart_item_data[ $service_key ] = ( $price >= 0 ) ? $price : 0; 37 } 38 } 39 } 40 return $cart_item_data; 41 } 42 43 public function add_customradio_fields_to_cart_item( $cart_item_data, $product_id ) { 44 if ( ! isset( $_POST['icwcu_custom_radio_nonce'] ) || ! wp_verify_nonce( sanitize_text_field(wp_unslash( $_POST['icwcu_custom_radio_nonce']) ), 'icwcu_custom_radio_nonce_action' ) ) { 45 return $cart_item_data; 46 } 47 $custom_servicesradio = get_post_meta( $product_id, 'icwcu_custom_servicesradio', true ); 48 if ( ! is_array( $custom_servicesradio ) ) { 49 $custom_servicesradio = array(); 50 } 51 52 if ( ! empty( $custom_servicesradio ) ) { 53 foreach ( $custom_servicesradio as $serviceradio ) { 54 $service_keyradio = sanitize_title( $serviceradio['name'] ); 55 if ( isset( $_POST[ $service_keyradio ] ) ) { 56 $price = floatval( wp_unslash( $_POST[ $service_keyradio ] ) ); 57 $cart_item_data[ $service_keyradio ] = ( $price >= 0 ) ? $price : 0; 58 } 22 /** 23 * Save the selected services on the cart item. 24 * Prices are always read from the product settings, never from the request. 25 */ 26 public function add_services_to_cart_item( $cart_item_data, $product_id ) { 27 $selected = array(); 59 28 29 // Checkbox services. 30 if ( isset( $_POST['icwcu_custom_checkbox_nonce'] ) 31 && is_string( $_POST['icwcu_custom_checkbox_nonce'] ) 32 && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['icwcu_custom_checkbox_nonce'] ) ), 'icwcu_custom_checkbox_nonce_action' ) 33 && ! empty( $_POST['icwcu_services'] ) && is_array( $_POST['icwcu_services'] ) ) { 60 34 35 $services = icwcu_get_product_services( $product_id, 'checkbox' ); 36 $posted = array_map( 'sanitize_title', array_filter( wp_unslash( $_POST['icwcu_services'] ), 'is_string' ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Non-string entries are discarded and every remaining entry passes through sanitize_title. 61 37 62 } 63 } 64 return $cart_item_data; 65 } 66 67 public function display_custom_fields_in_cart( $item_data, $cart_item ) { 68 $custom_services = get_post_meta( $cart_item['product_id'], 'icwcu_custom_services', true ); 69 $custom_servicesradio = get_post_meta( $cart_item['product_id'], 'icwcu_custom_servicesradio', true ); 70 71 if ( ! empty( $custom_services ) ) { 72 foreach ( $custom_services as $service ) { 73 $service_key = sanitize_title( $service['name'] ); 74 if ( isset( $cart_item[ $service_key ] ) ) { 75 $item_data[] = array( 76 'name' => $service['name'], 77 'value' => get_woocommerce_currency_symbol() . esc_html( $cart_item[ $service_key ] ) 38 foreach ( array_unique( $posted ) as $key ) { 39 if ( isset( $services[ $key ] ) ) { 40 $selected[] = array( 41 'name' => $services[ $key ]['name'], 42 'price' => $services[ $key ]['price'], 78 43 ); 79 44 } 80 45 } 81 46 } 82 83 if ( ! empty( $custom_servicesradio ) ) { 84 foreach ( $custom_servicesradio as $serviceradio ) { 85 $service_keyradio = sanitize_title( $serviceradio['name'] ); 86 if ( isset( $cart_item[ $service_keyradio ] ) ) { 87 $item_data[] = array( 88 'name' => $serviceradio['name'], 89 'value' => get_woocommerce_currency_symbol() . esc_html( $cart_item[ $service_keyradio ] ) 47 48 // Radio service (only one can be chosen). 49 if ( isset( $_POST['icwcu_custom_radio_nonce'] ) 50 && is_string( $_POST['icwcu_custom_radio_nonce'] ) 51 && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['icwcu_custom_radio_nonce'] ) ), 'icwcu_custom_radio_nonce_action' ) 52 && ! empty( $_POST['icwcu_radio_service'] ) && is_string( $_POST['icwcu_radio_service'] ) ) { 53 54 $services = icwcu_get_product_services( $product_id, 'radio' ); 55 $key = sanitize_title( wp_unslash( $_POST['icwcu_radio_service'] ) ); 56 57 if ( isset( $services[ $key ] ) ) { 58 $selected[] = array( 59 'name' => $services[ $key ]['name'], 60 'price' => $services[ $key ]['price'], 61 ); 62 } 63 } 64 65 if ( ! empty( $selected ) ) { 66 $cart_item_data['icwcu_services'] = $selected; 67 } 68 69 return $cart_item_data; 70 } 71 72 /** 73 * Get the services selected for a cart item. 74 * 75 * @param array $cart_item Cart item. 76 * @return array List of array( 'name' => string, 'price' => float ). 77 */ 78 public static function get_cart_item_services( $cart_item ) { 79 if ( ! empty( $cart_item['icwcu_services'] ) && is_array( $cart_item['icwcu_services'] ) ) { 80 return array_values( array_filter( $cart_item['icwcu_services'], function( $service ) { 81 return is_array( $service ) && isset( $service['name'], $service['price'] ) && is_string( $service['name'] ) && is_numeric( $service['price'] ) && is_finite( (float) $service['price'] ) && $service['price'] >= 0; 82 } ) ); 83 } 84 85 // Items added to the cart by version 1.3 or older. 86 $selected = array(); 87 if ( empty( $cart_item['product_id'] ) ) { 88 return $selected; 89 } 90 foreach ( array( 'checkbox', 'radio' ) as $type ) { 91 foreach ( icwcu_get_product_services( $cart_item['product_id'], $type ) as $key => $service ) { 92 if ( isset( $cart_item[ $key ] ) && is_numeric( $cart_item[ $key ] ) ) { 93 $selected[] = array( 94 'name' => $service['name'], 95 'price' => $service['price'], 90 96 ); 97 if ( 'radio' === $type ) { 98 break; 99 } 91 100 } 92 101 } 93 102 } 94 103 return $selected; 104 } 105 106 /** 107 * Show the selected services under the product name in cart and checkout. 108 */ 109 public function display_services_in_cart( $item_data, $cart_item ) { 110 foreach ( self::get_cart_item_services( $cart_item ) as $service ) { 111 $item_data[] = array( 112 'name' => $service['name'], 113 'value' => icwcu_format_price( $service['price'] ), 114 ); 115 } 95 116 return $item_data; 96 117 } 97 98 118 99 100 private function adjust_cart_item_price_generic( $cart, $meta_key, $add = true ) { 101 if ( is_admin() && ! defined( 'DOING_AJAX' ) ) { 119 /** 120 * Add the selected services to the item price. 121 */ 122 public function adjust_cart_item_prices( $cart ) { 123 if ( is_admin() && ! wp_doing_ajax() ) { 102 124 return; 103 125 } 104 foreach ( $cart->get_cart() as $cart_item ) { 105 $services = get_post_meta( $cart_item['product_id'], $meta_key, true ); 106 if ( ! is_array( $services ) ) { 107 $services = array(); 126 127 foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) { 128 $services = self::get_cart_item_services( $cart_item ); 129 if ( empty( $services ) || empty( $cart_item['data'] ) ) { 130 continue; 108 131 } 109 if ( ! empty( $services ) ) { 110 $additional_cost = 0; 111 foreach ( $services as $service ) { 112 $service_key = sanitize_title( $service['name'] ); 113 if ( isset( $cart_item[ $service_key ] ) ) { 114 $additional_cost += floatval( $cart_item[ $service_key ] ); 115 } 116 } 117 if ( $additional_cost > 0 ) { 118 $new_price = $add ? 119 $cart_item['data']->get_price() + $additional_cost : 120 $cart_item['data']->get_price() - $additional_cost; 121 $cart_item['data']->set_price( $new_price ); 122 } 132 133 $extra = 0; 134 foreach ( $services as $service ) { 135 $extra += (float) $service['price']; 123 136 } 137 if ( $extra <= 0 ) { 138 continue; 139 } 140 141 $product = $cart_item['data']; 142 $id = spl_object_hash( $product ); 143 if ( ! isset( $this->base_prices[ $id ] ) ) { 144 $this->base_prices[ $id ] = (float) $product->get_price( 'edit' ); 145 } 146 147 $product->set_price( $this->base_prices[ $id ] + $extra ); 124 148 } 125 149 } 126 127 public function adjust_cart_item_price( $cart ) {128 $this->adjust_cart_item_price_generic( $cart, 'icwcu_custom_services', true );129 }130 131 public function adjustradio_cart_item_price( $cart ) {132 $this->adjust_cart_item_price_generic( $cart, 'icwcu_custom_servicesradio', true );133 }134 135 public function adjust_cart_item_priceless( $cart ) {136 $this->adjust_cart_item_price_generic( $cart, 'icwcu_custom_services', false );137 }138 139 public function adjustradio_cart_item_priceless( $cart ) {140 $this->adjust_cart_item_price_generic( $cart, 'icwcu_custom_servicesradio', false );141 }142 143 /********144 ** Substract from item price145 ****/146 147 148 149 150 public function icwcu_adjust_cart_total( $cart ) {151 if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {152 return;153 }154 155 $additional_cost = 0;156 157 foreach ( $cart->get_cart() as $cart_item ) {158 $custom_services = get_post_meta( $cart_item['product_id'], 'icwcu_custom_services', true );159 if ( is_array( $custom_services ) && ! empty( $custom_services ) ) {160 foreach ( $custom_services as $service ) {161 $service_key = sanitize_title( $service['name'] );162 if ( isset( $cart_item[ $service_key ] ) ) {163 $additional_cost += floatval( $cart_item[ $service_key ] );164 }165 }166 }167 168 $custom_servicesradio = get_post_meta( $cart_item['product_id'], 'icwcu_custom_servicesradio', true );169 if ( is_array( $custom_servicesradio ) && ! empty( $custom_servicesradio ) ) {170 foreach ( $custom_servicesradio as $serviceradio ) {171 $service_keyradio = sanitize_title( $serviceradio['name'] );172 if ( isset( $cart_item[ $service_keyradio ] ) ) {173 $additional_cost += floatval( $cart_item[ $service_keyradio ] );174 }175 }176 }177 }178 179 if ( $additional_cost > 0 ) {180 WC()->cart->add_fee( __( 'Additional Services', 'custom-checkbox-ultimate-for-woocommerce' ), $additional_cost );181 }182 }183 184 150 } -
custom-checkbox-ultimate-for-woocommerce/trunk/includes/class-icwcu-init.php
r3264079 r3759386 7 7 public function __construct() { 8 8 // Include required class files and helpers 9 require_once 'class-icwcu-meta-box.php';10 require_once 'class-icwcu-cart.php';11 require_once 'class-icwcu-order.php';12 require_once 'class-icwcu-settings.php';13 require_once 'helpers.php';9 require_once __DIR__ . '/class-icwcu-meta-box.php'; 10 require_once __DIR__ . '/class-icwcu-cart.php'; 11 require_once __DIR__ . '/class-icwcu-order.php'; 12 require_once __DIR__ . '/class-icwcu-settings.php'; 13 require_once __DIR__ . '/helpers.php'; 14 14 15 15 // Instantiate the classes to initialize functionality -
custom-checkbox-ultimate-for-woocommerce/trunk/includes/class-icwcu-meta-box.php
r3264079 r3759386 1 1 <?php 2 if ( ! defined( 'ABSPATH' ) ) { 3 exit; 4 } 2 defined( 'ABSPATH' ) || exit; 5 3 4 /** Product editor and classic product form fields. */ 6 5 class ICWCU_Meta_Box { 7 6 public function __construct() { 8 7 add_action( 'add_meta_boxes', array( $this, 'add_custom_services_meta_box' ) ); 9 add_action( 'save_post ', array( $this, 'save_custom_services_meta_box_data' ) );8 add_action( 'save_post_product', array( $this, 'save_custom_services_meta_box_data' ) ); 10 9 add_action( 'admin_enqueue_scripts', array( $this, 'meta_box_scripts' ) ); 11 10 add_action( 'woocommerce_before_add_to_cart_button', array( $this, 'add_custom_fields' ) ); 12 11 } 13 14 12 public function add_custom_services_meta_box() { 15 add_meta_box( 16 'icwcu_custom_services_meta_box', 17 icwcu_get_custom_svg_icon() . 'Custom Services - IC Woo Ultimate', 18 array( $this, 'display_custom_services_meta_box' ), 19 'product', 20 'normal', 21 'high' 22 ); 13 add_meta_box( 'icwcu_custom_services_meta_box', __( 'Custom Services - IC Woo Ultimate', 'custom-checkbox-ultimate-for-woocommerce' ), array( $this, 'display_custom_services_meta_box' ), 'product', 'normal', 'high' ); 23 14 } 24 25 15 public function display_custom_services_meta_box( $post ) { 26 $custom_services = get_post_meta( $post->ID, 'icwcu_custom_services', true ); 27 if ( ! is_array( $custom_services ) ) { 28 $custom_services = array(); 16 wp_nonce_field( 'icwcu_custom_services_meta_box_nonce', 'icwcu_custom_services_meta_box_nonce' ); 17 $help_url = current_user_can( 'manage_woocommerce' ) ? ICWCU_Help::url() : ICWCU_DOCUMENTATION_URL; 18 echo '<p>' . esc_html__( 'Add optional services per product unit. Use checkboxes for multiple choices and radio buttons for one choice.', 'custom-checkbox-ultimate-for-woocommerce' ) . ' <a href="' . esc_url( $help_url ) . '">' . esc_html__( 'Setup guide', 'custom-checkbox-ultimate-for-woocommerce' ) . '</a></p>'; 19 echo '<input class="icwcu-tab-radio" id="icwcu-tab-checkbox" name="icwcu-editor-tab" type="radio" checked><label class="icwcu-tab" for="icwcu-tab-checkbox">' . esc_html__( 'Checkbox Type', 'custom-checkbox-ultimate-for-woocommerce' ) . '</label>'; 20 echo '<input class="icwcu-tab-radio" id="icwcu-tab-radio" name="icwcu-editor-tab" type="radio"><label class="icwcu-tab" for="icwcu-tab-radio">' . esc_html__( 'Radio Type', 'custom-checkbox-ultimate-for-woocommerce' ) . '</label>'; 21 foreach ( array( 'checkbox', 'radio' ) as $type ) { 22 $meta = 'radio' === $type ? 'icwcu_custom_servicesradio' : 'icwcu_custom_services'; 23 $title_meta = 'radio' === $type ? 'icwcu_custom_radioservices_title' : 'icwcu_custom_services_title'; 24 $saved = get_post_meta( $post->ID, $meta, true ); 25 $services = is_array( $saved ) ? array_values( array_filter( $saved, 'is_array' ) ) : array(); 26 echo '<div class="icwcu-editor-panel" id="icwcu-panel-' . esc_attr( $type ) . '">'; 27 echo '<p><label for="' . esc_attr( $title_meta ) . '">' . esc_html__( 'Custom Services Title:', 'custom-checkbox-ultimate-for-woocommerce' ) . '</label> <input type="text" id="' . esc_attr( $title_meta ) . '" name="' . esc_attr( $title_meta ) . '" value="' . esc_attr( get_post_meta( $post->ID, $title_meta, true ) ) . '"></p>'; 28 echo '<div class="icwcu-service-fields" data-meta="' . esc_attr( $meta ) . '">'; 29 for ( $i = 0; $i < max( 5, count( $services ) ); $i++ ) { 30 $service = isset( $services[ $i ] ) ? $services[ $i ] : array(); 31 echo '<div class="icwcu-custom-service-field" data-index="' . esc_attr( $i + 1 ) . '">'; 32 foreach ( self::labels() as $key => $label ) { 33 $field = $meta . '[' . ( $i + 1 ) . '][' . $key . ']'; 34 $id = $meta . '-' . ( $i + 1 ) . '-' . $key; 35 $value = isset( $service[ $key ] ) && is_scalar( $service[ $key ] ) ? $service[ $key ] : ''; 36 echo '<p><label for="' . esc_attr( $id ) . '">' . esc_html( $label ) . '</label> '; 37 if ( 'desc' === $key ) { 38 echo '<textarea id="' . esc_attr( $id ) . '" name="' . esc_attr( $field ) . '">' . esc_textarea( $value ) . '</textarea>'; 39 } else { 40 echo '<input type="' . ( 'price' === $key ? 'number' : 'text' ) . '" ' . ( 'price' === $key ? 'min="0" step="any" ' : '' ) . 'id="' . esc_attr( $id ) . '" name="' . esc_attr( $field ) . '" value="' . esc_attr( $value ) . '">'; 41 } 42 echo '</p>'; 43 } 44 echo '</div>'; 45 } 46 echo '</div><button type="button" class="button icwcu-add-fields">+ ' . esc_html__( 'Add More Fields', 'custom-checkbox-ultimate-for-woocommerce' ) . '</button></div>'; 29 47 } 30 $custom_servicesradio = get_post_meta( $post->ID, 'icwcu_custom_servicesradio', true );31 if ( ! is_array( $custom_servicesradio ) ) {32 $custom_servicesradio = array();33 }34 35 wp_nonce_field( 'icwcu_custom_services_meta_box_nonce', 'icwcu_custom_services_meta_box_nonce' );36 $custom_services_title = get_post_meta( $post->ID, 'icwcu_custom_services_title', true );37 $custom_radioservices_title = get_post_meta( $post->ID, 'icwcu_custom_radioservices_title', true );38 39 echo '<input class="radio icwcu-radio" id="one" name="group" type="radio" checked>40 <input class="radio icwcu-radio" id="two" name="group" type="radio">41 <div class="icwcu-tabs">42 <label class="icwcu-tab" id="one-tab" for="one">'.esc_html__( 'Checkbox Type', 'custom-checkbox-ultimate-for-woocommerce' ).'</label>43 <label class="icwcu-tab" id="two-tab" for="two">'.esc_html__( 'Radio Type', 'custom-checkbox-ultimate-for-woocommerce' ).'</label>44 </div>';45 46 echo '<div class="icwcu-panels">';47 48 // Checkbox Type Panel49 echo '<div class="icwcu-panel" id="one-panel">';50 echo '<div id="icwcu-custom-services-title">';51 echo '<p><label for="icwcu_custom_services_title">' . esc_html__( 'Custom Services Title:', 'custom-checkbox-ultimate-for-woocommerce' ) . ' </label>';52 echo '<input type="text" id="icwcu_custom_services_title" name="icwcu_custom_services_title" value="' . esc_attr( sanitize_text_field($custom_services_title) ) . '" /></p>';53 echo '</div>';54 55 $default_fields = 5;56 $field_count = max( count( $custom_services ), $default_fields );57 58 echo '<div id="icwcu-custom-services-fields">';59 for ( $i = 1; $i <= $field_count; $i++ ) {60 $service_name = isset( $custom_services[$i]['name'] ) ? $custom_services[$i]['name'] : '';61 // $service_price = isset( $custom_services[$i]['price'] ) ? $custom_services[$i]['price'] : '';62 $service_price = isset( $custom_services[$i]['price'] ) ? floatval( $custom_services[$i]['price'] ) : 0;63 $service_desc = isset( $custom_services[$i]['desc'] ) ? $custom_services[$i]['desc'] : '';64 65 echo '<div class="icwcu-custom-service-field" data-index="' . esc_html( $i ) . '">';66 echo '<p><label for="service_name_' . esc_html( $i ) . '">' . esc_html( $i ) . '. ' . esc_html__( 'Service Name:', 'custom-checkbox-ultimate-for-woocommerce' ) . ' </label>';67 echo '<input type="text" id="service_name_' . esc_html( $i ) . '" name="icwcu_custom_services[' . esc_html( $i ) . '][name]" value="' . esc_attr(sanitize_text_field( $service_name) ) . '" />';68 echo '<label for="service_price_' . esc_html( $i ) . '">' . esc_html__( 'Service Price', 'custom-checkbox-ultimate-for-woocommerce' ) . ' ' . esc_html( get_woocommerce_currency_symbol() ) . ' </label>';69 echo '<input type="number" step="0.01" id="service_price_' . esc_html( $i ) . '" name="icwcu_custom_services[' . esc_html( $i ) . '][price]" value="' . esc_attr( $service_price ) . '" />';70 echo '<label for="service_desc_' . esc_html( $i ) . '">' . esc_html__( 'Service Description:', 'custom-checkbox-ultimate-for-woocommerce' ) . ' </label>';71 echo '<textarea id="service_desc_' . esc_html( $i ) . '" name="icwcu_custom_services[' . esc_html( $i ) . '][desc]">' . esc_textarea( sanitize_textarea_field($service_desc) ) . '</textarea></p>';72 echo '</div>';73 }74 echo '</div>';75 echo '<button type="button" id="icwcu-add-more-fields" class="button">+ ' . esc_html__( 'Add More Fields', 'custom-checkbox-ultimate-for-woocommerce' ) . '</button>';76 echo '</div>';77 78 // Radio Type Panel79 echo '<div class="icwcu-panel" id="two-panel">';80 echo '<div id="icwcu-custom-services-title">';81 echo '<p><label for="icwcu_custom_radioservices_title">' . esc_html__( 'Custom Radio Services Title:', 'custom-checkbox-ultimate-for-woocommerce' ) . ' </label>';82 echo '<input type="text" id="icwcu_custom_radioservices_title" name="icwcu_custom_radioservices_title" value="' . esc_attr( sanitize_text_field($custom_radioservices_title) ) . '" /></p>';83 echo '</div>';84 85 $default_fieldsradio = 5;86 $field_countradio = max( count( $custom_servicesradio ), $default_fieldsradio );87 88 echo '<div id="icwcu-custom-radio-services-fields">';89 for ( $i = 1; $i <= $field_countradio; $i++ ) {90 $radioservice_name = isset( $custom_servicesradio[$i]['name'] ) ? $custom_servicesradio[$i]['name'] : '';91 // $radioservice_price = isset( $custom_servicesradio[$i]['price'] ) ? $custom_servicesradio[$i]['price'] : '';92 $radioservice_price = isset( $custom_servicesradio[$i]['price'] ) ? floatval( $custom_servicesradio[$i]['price'] ) : 0;93 $radioservice_desc = isset( $custom_servicesradio[$i]['desc'] ) ? $custom_servicesradio[$i]['desc'] : '';94 echo '<div class="icwcu-custom-service-field" data-index="' . esc_html( $i ) . '">';95 echo '<p><label for="radioservice_name_' . esc_html( $i ) . '">' . esc_html( $i ) . '. ' . esc_html__( 'Service Name:', 'custom-checkbox-ultimate-for-woocommerce' ) . ' </label>';96 echo '<input type="text" id="radioservice_name_' . esc_html( $i ) . '" name="icwcu_custom_servicesradio[' . esc_html( $i ) . '][name]" value="' . esc_attr( sanitize_text_field($radioservice_name) ) . '" />';97 echo '<label for="radioservice_price_' . esc_html( $i ) . '">' . esc_html__( 'Service Price', 'custom-checkbox-ultimate-for-woocommerce' ) . ' ' . esc_html( get_woocommerce_currency_symbol() ) . ' </label>';98 echo '<input type="number" step="0.01" id="radioservice_price_' . esc_html( $i ) . '" name="icwcu_custom_servicesradio[' . esc_html( $i ) . '][price]" value="' . esc_attr( $radioservice_price ) . '" />';99 echo '<label for="radioservice_desc_' . esc_html( $i ) . '">' . esc_html__( 'Service Description:', 'custom-checkbox-ultimate-for-woocommerce' ) . ' </label>';100 echo '<textarea id="radioservice_desc_' . esc_html( $i ) . '" name="icwcu_custom_servicesradio[' . esc_html( $i ) . '][desc]">' . esc_textarea( sanitize_textarea_field($radioservice_desc )) . '</textarea></p>';101 echo '</div>';102 }103 echo '</div>';104 echo '<button type="button" id="icwcu-add-moreradio-fields" class="button">+ ' . esc_html__( 'Add More Fields', 'custom-checkbox-ultimate-for-woocommerce' ) . '</button>';105 echo '</div>';106 107 echo '</div>'; // End of panels108 48 } 109 49 public static function labels() { 50 return array( 'name' => __( 'Service Name:', 'custom-checkbox-ultimate-for-woocommerce' ), 'price' => __( 'Service Price:', 'custom-checkbox-ultimate-for-woocommerce' ), 'desc' => __( 'Service Description:', 'custom-checkbox-ultimate-for-woocommerce' ) ); 51 } 110 52 public function meta_box_scripts( $hook ) { 111 if ( 'post.php' != $hook && 'post-new.php' != $hook ) { 53 $screen = get_current_screen(); 54 if ( ! in_array( $hook, array( 'post.php', 'post-new.php' ), true ) || ! $screen || 'product' !== $screen->post_type ) { 112 55 return; 113 56 } 114 115 wp_enqueue_script( 'icwcu-custom-services-meta-box', plugin_dir_url( __FILE__ ) . '../assets/js/custom-services-meta-box.js', array( 'jquery' ), ICWCU_PLUGIN_VERSION, true ); 116 $initial_field_count = max( 5, count( (array) get_post_meta( get_the_ID(), 'icwcu_custom_services', true ) ) ); 117 $initial_radiofield_count = max( 5, count( (array) get_post_meta( get_the_ID(), 'icwcu_custom_servicesradio', true ) ) ); 118 wp_localize_script( 'icwcu-custom-services-meta-box', 'customServicesMetaBox', array( 119 'initialFieldCount' => $initial_field_count, 120 'currencySymbol' => get_woocommerce_currency_symbol(), 121 ) ); 122 wp_localize_script( 'icwcu-custom-services-meta-box', 'customRadioServicesMetaBox', array( 123 'initialRadioFieldCount' => $initial_radiofield_count 124 ) ); 57 wp_enqueue_script( 'icwcu-custom-services-meta-box', plugins_url( 'assets/js/custom-services-meta-box.js', ICWCU_PLUGIN_FILE ), array(), ICWCU_PLUGIN_VERSION, true ); 58 wp_localize_script( 'icwcu-custom-services-meta-box', 'icwcuFields', self::labels() ); 125 59 } 126 127 60 public function save_custom_services_meta_box_data( $post_id ) { 128 if ( ! isset( $_POST['icwcu_custom_services_meta_box_nonce'] ) || 129 ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['icwcu_custom_services_meta_box_nonce'] ) ), 'icwcu_custom_services_meta_box_nonce' ) ) { 61 if ( ! isset( $_POST['icwcu_custom_services_meta_box_nonce'] ) || ! is_string( $_POST['icwcu_custom_services_meta_box_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['icwcu_custom_services_meta_box_nonce'] ) ), 'icwcu_custom_services_meta_box_nonce' ) ) { 130 62 return; 131 63 } 132 if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE) {64 if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) || wp_is_post_revision( $post_id ) || 'product' !== get_post_type( $post_id ) || ! current_user_can( 'edit_post', $post_id ) ) { 133 65 return; 134 66 } 135 if ( isset( $_POST['post_type'] ) && 'product' === $_POST['post_type']) {136 if ( ! current_user_can( 'edit_post', $post_id) ) {137 return;67 foreach ( array( 'icwcu_custom_services', 'icwcu_custom_servicesradio' ) as $meta ) { 68 if ( isset( $_POST[ $meta ] ) ) { 69 update_post_meta( $post_id, $meta, icwcu_sanitize_services( wp_unslash( $_POST[ $meta ] ) ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- icwcu_sanitize_services validates each row and sanitizes all allowed fields. 138 70 } 139 } else { 71 } 72 foreach ( array( 'icwcu_custom_services_title', 'icwcu_custom_radioservices_title' ) as $meta ) { 73 if ( isset( $_POST[ $meta ] ) && is_string( $_POST[ $meta ] ) ) { 74 update_post_meta( $post_id, $meta, sanitize_text_field( wp_unslash( $_POST[ $meta ] ) ) ); 75 } 76 } 77 } 78 public function add_custom_fields() { 79 global $product; 80 if ( ! $product instanceof WC_Product ) { 140 81 return; 141 82 } 142 143 if ( isset( $_POST['icwcu_custom_services'] ) ) { 144 $custom_services = wp_unslash( $_POST['icwcu_custom_services'] ); 145 if ( is_array( $custom_services ) ) { 146 array_walk_recursive( $custom_services, 'icwcu_sanitize_service_item' ); 147 // Filter out empty services 148 $custom_services = array_filter( $custom_services, function( $service ) { 149 return ! empty( $service['name'] ); 150 } ); 151 } else { 152 $custom_services = array(); 83 foreach ( array( 'checkbox', 'radio' ) as $type ) { 84 $services = icwcu_get_product_services( $product->get_id(), $type ); 85 if ( ! $services ) { 86 continue; 153 87 } 154 update_post_meta( $post_id, 'icwcu_custom_services', $custom_services ); 155 } 156 157 if ( isset( $_POST['icwcu_custom_servicesradio'] ) ) { 158 $custom_servicesradio = wp_unslash( $_POST['icwcu_custom_servicesradio'] ); 159 if ( is_array( $custom_servicesradio ) ) { 160 array_walk_recursive( $custom_servicesradio, 'icwcu_sanitize_service_item' ); 161 $custom_servicesradio = array_filter( $custom_servicesradio, function( $service ) { 162 return ! empty( $service['name'] ); 163 } ); 164 } else { 165 $custom_servicesradio = array(); 88 $nonce = 'icwcu_custom_' . $type . '_nonce'; 89 wp_nonce_field( $nonce . '_action', $nonce, false ); 90 $title_key = 'radio' === $type ? 'icwcu_custom_radioservices_title' : 'icwcu_custom_services_title'; 91 $title = get_post_meta( $product->get_id(), $title_key, true ); 92 echo '<fieldset class="icwcu-custom-services"><legend>' . esc_html( $title ? $title : __( 'Additional Services', 'custom-checkbox-ultimate-for-woocommerce' ) ) . '</legend>'; 93 if ( 'radio' === $type ) { 94 echo '<label class="icwcu-service"><input type="radio" name="icwcu_radio_service" value="" checked> ' . esc_html__( 'No additional radio service', 'custom-checkbox-ultimate-for-woocommerce' ) . '</label>'; 166 95 } 167 update_post_meta( $post_id, 'icwcu_custom_servicesradio', $custom_servicesradio ); 168 } 169 170 171 if ( isset( $_POST['icwcu_custom_services_title'] ) ) { 172 update_post_meta( 173 $post_id, 174 'icwcu_custom_services_title', 175 sanitize_text_field( wp_unslash( $_POST['icwcu_custom_services_title'] ) ) 176 ); 177 } 178 if ( isset( $_POST['icwcu_custom_radioservices_title'] ) ) { 179 update_post_meta( 180 $post_id, 181 'icwcu_custom_radioservices_title', 182 sanitize_text_field( wp_unslash( $_POST['icwcu_custom_radioservices_title'] ) ) 183 ); 184 } 185 } 186 187 public function add_custom_fields() { 188 global $post; 189 $custom_services = get_post_meta( $post->ID, 'icwcu_custom_services', true ); 190 $custom_servicesradio = get_post_meta( $post->ID, 'icwcu_custom_servicesradio', true ); 191 192 wp_nonce_field( 'icwcu_custom_checkbox_nonce_action', 'icwcu_custom_checkbox_nonce' ); 193 wp_nonce_field( 'icwcu_custom_radio_nonce_action', 'icwcu_custom_radio_nonce' ); 194 if ( ! is_array( $custom_services ) ) { 195 $custom_services = array(); 196 } 197 if ( ! is_array( $custom_servicesradio ) ) { 198 $custom_servicesradio = array(); 199 } 200 201 if ( ! empty( $custom_services ) ) { 202 echo '<div class="icwcu-custom-services">'; 203 $custom_services_title = get_post_meta( $post->ID, 'icwcu_custom_services_title', true ); 204 if ( ! empty( $custom_services_title ) ) { 205 echo '<h4 class="icwcu-checkbox-title">' . esc_html( $custom_services_title ) . '</h4><table>'; 206 } else { 207 echo '<h4 class="icwcu-checkbox-title">' . esc_html__( 'Additional Services', 'custom-checkbox-ultimate-for-woocommerce' ) . '</h4><table>'; 96 foreach ( $services as $key => $service ) { 97 $id = 'icwcu-' . $type . '-' . $key; 98 $name = 'radio' === $type ? 'icwcu_radio_service' : 'icwcu_services[]'; 99 echo '<div class="icwcu-service"><label for="' . esc_attr( $id ) . '"><input id="' . esc_attr( $id ) . '" type="' . esc_attr( $type ) . '" name="' . esc_attr( $name ) . '" value="' . esc_attr( $key ) . '" aria-describedby="' . esc_attr( $id . '-description' ) . '"> ' . esc_html( $service['name'] ) . '</label> <span class="icwcu-currency">' . wp_kses_post( wc_price( wc_get_price_to_display( $product, array( 'price' => $service['price'] ) ) ) ) . '</span><small id="' . esc_attr( $id . '-description' ) . '" class="icwcu-service-description">' . esc_html( $service['desc'] ) . '</small></div>'; 208 100 } 209 foreach ( $custom_services as $service ) { 210 if ( ! empty( $service['name'] ) && ! empty( $service['price'] ) ) { 211 echo '<tr><td><label><input type="checkbox" class="icwcu-checkbox" name="' . esc_html( $service['name'] ) . '" value="' . esc_attr( $service['price'] ) . '">' . esc_html( $service['name'] ); 212 213 if ( ! empty( $service['desc'] ) ) { 214 // echo '<span class="icwcu-tooltip">'.wp_kses(icwcu_get_frontend_svg_icon()).'<span class="icwcu-tooltiptext">' . esc_html( $service['desc'] ) . '</span></span></td>'; 215 echo '<span class="icwcu-tooltip">' . 216 wp_kses( icwcu_get_frontend_svg_icon(), array( 217 'svg' => array( 218 'xmlns' => array(), 219 'width' => array(), 220 'height' => array(), 221 'fill' => array(), 222 'class' => array(), 223 'viewBox' => array(), 224 ), 225 'path' => array( 226 'd' => array(), 227 ), 228 ) ) . 229 '<span class="icwcu-tooltiptext">' . esc_html( $service['desc'] ) . '</span></span>'; 230 231 } 232 233 echo '<td></td><td><span class="icwcu-currency">' . esc_html( get_woocommerce_currency_symbol() ) . esc_attr( $service['price'] ) . '</span></td>'; 234 echo '</label></tr>'; 235 } 236 } 237 echo '</table></div>'; 238 } 239 240 if ( ! empty( $custom_servicesradio ) ) { 241 echo '<div class="icwcu-custom-services">'; 242 $custom_services_title = get_post_meta( $post->ID, 'icwcu_custom_radioservices_title', true ); 243 if ( ! empty( $custom_services_title ) ) { 244 echo '<h4 class="icwcu-checkbox-title">' . esc_html( $custom_services_title ) . '</h4><table>'; 245 } else { 246 echo '<h4 class="icwcu-checkbox-title">' . esc_html__( 'Additional Services', 'custom-checkbox-ultimate-for-woocommerce' ) . '</h4><table>'; 247 } 248 foreach ( $custom_servicesradio as $serviceradio ) { 249 if ( ! empty( $serviceradio['name'] ) && ! empty( $serviceradio['price'] ) ) { 250 echo '<tr><td><label><input type="radio" class="icwcu-radio" data-group="icwcupayment" name="' . esc_html( sanitize_title( $serviceradio['name'] ) ) . '" value="' . esc_attr( $serviceradio['price'] ) . '">' . esc_html( $serviceradio['name'] ); 251 252 if ( ! empty( $serviceradio['desc'] ) ) { 253 // echo '<span class="icwcu-tooltip">'.wp_kses(icwcu_get_frontend_svg_icon()).'<span class="icwcu-tooltiptext">' . esc_html( $serviceradio['desc'] ) . '</span></span></td>'; 254 echo '<span class="icwcu-tooltip">' . 255 wp_kses( icwcu_get_frontend_svg_icon(), array( 256 'svg' => array( 257 'xmlns' => array(), 258 'width' => array(), 259 'height' => array(), 260 'fill' => array(), 261 'class' => array(), 262 'viewBox' => array(), 263 ), 264 'path' => array( 265 'd' => array(), 266 ), 267 ) ) . 268 '<span class="icwcu-tooltiptext">' . esc_html( $serviceradio['desc'] ) . '</span></span>'; 269 270 } 271 272 echo '<td></td><td><span class="icwcu-currency">' . esc_html( get_woocommerce_currency_symbol() ) . esc_attr( $serviceradio['price'] ) . '</span></td>'; 273 echo '</label></tr>'; 274 } 275 } 276 echo '</table></div>'; 101 echo '</fieldset>'; 277 102 } 278 103 } -
custom-checkbox-ultimate-for-woocommerce/trunk/includes/class-icwcu-order.php
r3264079 r3759386 6 6 class ICWCU_Order { 7 7 public function __construct() { 8 add_action( 'woocommerce_checkout_create_order_line_item', array( $this, 'add_custom_fields_to_order_items' ), 10, 4 );9 add_action( 'woocommerce_checkout_create_order_line_item', array( $this, 'add_ customradio_fields_to_order_items' ), 10, 4 );8 // Runs for both classic checkout and the Checkout block (Store API). 9 add_action( 'woocommerce_checkout_create_order_line_item', array( $this, 'add_services_to_order_item' ), 10, 4 ); 10 10 } 11 12 public function add_custom_fields_to_order_items( $item, $cart_item_key, $values, $order ) { 13 $custom_services = get_post_meta( $values['product_id'], 'icwcu_custom_services', true ); 14 if ( ! is_array( $custom_services ) ) { 15 $custom_services = array(); 16 } 17 18 if ( ! empty( $custom_services ) ) { 19 foreach ( $custom_services as $service ) { 20 $service_key = sanitize_title( $service['name'] ); 21 if ( isset( $values[ $service_key ] ) ) { 22 $item->add_meta_data( $service['name'], get_woocommerce_currency_symbol() . $values[ $service_key ] ); 23 } 24 } 25 } 26 } 27 28 public function add_customradio_fields_to_order_items( $item, $cart_item_key, $values, $order ) { 29 $custom_servicesradio = get_post_meta( $values['product_id'], 'icwcu_custom_servicesradio', true ); 30 if ( ! is_array( $custom_servicesradio ) ) { 31 $custom_servicesradio = array(); 32 } 33 34 if ( ! empty( $custom_servicesradio ) ) { 35 foreach ( $custom_servicesradio as $serviceradio ) { 36 $service_keyradio = sanitize_title( $serviceradio['name'] ); 37 if ( isset( $values[ $service_keyradio ] ) ) { 38 $item->add_meta_data( $serviceradio['name'], get_woocommerce_currency_symbol() . $values[ $service_keyradio ] ); 39 } 40 } 11 12 public function add_services_to_order_item( $item, $cart_item_key, $values, $order ) { 13 foreach ( ICWCU_Cart::get_cart_item_services( $values ) as $service ) { 14 $item->add_meta_data( $service['name'], icwcu_format_price( $service['price'] ) ); 41 15 } 42 16 } -
custom-checkbox-ultimate-for-woocommerce/trunk/includes/class-icwcu-settings.php
r3264079 r3759386 47 47 global $post; 48 48 49 if ( is_product()) {49 if ( function_exists( 'is_product' ) && is_product() && $post instanceof WP_Post ) { 50 50 $custom_servicesradio = get_post_meta( $post->ID, 'icwcu_custom_servicesradio', true ); 51 51 52 52 if ( ! empty( $custom_servicesradio ) ) { 53 wp_enqueue_script( 'icwcu-radio-script', plugin_dir_url( __FILE__ ) . '../assets/js/custom-checkbox-frontend.js', array( 'jquery' ), ICWCU_PLUGIN_VERSION, true);53 wp_enqueue_script( 'icwcu-radio-script', plugin_dir_url( __FILE__ ) . '../assets/js/custom-checkbox-frontend.js', array(), ICWCU_PLUGIN_VERSION, array( 'in_footer' => true, 'strategy' => 'defer' ) ); 54 54 } 55 55 } -
custom-checkbox-ultimate-for-woocommerce/trunk/includes/enqueue.php
r3264079 r3759386 1 1 <?php 2 /* 3 Plugin Name: Custom Checkbox Ultimate for WooCommerce 4 * 5 */ 2 defined( 'ABSPATH' ) || exit; 6 3 7 if ( ! defined( 'ABSPATH' ) ) { 8 exit; // Exit if accessed directly 9 } 10 /* 11 Enqueue Styles 12 */ 13 // Enqueue custom SVG icon 14 add_action('admin_enqueue_scripts', 'icwcu_enqueue_custom_svg_icon'); 15 function icwcu_enqueue_custom_svg_icon() { 16 wp_enqueue_style('icwcu-custom-svg-icon', plugin_dir_url(__FILE__) . '../assets/css/icwcu-admin-styling.css', array(), ICWCU_PLUGIN_VERSION ); 17 4 add_action( 'admin_enqueue_scripts', 'icwcu_enqueue_admin_styles' ); 5 function icwcu_enqueue_admin_styles( $hook ) { 6 $screen = get_current_screen(); 7 if ( 'toplevel_page_ic-woo-ultimate' === $hook || ( $screen && 'product' === $screen->post_type ) ) { 8 wp_enqueue_style( 'icwcu-admin-style', plugins_url( 'assets/css/icwcu-admin-styling.css', ICWCU_PLUGIN_FILE ), array(), ICWCU_PLUGIN_VERSION ); 9 } 18 10 } 19 11 20 // Enqueue custom CSS for frontend product page 21 add_action('wp_enqueue_scripts', 'icwcu_enqueue_custom_css'); 12 add_action( 'wp_enqueue_scripts', 'icwcu_enqueue_custom_css' ); 22 13 function icwcu_enqueue_custom_css() { 23 // Check if WooCommerce is active 24 if (class_exists('WooCommerce') && function_exists('is_product') && is_product()) { 25 wp_enqueue_style('icwcu-custom-services-style', plugin_dir_url(__FILE__) . '../assets/css/icwcu-services-style.css', array(),ICWCU_PLUGIN_VERSION ); 14 if ( function_exists( 'is_product' ) && is_product() ) { 15 wp_enqueue_style( 'icwcu-custom-services-style', plugins_url( 'assets/css/icwcu-services-style.css', ICWCU_PLUGIN_FILE ), array(), ICWCU_PLUGIN_VERSION ); 26 16 } 27 17 } 28 function icwcu_enqueue_admin_styles($hook) {29 // Check if we are on the plugin's dashboard page30 if ( 'toplevel_page_ic-woo-ultimate' != $hook ) {31 return;32 }33 34 // Enqueue the admin CSS file35 wp_enqueue_style(36 'icwcu-admin-style',37 plugin_dir_url(__FILE__) . '../assets/css/icwcu-admin-styling.css', // Path to your CSS file38 [],39 ICWCU_PLUGIN_VERSION // Version40 );41 }42 add_action( 'admin_enqueue_scripts', 'icwcu_enqueue_admin_styles' ); -
custom-checkbox-ultimate-for-woocommerce/trunk/includes/helpers.php
r3264079 r3759386 1 1 <?php 2 /*3 * Plugin Name: Custom Checkbox Ultimate for WooCommerce4 *5 */6 2 if ( ! defined( 'ABSPATH' ) ) { 7 3 exit; … … 49 45 } 50 46 } 47 48 if ( ! function_exists( 'icwcu_get_product_services' ) ) { 49 /** 50 * Get the saved services of a product. 51 * 52 * @param int $product_id Product ID. 53 * @param string $type 'checkbox' or 'radio'. 54 * @return array Services keyed by slug: array( 'name' => string, 'price' => float, 'desc' => string ). 55 */ 56 function icwcu_get_product_services( $product_id, $type = 'checkbox' ) { 57 $meta_key = ( 'radio' === $type ) ? 'icwcu_custom_servicesradio' : 'icwcu_custom_services'; 58 $services = get_post_meta( $product_id, $meta_key, true ); 59 $result = array(); 60 61 if ( ! is_array( $services ) ) { 62 return $result; 63 } 64 65 foreach ( icwcu_sanitize_services( $services ) as $index => $service ) { 66 if ( ! is_array( $service ) || empty( $service['name'] ) ) { 67 continue; 68 } 69 $key = sanitize_title( $service['name'] ); 70 if ( '' === $key ) { 71 continue; 72 } 73 if ( isset( $result[ $key ] ) ) { 74 $key .= '-' . sanitize_key( (string) $index ); 75 } 76 $result[ $key ] = array( 77 'name' => (string) $service['name'], 78 'price' => isset( $service['price'] ) ? max( 0, (float) $service['price'] ) : 0, 79 'desc' => isset( $service['desc'] ) ? (string) $service['desc'] : '', 80 ); 81 } 82 83 return $result; 84 } 85 } 86 87 /** Normalize submitted rows, removing gaps and rejecting malformed fields. */ 88 function icwcu_sanitize_services( $rows ) { 89 $result = array(); 90 if ( ! is_array( $rows ) ) { 91 return $result; 92 } 93 foreach ( $rows as $row ) { 94 if ( ! is_array( $row ) || ! isset( $row['name'] ) || ! is_string( $row['name'] ) ) { 95 continue; 96 } 97 $name = sanitize_text_field( $row['name'] ); 98 if ( '' === trim( $name ) ) { 99 continue; 100 } 101 $price = isset( $row['price'] ) && is_scalar( $row['price'] ) ? (float) wc_format_decimal( $row['price'] ) : 0; 102 $result[ count( $result ) + 1 ] = array( 103 'name' => $name, 104 'price' => is_finite( $price ) ? max( 0, $price ) : 0, 105 'desc' => isset( $row['desc'] ) && is_string( $row['desc'] ) ? sanitize_textarea_field( $row['desc'] ) : '', 106 ); 107 } 108 return $result; 109 } 110 111 if ( ! function_exists( 'icwcu_format_price' ) ) { 112 /** 113 * Format a price as plain text (safe for cart item data and order item meta). 114 * 115 * @param float $price Price. 116 * @return string 117 */ 118 function icwcu_format_price( $price ) { 119 return html_entity_decode( wp_strip_all_tags( wc_price( $price ) ), ENT_QUOTES, 'UTF-8' ); 120 } 121 } -
custom-checkbox-ultimate-for-woocommerce/trunk/includes/plugin-info.php
r3264079 r3759386 1 1 <?php 2 if ( ! defined( 'ABSPATH' ) ) { 3 exit; 4 } 2 defined( 'ABSPATH' ) || exit; 5 3 ?> 6 <div class="wrap"> 7 <h1><?php esc_html_e( 'Custom Checkbox Ultimate for WooCommerce Documentation', 'custom-checkbox-ultimate-for-woocommerce' ); ?></h1> 8 9 <h2><?php esc_html_e( 'Introduction', 'custom-checkbox-ultimate-for-woocommerce' ); ?></h2> 10 <p><?php esc_html_e( 'Custom Checkbox Ultimate for WooCommerce is a plugin that allows you to add customizable checkbox options on WooCommerce product pages for additional services and charges.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></p> 11 12 <h2><?php esc_html_e( 'Installation', 'custom-checkbox-ultimate-for-woocommerce' ); ?></h2> 4 <div class="wrap icwcu-guide"> 5 <h1><?php esc_html_e( 'Custom Checkbox Ultimate: setup guide', 'custom-checkbox-ultimate-for-woocommerce' ); ?></h1> 6 <p><?php esc_html_e( 'Offer optional services such as gift wrapping, assembly, or premium packaging. Configure each product in the classic WooCommerce product editor.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></p> 7 <h2><?php esc_html_e( '1. Configure a product', 'custom-checkbox-ultimate-for-woocommerce' ); ?></h2> 13 8 <ol> 14 <li><?php esc_html_e( 'Navigate to Plugins > Add New in your WordPress dashboard.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></li> 15 <li><?php esc_html_e( 'Click on "Upload Plugin" and select the plugin ZIP file.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></li> 16 <li><?php esc_html_e( 'Click "Install Now" and then "Activate" to enable the plugin.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></li> 9 <li><?php esc_html_e( 'Install and activate WooCommerce, then open Products > All Products and edit a simple or variable product.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></li> 10 <li><?php esc_html_e( 'Find Custom Services - IC Woo Ultimate below the editor. If it is hidden, enable it in Screen Options.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></li> 11 <li><?php esc_html_e( 'In Checkbox Type, enter a group title and services customers can combine. In Radio Type, enter alternatives where customers choose at most one.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></li> 12 <li><?php esc_html_e( 'For each row, enter a service name, a non-negative price in the store currency, and an optional description. Zero-priced services are supported. Blank names are ignored.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></li> 13 <li><?php esc_html_e( 'Use Add More Fields for additional rows. To remove a service, clear its name. Update or publish the product.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></li> 17 14 </ol> 18 19 <h2><?php esc_html_e( 'Setup', 'custom-checkbox-ultimate-for-woocommerce' ); ?></h2> 20 <ol> 21 <li><?php esc_html_e( 'Go to WooCommerce > Settings > Custom Checkbox Ultimate.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></li> 22 <li><?php esc_html_e( 'Configure the default settings for your checkboxes, such as label text, default state, and additional costs.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></li> 23 <li><?php esc_html_e( 'Save your settings.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></li> 24 </ol> 25 26 <h2><?php esc_html_e( 'Usage', 'custom-checkbox-ultimate-for-woocommerce' ); ?></h2> 27 <ol> 28 <li><?php esc_html_e( 'Edit the product where you want to add a custom checkbox.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></li> 29 <li><?php esc_html_e( 'In the product data section, navigate to the "Custom Checkbox" tab.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></li> 30 <li><?php esc_html_e( 'Enable the checkbox option and customize the label, description, and additional cost as needed.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></li> 31 <li><?php esc_html_e( 'Update or publish the product to apply the changes.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></li> 32 </ol> 33 34 <h2><?php esc_html_e( 'Support', 'custom-checkbox-ultimate-for-woocommerce' ); ?></h2> 35 <p><?php esc_html_e( 'For further assistance, please visit our support page or contact us at support@insertcart.com.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></p> 15 <h2><?php esc_html_e( '2. Test the customer journey', 'custom-checkbox-ultimate-for-woocommerce' ); ?></h2> 16 <p><?php esc_html_e( 'Open the product page, select services, choose a variation if applicable, and add the product to the cart. Radio services include a no-service option. Selections are optional and are not preselected.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></p> 17 <p><?php esc_html_e( 'The product headline price does not change before adding to cart. In the cart, selected service costs are added to each product unit. For a product priced at 20 with a service priced at 5, quantity 2 has a subtotal of 50 before tax, discounts, and shipping. Taxes and coupons follow the product settings in WooCommerce.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></p> 18 <h2><?php esc_html_e( '3. Review orders', 'custom-checkbox-ultimate-for-woocommerce' ); ?></h2> 19 <p><?php esc_html_e( 'Selected service names and unit costs appear beneath the product in the cart and checkout. After checkout, find them on the order line item in WooCommerce > Orders. They also appear in standard WooCommerce order details and emails. Refunds use the product line amount; services are not separate fee lines.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></p> 20 <h2><?php esc_html_e( 'Compatibility and limitations', 'custom-checkbox-ultimate-for-woocommerce' ); ?></h2> 21 <p><?php esc_html_e( 'Variable products share services configured on the parent product. Existing selections work with classic and block cart/checkout and with HPOS. Service selection requires the standard WooCommerce product add-to-cart form; the block Add to Cart form and direct Store API selection are not provided. Shop-loop quick add does not collect services. Grouped, external, subscription, bundle, and custom product forms need separate testing.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></p> 22 <h2><?php esc_html_e( 'Troubleshooting and privacy', 'custom-checkbox-ultimate-for-woocommerce' ); ?></h2> 23 <p><?php esc_html_e( 'If options are missing, confirm that service names are saved and the theme renders the standard add-to-cart hook. If selections are missing from the cart, refresh the product page and exclude cart and checkout from full-page caching. Test theme and add-on conflicts on a staging site. After changing prices, remove the old cart line and add the product again.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></p> 24 <p><?php esc_html_e( 'The plugin sends no analytics or service data to an external server. Configuration is stored with products; selections are stored in WooCommerce carts and order line metadata. Deactivation preserves configuration and orders.', 'custom-checkbox-ultimate-for-woocommerce' ); ?></p> 25 <p><a class="button" href="<?php echo esc_url( admin_url( 'edit.php?post_type=product' ) ); ?>"><?php esc_html_e( 'Open products', 'custom-checkbox-ultimate-for-woocommerce' ); ?></a> <a class="button" href="<?php echo esc_url( ICWCU_DOCUMENTATION_URL ); ?>" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'Online documentation', 'custom-checkbox-ultimate-for-woocommerce' ); ?></a> <a href="https://wordpress.org/support/plugin/custom-checkbox-ultimate-for-woocommerce/"><?php esc_html_e( 'Support forum', 'custom-checkbox-ultimate-for-woocommerce' ); ?></a></p> 36 26 </div> 37 -
custom-checkbox-ultimate-for-woocommerce/trunk/readme.txt
r3264079 r3759386 1 1 === Custom Checkbox Ultimate for WooCommerce === 2 2 Contributors: sandy786 3 Tags: WooCommerce, checkbox, additional services, customizable options, e-commerce 4 Plugin URI: https://www.insertcart.com/insertcart-custom-checkbox-ultimate-for-woocommerce-documentation/ 5 Author URI: https://www.insertcart.com/ 3 Tags: woocommerce, checkbox, product add-ons, extra services, radio buttons 6 4 Requires at least: 6.3 7 Tested up to: 6.75 Tested up to: 7.1 8 6 Requires PHP: 7.2 9 Stable tag: 1.3 10 License: GPL v2 or later 7 Requires Plugins: woocommerce 8 Stable tag: 1.4 9 License: GPLv2 or later 11 10 License URI: https://www.gnu.org/licenses/gpl-2.0.html 12 11 13 Add customizable checkbox options on WooCommerce product pages for additional services and charges.12 Add optional checkbox and radio services to WooCommerce products, with per-unit prices and service details in the cart and orders. 14 13 15 14 == Description == 16 Custom Checkbox Ultimate for Woocommerce is a powerful WooCommerce extension that allows store owners to offer additional services and charges to customers through customizable checkboxes on product pages. Enhance your product offerings by enabling customers to select extra features, services, or options, seamlessly integrating additional costs into their purchases.17 15 18 == Key Features == 16 Offer gift wrapping, assembly, premium packaging, and other optional services on WooCommerce product pages. Configure services for each product in the classic product editor. 19 17 20 **Customizable Checkbox Options:** Easily add multiple checkbox fields to your WooCommerce product pages for additional services. 21 **Dynamic Pricing:** Automatically adjust product prices based on selected additional services. 22 **Service Descriptions:** Provide detailed descriptions for each additional service using tooltips for better customer understanding. 23 **Admin-Friendly Interface:** Manage and configure additional services directly from the product edit screen with an intuitive meta box. 24 **Order Integration:** Ensure that selected services and their costs are accurately reflected in the cart, checkout, and order details. 25 **Flexible Field Management:** Add as many custom service fields as needed with ease. 18 * Checkbox services: customers can select several options. 19 * Radio services: customers choose at most one option or no additional service. 20 * Set a name, non-negative price, and description for each service. Free services are supported. 21 * Start with five rows per type and add more when needed. 22 * Service prices come from saved product settings, never customer-submitted prices. 23 * Selected services increase the product unit price and appear in the cart, checkout, order details, and standard emails. 24 * Works with simple products and parent-level services on variable products. 25 * Supports HPOS and classic or block cart/checkout for items added through the standard product form. 26 26 27 Check out our [Documentation](https://www.insertcart.com/insertcart-custom-checkbox-ultimate-for-woocommerce-documentation/), [Support Ticket](https://www.insertcart.com/support-ticket/), and [Contact us](https://www.insertcart.com/contact-us/).27 Descriptions are displayed beside the service options, including for keyboard and touch users. The product headline price does not update as options are selected; the adjusted price appears in the cart. 28 28 29 = =Installation==29 = Documentation and first setup = 30 30 31 Upload the Plugin: 31 Open **IC Woo Ultimate** in the WordPress dashboard for the included setup guide. You can also use **Plugins > Setup guide** or the guide link inside the Custom Services product meta box. The included guide works without an external website. 32 32 33 * Download the plugin zip file. 34 * In your WordPress dashboard, navigate to Plugins > Add New. 35 * Click on the Upload Plugin button at the top. 36 * Choose the downloaded zip file and click Install Now. 37 * Activate the Plugin: 38 * After installation, click on Activate Plugin. 39 * Ensure WooCommerce is Active: 33 [Online documentation](https://www.insertcart.com/insertcart-custom-checkbox-ultimate-for-woocommerce-documentation/) | [Support forum](https://wordpress.org/support/plugin/custom-checkbox-ultimate-for-woocommerce/) | [Contact InsertCart](https://www.insertcart.com/contact-us/) 40 34 41 This plugin requires WooCommerce to be installed and activated. 42 If WooCommerce is not active, you will receive an admin notice prompting you to activate it. 35 = How pricing works = 43 36 44 ==Quick Start== 37 Services are charged per product unit, in the store currency. A product priced at 20 with a service priced at 5 has a unit price of 25. Quantity 2 has a subtotal of 50 before taxes, discounts, and shipping. WooCommerce applies product taxes and coupons to the combined product price. Services are not separate cart fees. 45 38 46 * Adding Custom Services to a Product 39 = Compatibility limits = 47 40 48 Navigate to Products: 41 Service selection uses the standard WooCommerce product add-to-cart form. This release does not provide selection controls for the block Add to Cart form, direct Store API additions, shop-loop quick add, or third-party custom product forms. Variable products share the parent product's services. Test grouped, external, bundled, and subscription products separately. 49 42 50 * In your WordPress dashboard, go to Products > All Products. 51 * Click on the product you want to add additional services to, or create a new product. 52 * Locate the Custom Services Meta Box: 53 * Scroll down to find the Custom Services – IC Woo Ultimate meta box. 43 = Privacy = 54 44 55 ==Configure the Services== 45 No analytics or service data is sent to an external server. Product settings are stored in WordPress product metadata. Selections are stored in WooCommerce carts and order item metadata. Deactivation preserves settings and historical orders. 56 46 57 **Custom Services Title:** Enter a title that will appear above the checkbox options on the product page (e.g., "Add-Ons", "Extra Services"). 58 **Service Fields:** By default, five service fields are available. For each service, provide: 59 **Service Name:** The label for the checkbox (e.g., "Gift Wrapping"). 60 **Service Price:** The additional cost associated with the service. 61 **Service Description: (Optional)** A brief description that appears as a tooltip for more information. 62 **Add More Fields:** Click the **+Add More Fields** button to add additional service options beyond the default five. 63 **Save the Product:** Once configured, click Update or Publish to save your changes. 47 == Installation == 64 48 65 Displaying Custom Services on the Frontend 66 On the product page, customers will see the Custom Services Title followed by a list of checkbox options with their respective prices.67 Selecting a checkbox will automatically update the product price in the cart to include the additional service cost.68 Service descriptions are available as tooltips for more detailed information.69 Managing Orders 70 Selected additional services and their costs are displayed in the cart, checkout, and order details, ensuring transparency and accurate billing.49 1. Install and activate WooCommerce. 50 2. Install Custom Checkbox Ultimate for WooCommerce from Plugins > Add New, or upload the plugin ZIP. 51 3. Activate the plugin and open IC Woo Ultimate for the included guide. 52 4. Edit a product and locate Custom Services - IC Woo Ultimate. If hidden, enable it in Screen Options. 53 5. Configure Checkbox Type and/or Radio Type, then update the product. 54 6. Test the product page, cart, checkout, and a sample order before using it on your live store. 71 55 56 == Frequently Asked Questions == 72 57 58 = Where are the service settings? = 59 Edit a product in the classic product editor and use Custom Services - IC Woo Ultimate. There is no global WooCommerce settings tab for configuring service rows. 60 61 = Can I add more than five services? = 62 Yes. Use Add More Fields in either type. Blank service names are ignored. Clear the name and update the product to remove a service. 63 64 = Are services required or preselected? = 65 No. Checkboxes start unchecked. Radio services include a no-service choice. 66 67 = Can services be free? = 68 Yes. Enter 0 as the service price. Negative prices are not supported. 69 70 = Do services work with variations? = 71 Yes. Configure services on the parent product; they are added to the chosen variation's unit price. Per-variation service definitions are not provided. 72 73 = Why is the product page price unchanged? = 74 This release shows each service price beside its option. The product's combined price is calculated after adding it to the cart. 75 76 = Why are selections missing? = 77 Check that your theme renders the standard add-to-cart form and refresh the product page to obtain a fresh form nonce. Exclude cart and checkout from page caching. Shop-loop quick add and block product forms do not collect selections. 78 79 = What happens when a service price changes? = 80 New additions use the saved product price. Existing 1.4 cart selections retain their recorded service amount; remove and re-add the product to use new settings. Legacy 1.3 cart entries are resolved against current settings. Historical orders retain their stored line item details. 73 81 74 82 == Screenshots == 75 83 76 1. Checkbox you can add fields and services name with price and description. 77 2. Radio button you can select only one of theme. 78 3. Pricing will display on WooCommerce Product Page. 79 4. Check screen option in product page to hide and show plugin settings 80 81 82 ===Frequently Asked Questions=== 83 84 = Does this plugin work with all WooCommerce themes? = 85 Yes, the plugin is designed to be compatible with most WooCommerce-compatible themes. However, styling may vary based on your theme. You can use custom CSS to adjust the appearance as needed. 86 87 = Can I add more than five custom service fields? = 88 Absolutely! While the plugin initializes with five fields by default, you can add as many additional service fields as required using the + Add More Fields button in the meta box. 89 90 = Is it possible to set different prices for different user roles? = 91 By default, the plugin does not support role-based pricing. However, with custom development or additional extensions, this functionality can be achieved. 92 93 = How are the additional service costs handled during checkout? = 94 Selected additional services are added to the cart item data, ensuring that the total price reflects the added costs. These details are visible in the cart, checkout, and order summaries. 95 96 = What happens if WooCommerce is deactivated? = 97 If WooCommerce is not active, the plugin will display an admin notice prompting you to activate WooCommerce, as it is a required dependency. 98 99 = More Features = 100 Working on more features and settings like Shortcodes, Styling options etc & more. [Contact us](https://www.insertcart.com/contact-us/) for more requests. 84 1. Checkbox service fields with names, prices, and descriptions, rendered with a sample product. 85 2. Radio service fields with alternative choices and an additional row. 86 3. Product service options, descriptions, prices, and the no-service radio choice. 87 4. The setup guide included in version 1.4. Screenshots use actual plugin output in a local test harness. 101 88 102 89 == Changelog == 103 1.3 104 Initial release of Custom Checkbox Ultimate for WooCommerce. 105 Features include adding customizable checkbox options, dynamic pricing adjustments, service descriptions with tooltips, and order integration. 106 Upgrade Notice 107 1.0 108 Initial release. 109 License 110 This plugin is licensed under the GPL v2 or later. You can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or any later version. 90 91 = 1.4 = 92 * Security: use saved service prices instead of submitted form prices; validate malformed requests and product editor fields. 93 * Fix checkbox names containing spaces or capital letters and group radio choices correctly. 94 * Keep add-on prices on cart lines and avoid adding charges twice during repeated totals calculations. 95 * Preserve existing services when saved rows contain gaps and support free services. 96 * Improve accessible service descriptions and provide an explicit no-radio-service option. 97 * Add an accurate built-in setup guide, product help link, Plugins list links, and a one-time introduction for store managers. 98 * Guard against missing WooCommerce and limit admin styling to plugin/product screens. 99 * Declare HPOS and Cart/Checkout block compatibility for selections made on standard product forms. 100 * Verified locally with WordPress 7.1.3 and WooCommerce 10.3.5. 101 102 = 1.3 = 103 * Initial release with checkbox and radio services, service descriptions, and cart/order integration. 111 104 112 105 == Upgrade Notice == 113 106 114 No major requirements 107 = 1.4 = 108 Security and reliability update. Service prices now come from saved product settings. Includes a setup guide and improved cart/order handling. Test on staging before updating your live store.
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)