├── ShippingAbstract.php ├── ShoppingOrdersManagement.css ├── ShippingFixedCost.module ├── PaymentAbstract.php ├── PaymentSimpleExample.module ├── PaymentInvoice.module ├── README.md ├── PaymentExample.module ├── ShoppingStepsMarkup.module ├── ShoppingOrdersManagement.module ├── ShoppingCart.module └── ShoppingCheckout.module /ShippingAbstract.php: -------------------------------------------------------------------------------- 1 | 'Fixed cost shipping', 11 | 'version' => 001, 12 | 'summary' => 'Simple shipping option, which sets fixed shipping cost for all orders.', 13 | 'singular' => false, 14 | 'autoload' => false 15 | ); 16 | } 17 | public function init() { 18 | $this->title = $this->_("Fixed shipping cost"); 19 | } 20 | 21 | public function calculateShippingCost() { 22 | return $this->shippingCost; 23 | } 24 | 25 | static public function getModuleConfigInputfields(Array $data) { 26 | // this is a container for fields, basically like a fieldset 27 | $fields = new InputfieldWrapper(); 28 | 29 | // since this is a static function, we can't use $this->modules, so get them from the global wire() function 30 | $modules = wire('modules'); 31 | 32 | $field = $modules->get("InputfieldFloat"); 33 | $field->attr('name', 'shippingCost'); 34 | $field->attr('value', $data['shippingCost']); 35 | $field->label = "Shipping cost for all orders"; 36 | $fields->add($field); 37 | 38 | 39 | return $fields; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /PaymentAbstract.php: -------------------------------------------------------------------------------- 1 | modules->getModuleConfigData('ShoppingCheckout'); 14 | if (!isset($data['completedUrlSegment'])) { 15 | $module = $this->modules->get('ShoppingCheckout'); 16 | $data = $module::getDefaultData(); 17 | } 18 | $page = $this->page; 19 | $this->currentUrl = $page->url . $data['paymentUrlSegment'] . '/'; 20 | $this->completedUrl = $page->url . $data['completedUrlSegment'] . '/'; 21 | } 22 | 23 | /* 24 | * 25 | * returns nothing. You should edit and save $order page. If payment was succesful, 26 | * then do $order->removeStatus(Page::statusUnpublished) and remember to save the order! 27 | * 28 | * If order was also paid, then do $order->sc_paid = time(); 29 | * 30 | * If order was not paid, but it was succesful (like invoice, money on delivery etc) 31 | * then just publish the order, but do not set sc_paid value. 32 | * 33 | * After you have manipulated the order, then just to redirect to $this->completedUrl 34 | * 35 | * 36 | * @param Page $order keeps the page object for the order 37 | * 38 | */ 39 | abstract function processPayment(Page $order); 40 | 41 | } 42 | -------------------------------------------------------------------------------- /PaymentSimpleExample.module: -------------------------------------------------------------------------------- 1 | 'Simple Payment Example', 11 | 'version' => 001, 12 | 'summary' => 'Simplest possible payment example module for PW Shop', 13 | 'singular' => false, 14 | 'autoload' => false 15 | ); 16 | } 17 | public function init() { 18 | $this->title = $this->_("Simple Payment Example"); 19 | } 20 | 21 | /* 22 | * 23 | * returns nothing. You should edit and save $order page. If payment was succesful, 24 | * then do $order->removeStatus(Page::statusUnpublished) and remember to save the order! 25 | * 26 | * If order was also paid, then do $order->sc_paid = time(); 27 | * 28 | * If order was not paid, but it was succesful (like invoice, money on delivery etc) 29 | * then just publish the order, but do not set sc_paid value. 30 | * 31 | * After you have manipulated the order, then just to redirect to $this->completedUrl 32 | * 33 | * 34 | * @param Page $order keeps the page object for the order 35 | * 36 | */ 37 | public function processPayment(Page $order) { 38 | 39 | /* 40 | * If you need to know how to access order details, please look for PaymentExample.module 41 | */ 42 | 43 | $order->setOutputFormatting(false); 44 | $order->sc_paid = time(); // Set order as paid 45 | $order->removeStatus(Page::statusUnpublished); // Set order as finished 46 | $order->save(); 47 | 48 | /* 49 | * You always end with redirect to $this->completedUrl; 50 | */ 51 | $this->session->redirect($this->completedUrl); 52 | 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /PaymentInvoice.module: -------------------------------------------------------------------------------- 1 | 'Invoice Payment', 11 | 'version' => 001, 12 | 'summary' => 'Very simple payment method, where payment will be invoiced later on.', 13 | 'singular' => false, 14 | 'autoload' => false 15 | ); 16 | } 17 | public function init() { 18 | $this->title = $this->_("Invoice"); 19 | } 20 | 21 | /* 22 | * 23 | * returns nothing. You should edit and save $order page. If payment was succesful, 24 | * then do $order->removeStatus(Page::statusUnpublished) and remember to save the order! 25 | * 26 | * If order was also paid, then do $order->sc_paid = time(); 27 | * 28 | * If order was not paid, but it was succesful (like invoice, money on delivery etc) 29 | * then just publish the order, but do not set sc_paid value. 30 | * 31 | * After you have manipulated the order, then just to redirect to $this->completedUrl 32 | * 33 | * 34 | * @param Page $order keeps the page object for the order 35 | * 36 | */ 37 | public function processPayment(Page $order) { 38 | 39 | /* 40 | * If you need to know how to access order details, please look for PaymentExample.module 41 | */ 42 | 43 | $order->setOutputFormatting(false); 44 | //$order->sc_paid = time(); // No payment here 45 | $order->removeStatus(Page::statusUnpublished); // Set order as finished 46 | $order->save(); 47 | 48 | /* 49 | * You always end with redirect to $this->completedUrl; 50 | */ 51 | $this->session->redirect($this->completedUrl); 52 | 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 1. Install Shopping Cart 2 | 2. Add sc_price field to your product template (can be added into multiple templates) 3 | 3. Edit few of your products and give them price 4 | 4. Add get("ShoppingCart")->renderAddToCart(); ?> to your product template. If you don't like the markup it generates, you can put the wanted markup directly into template. 5 | 5. At this point you can add products to your cart. If you want to show somewhere how many products there is in your cart, do this in your template: 6 | echo $modules->get('ShoppingCart')->getNumberOfItems(false); // Total number of items 7 | or 8 | echo $modules->get('ShoppingCart')->getNumberOfItems(); // Different items only, ie. qty doesn't matter 9 | 6. How about the actual cart page, where you can see all the products in your cart? Add this to any of your templates where you want to see it: 10 | get("ShoppingCart")->renderCart() ?> 11 | This is starting to take shape. But there is no checkout at all? How to actually order something? 12 | 7. Install Shopping Checkout module 13 | 8. Now you see "Continue to checkout" under your renderCart() output. Clicking that would result in page not found error. Shopping Checkout module creates system template called sc-checkout. You need to create corresponding template file. So create sc-checkout.php to your /site/templates/ folder. Only code you need to put there is: 14 | get("ShoppingCheckout")->renderCheckout(); ?> 15 | 9. If you need to customize the fields etc, you can do that in certain degree by editing the ShoppingCheckout module. Also, pw-shop is fully multilang, so you probably do want to translate the module files. 16 | 10. Checkout doesn't let you go through unless you install at least one payment method. So do so if you want to continue. 17 | 11. You might want to show checkout steps (kind of a breadcrumb). That is possible with yet another module. Just put this to your sc-checkout template: 18 | get("ShoppingStepsMarkup")->render(); ?> 19 | 12. When someone orders something, you probably want to see those in your admin. That is what Shopping Orders Management module is for. Go ahead and install that also (it has basic functionality, but very much wip). 20 | 21 | You find PayPal payment method module from here: https://github.com/apeisa/PaymentPaypal 22 | -------------------------------------------------------------------------------- /PaymentExample.module: -------------------------------------------------------------------------------- 1 | 'Example Payment Method', 11 | 'version' => 001, 12 | 'summary' => 'Demonstrates how to create payment methods for PW Shop', 13 | 'singular' => false, 14 | 'autoload' => false 15 | ); 16 | } 17 | public function init() { 18 | 19 | } 20 | 21 | /* 22 | * 23 | * returns nothing. You should edit and save $order page. If payment was succesful, 24 | * then do $order->removeStatus(Page::statusUnpublished) and remember to save the order! 25 | * 26 | * If order was also paid, then do $order->sc_paid = time(); 27 | * 28 | * If order was not paid, but it was succesful (like invoice, money on delivery etc) 29 | * then just publish the order, but do not set sc_paid value. 30 | * 31 | * After you have manipulated the order, then just to redirect to $this->completedUrl 32 | * 33 | * 34 | * @param Page $order keeps the page object for the order 35 | * 36 | */ 37 | public function processPayment(Page $order) { 38 | if ($this->input->get->ok) { 39 | 40 | if ($this->input->get->ok == 'fail') { 41 | // We don't remove statusUnpublished. That means failed or cancelled payment 42 | $this->session->redirect($this->completedUrl); 43 | } 44 | 45 | if ($this->input->get->ok == 'paid') { 46 | $order->setOutputFormatting(false); 47 | $order->sc_paid = time(); 48 | } 49 | 50 | $order->removeStatus(Page::statusUnpublished); 51 | $order->save(); 52 | 53 | $this->session->redirect($this->completedUrl); 54 | 55 | 56 | } 57 | 58 | // No need to really do anything like this on payment module, this is here just for educational purposes): 59 | $out = "
"; 52 | if ($anchors) $out .= ""; 53 | $out .= $this->_('Shopping cart'); 54 | if ($anchors) $out .= ""; 55 | $out .= "
"; 67 | if ($anchors) $out .= ""; 68 | $out .= $this->_('My information'); 69 | if ($anchors) $out .= ""; 70 | $out .= "
". $this->_('Place order') ."
". $this->_('Pay order') ."
". $this->_('Completed') ."
" . 138 | $order->sc_firstname . " ". 139 | $order->sc_lastname ."
".
140 | $order->sc_streetaddress . "
".
141 | $order->sc_zip . " " . $order->sc_city."
".
142 | $order->sc_country . "
$order->sc_phone
$order->email
$order->sc_custom1
"; 155 | } 156 | if ($order->sc_custom2) { 157 | $out .= "$order->sc_custom2
"; 159 | } 160 | $out .= "". $this->_("Payment method: ") . "" . $order->sc_paymentmethod . "
" . $this->_("Mark this paid after you have received the payment.") . "
". $this->_("Payment method: ") . "" . $order->sc_paymentmethod . "
" . $this->_("Date of payment received: ") . date($this->config->dateFormat,$order->sc_paid) . "
" . $this->_("Mark this processed after you have delivered the goods.") . "
"; 237 | $form = $this->modules->get('InputfieldForm'); 238 | $form->attr('action', './?id=' . $id); 239 | $form->attr('method', 'post'); 240 | 241 | $field = $this->modules->get('InputfieldSubmit'); 242 | $field->attr('id+name', 'processed'); 243 | $field->attr('value', $this->_('Mark as Processed')); 244 | $form->append($field); 245 | 246 | $out .= $form->render(); 247 | $out .= "" . $this->_("This order has been marked processed on: ") . date($this->config->dateFormat,$order->sc_processed) . "
"; 252 | $out .= "" . $this->_("Your shopping cart is empty.") . "
"; 187 | } 188 | 189 | if (!$this->input->post->submit) { 190 | $this->validateInformation(false); 191 | } else { 192 | $orderArray = $this->validateInformation(true); 193 | if($orderArray['valid']) { 194 | $this->session->redirect("./{$this->confirmationUrlSegment}/"); 195 | } 196 | } 197 | 198 | $out = ""; 310 | $out .= ""; 323 | $out .= "
"; 324 | 325 | } else { 326 | $out = ""; 327 | } 328 | 329 | return $out; 330 | } 331 | 332 | public function renderPaymentMethods() { 333 | $out = ""; 334 | $paymentModules = $this->modules->find('className^=Payment'); 335 | if ($paymentModules->count() > 0) { 336 | $out .= ""; 337 | $out .= ""; 344 | $out .= "
"; 345 | $out .= ""; 346 | $out .= ""; 347 | $out .= "
"; 348 | 349 | } else { 350 | $out .= "No payment methods installed, you should have at least one
"; 351 | } 352 | 353 | return $out; 354 | } 355 | 356 | public function calculateNewStock(Page $order) { 357 | foreach($order->children as $orderitem) { 358 | $product = $orderitem->sc_product; 359 | if ($product->id) { 360 | $product->setOutputFormatting(false); 361 | $product->sc_qty = ($product->sc_qty - $orderitem->sc_qty); // TODO: We need to make sure that negative integers are allowed. Currently not.. 362 | $product->save(); 363 | } 364 | } 365 | } 366 | /* 367 | * Return markup for Information form items 368 | * 369 | * @param boolen $viewOnly false|true if you want to allow customer to edit 370 | * information, then use without viewOnly. 371 | * 372 | * @return string markup 373 | * 374 | */ 375 | public function renderInformationFormItems($viewOnly = false) { 376 | 377 | $out = ''; 378 | $errors = false; 379 | if (empty($this->session->orderArray['fields'])) { 380 | return; 381 | } 382 | foreach($this->session->orderArray['fields'] as $key => $field) { 383 | 384 | $reqKey = $key . 'Required'; 385 | 386 | if ($this->$key) { 387 | $class = ($this->$reqKey) ? 'required' : ''; 388 | $class .= (isset($field['error'])) ? ' error' : ''; 389 | 390 | 391 | if (strlen($field['value']) > 0 || !$viewOnly) { 392 | $out .= ""; 393 | $out .= ""; 397 | } 398 | if (isset($field['error'])) { 399 | $out .= "". $field['error'] .""; 400 | $errors = true; 401 | } 402 | 403 | if (!$viewOnly) { 404 | switch($field['type']) { 405 | case "textarea": 406 | $out .= ""; 407 | break; 408 | default: 409 | $out .= ""; 410 | } 411 | $out .= "
"; 412 | } 413 | else if (strlen($field['value']) > 0) { 414 | $out .= "". $field['value'] .""; 415 | $out .= ""; 416 | $out .= ""; 417 | } 418 | 419 | } 420 | } 421 | 422 | if($errors) $out = "" . $this->_("The formular contains errors, please check your informations.") . "
" . $out; 423 | return $out; 424 | } 425 | 426 | /* 427 | * Returns order data as an array 428 | * 429 | * @param boolean $validate If you validate, it will fetch data from post 430 | * and sanitize & validate that. If false, it will just give you orderArray 431 | * with current (should be default or once validated) 432 | * 433 | * @return array keeps information about order information fields (validation 434 | * errors, actual values etc) 435 | * 436 | */ 437 | public function validateInformation($validate = true) { 438 | 439 | 440 | $orderArray = Array(); 441 | $orderArray['valid'] = true; 442 | 443 | $orderArray['paymentmethod']['value'] = $this->session->orderArray['paymentmethod']['value']; 444 | $orderArray['paymentmethod']['label'] = $this->_("Payment method"); 445 | 446 | $fields = self::getDefaultFields(); 447 | 448 | foreach($fields as $key => $field) { 449 | 450 | // If not visible field, don't add to array 451 | if ($this->$key != 1) continue; 452 | 453 | // Set default labels 454 | $orderArray['fields'][$key]['label'] = $field['label']; 455 | 456 | // Set the form item type and default to input 457 | if (isset($field['type'])) $orderArray['fields'][$key]['type'] = $field['type']; 458 | else $orderArray['fields'][$key]['type'] = 'text'; 459 | 460 | // Set the custom values for custom field labels (can be set from settings) 461 | if ($key == 'custom1' || $key == 'custom2') { 462 | $customKey = $key . 'CustomLabel'; 463 | $orderArray['fields'][$key]['label'] = $this->$customKey; 464 | 465 | $customTextareaKey = $key . 'CustomTextarea'; 466 | if ($this->$customTextareaKey) $orderArray['fields'][$key]['type'] = 'textarea'; 467 | } 468 | 469 | // Set empty value for every field 470 | $orderArray['fields'][$key]['value'] = ''; 471 | 472 | // And if we have value saved on session, update it 473 | if(isset($this->session->orderArray['fields'][$key])) { 474 | $orderArray['fields'][$key]['value'] = $this->session->orderArray['fields'][$key]['value']; 475 | } 476 | } 477 | 478 | 479 | if ($validate) { 480 | foreach($orderArray['fields'] as $key => $field) { 481 | 482 | $reqKey = $key . 'Required'; 483 | 484 | $field['value'] = $this->input->post->$key; 485 | 486 | $orderArray['fields'][$key]['value'] = $this->sanitizer->text($field['value']); 487 | 488 | if($key == 'email' && !empty($this->input->post->email)) { 489 | if($this->sanitizer->email($field['value']) == '') { 490 | $orderArray['valid'] = false; 491 | $orderArray['fields'][$key]['error'] = $this->_("Email is not in right format."); 492 | } 493 | } 494 | 495 | if ($this->$reqKey && $orderArray['fields'][$key]['value'] == '') { 496 | $orderArray['valid'] = false; 497 | $orderArray['fields'][$key]['error'] = $this->_("This is a required field"); 498 | } 499 | } 500 | 501 | if ($this->input->post->shippingoption) { 502 | $orderArray['shippingoption']['value'] = $this->sanitizer->fieldName($this->input->post->shippingoption); 503 | } 504 | $orderArray['paymentmethod']['value'] = $this->sanitizer->fieldName($this->input->post->paymentmethod); 505 | $this->session->set('orderArray', $orderArray); 506 | } 507 | 508 | if (!isset($this->session->orderArray['valid'])) $this->session->set('orderArray', $orderArray); 509 | 510 | return $orderArray; 511 | 512 | } 513 | /* 514 | * return true if success 515 | * 516 | * Saves order page and it children products to database and destroys session 517 | * based shopping cart fron ShoppingCart table 518 | * 519 | */ 520 | public function createOrder() 521 | { 522 | if ($this->session->orderArray['valid'] == FALSE) throw new WireException("Non-valid order got through."); 523 | 524 | $fields = $this->session->orderArray['fields']; 525 | 526 | if(isset($this->session->orderArray['shippingoption']['value'])) { 527 | $fields['shippingoption']['value'] = $this->session->orderArray['shippingoption']['value']; 528 | $shippingOption = $this->modules->get($this->session->orderArray['shippingoption']['value']); 529 | } else { 530 | $fields['shippingoption']['value'] = ''; 531 | $shippingOption = NULL; 532 | } 533 | 534 | $fields['paymentmethod']['value'] = $this->session->orderArray['paymentmethod']['value']; 535 | 536 | $items = $this->modules->ShoppingCart->getCurrentCart(); 537 | 538 | $orderName = time(); 539 | foreach($fields as $field) { 540 | $orderName .= $field; 541 | } 542 | $orderName = md5($orderName); 543 | 544 | $order = new Page(); 545 | $order->template = $this->templates->get('sc-order'); 546 | $order->parent = $this->pages->get("template=admin,name=orders"); 547 | $order->title = $this->_("Order: ") . $fields['firstname']['value'] . ' ' . $fields['lastname']['value']; 548 | $order->name = $orderName; 549 | $order->sc_firstname = $fields['firstname']['value']; 550 | $order->sc_lastname = $fields['lastname']['value']; 551 | $order->email = $fields['email']['value']; 552 | $order->sc_streetaddress = $fields['streetaddress']['value']; 553 | $order->sc_city = $fields['city']['value']; 554 | $order->sc_zip = $fields['zip']['value']; 555 | $order->sc_country = $fields['country']['value']; 556 | $order->sc_phone = $fields['phone']['value']; 557 | $order->sc_greetings = $fields['greetings']['value']; 558 | $order->sc_custom1 = $fields['custom1']['value']; 559 | $order->sc_custom2 = $fields['custom2']['value']; 560 | $order->sc_price = $this->modules->ShoppingCart->getTotalSumFromItems($items, $shippingOption); 561 | $order->sc_customer = $this->user; 562 | $order->sc_paymentmethod = $fields['paymentmethod']['value']; 563 | $order->sc_shippingoption = $fields['shippingoption']['value']; 564 | $order->sc_session = $this->modules->ShoppingCart->getSession(); 565 | $order->addStatus(Page::statusUnpublished); 566 | 567 | // Default status will be first one there is on /shop/settings/statuses/ 568 | $admin = $this->pages->get($this->config->adminRootPageID); 569 | $order->sc_status = $this->pages->get("/{$admin->name}/shop/settings/statuses/")->children("check_access=0")->first(); 570 | 571 | $order->save(); 572 | 573 | $this->session->set('orderId', $order->id); 574 | 575 | foreach ($items as $item) { 576 | $product = $this->pages->get($item->product_id); 577 | 578 | // If the product is repeater, we assume it is a product variation. Se let's prepend the product title before variation 579 | if (strpos($product->template->name, "repeater_") === 0) { 580 | $parentProduct = $product->getForPage(); 581 | if ($parentProduct->id) $product->title = $parentProduct->title . ": " . $product->title; 582 | } 583 | 584 | $p = new Page(); 585 | $p->template = $this->templates->get('sc-order-item'); 586 | $p->parent = $order; 587 | $p->title = $product->title; 588 | // $p->sc_price = $product->sc_price; 589 | $p->sc_price = $this->modules->ShoppingCart->getProductPrice($item); 590 | $p->sc_qty = $item->qty; 591 | $p->sc_product = $product; 592 | $p->save(); 593 | } 594 | 595 | if($shippingOption) { 596 | $p = new Page(); 597 | $p->template = $this->templates->get('sc-order-item'); 598 | $p->parent = $order; 599 | $p->title = $this->_("Shipping costs"); 600 | $p->sc_price = $shippingOption->calculateShippingCost(); 601 | $p->sc_qty = 1; 602 | $p->save(); 603 | } 604 | 605 | $this->session->remove('orderArray'); 606 | $this->db->query("DELETE FROM ShoppingCart WHERE session_id = '" . $this->modules->ShoppingCart->getSession() . "'"); 607 | 608 | return ($order->id) ? true : false; 609 | } 610 | 611 | static public function getModuleConfigInputfields(Array $data) { 612 | // this is a container for fields, basically like a fieldset 613 | $fields = new InputfieldWrapper(); 614 | 615 | // since this is a static function, we can't use $this->modules, so get them from the global wire() function 616 | $modules = wire('modules'); 617 | 618 | $data = array_merge(self::getDefaultData(), $data); 619 | 620 | $field = $modules->get("InputfieldPageListSelect"); 621 | $field->attr('name', 'customThankyou'); 622 | $field->attr('value', $data['customThankyou']); 623 | $field->label = "Thankyou-page where user is redirected after succesful order"; 624 | $field->description = 'On that page, you can customize more personal thank you notice. You get order page id from $session->orderId and can display all the details you want from the order.'; 625 | $fields->add($field); 626 | 627 | $urls = $modules->get("InputfieldFieldset"); 628 | $urls->label = "Url segments"; 629 | $urls->set('collapsed', Inputfield::collapsedYes); 630 | 631 | $field = $modules->get("InputfieldText"); 632 | $field->attr('name', 'confirmationUrlSegment'); 633 | $field->attr('value', $data['confirmationUrlSegment']); 634 | $field->label = "UrlSegment for confirmation step"; 635 | $field->description = "This is only shown in the url. Default: confirmation"; 636 | $urls->add($field); 637 | //$fields->add($field); 638 | 639 | $field = $modules->get("InputfieldText"); 640 | $field->attr('name', 'paymentUrlSegment'); 641 | $field->attr('value', $data['paymentUrlSegment']); 642 | $field->label = "UrlSegment for payment step"; 643 | $field->description = "This is only shown in the url. Default: payment"; 644 | $urls->add($field); 645 | //$fields->add($field); 646 | 647 | $field = $modules->get("InputfieldText"); 648 | $field->attr('name', 'completedUrlSegment'); 649 | $field->attr('value', $data['completedUrlSegment']); 650 | $field->label = "UrlSegment for completed step"; 651 | $field->description = "This is only shown in the url. Default: completed"; 652 | $urls->add($field); 653 | //$fields->add($field); 654 | 655 | $fields->add($urls); 656 | 657 | $customer = $modules->get("InputfieldFieldset"); 658 | $customer->label = "Information asked from customers"; 659 | $customer->set('collapsed', Inputfield::collapsedYes); 660 | 661 | 662 | // This creates visible / required settings for each information field 663 | foreach(self::getDefaultFields() as $key => $field) { 664 | $fs = $modules->get("InputfieldFieldset"); 665 | $fs->label = $field['label']; 666 | //$fs->set('collapsed', Inputfield::collapsedBlank); 667 | // 668 | //if (isset($data[$key])) { 669 | // $checked = $data[$key]; 670 | //} else { 671 | // $checked = $informationFields[$key]['defaults']['visible']; 672 | //} 673 | // 674 | 675 | $f = $modules->get("InputfieldCheckbox"); 676 | $f->name = $key; 677 | $f->label = "Use ". $field['label'] ." field?"; 678 | $f->value = 1; 679 | $f->attr('checked', empty($data[$key]) ? '' : 'checked'); 680 | $f->set('collapsed', Inputfield::collapsedBlank); 681 | $fs->add($f); 682 | 683 | 684 | $f = $modules->get("InputfieldCheckbox"); 685 | $name = $key . "Required"; 686 | $f->name = $name; 687 | $f->label = "Is ". $field['label'] ." required?"; 688 | $f->value = 1; 689 | $f->attr('checked', empty($data[$name]) ? '' : 'checked'); 690 | $f->set('collapsed', Inputfield::collapsedBlank); 691 | $fs->add($f); 692 | 693 | if ($key == 'custom1' || $key == 'custom2') { 694 | $f = $modules->get("InputfieldText"); 695 | $name = $key . "CustomLabel"; 696 | if (empty($data[$name])) $data[$name] = "Custom label"; 697 | $f->name = $name; 698 | $f->label = "Label for this field"; 699 | $f->attr('value', $data[$name]); 700 | $f->set('collapsed', Inputfield::collapsedBlank); 701 | $fs->add($f); 702 | 703 | $f = $modules->get("InputfieldCheckbox"); 704 | $name = $key . "CustomTextarea"; 705 | $f->name = $name; 706 | $f->label = "Use textarea instead of regular input?"; 707 | $f->value = 1; 708 | $f->attr('checked', empty($data[$name]) ? '' : 'checked'); 709 | $f->set('collapsed', Inputfield::collapsedBlank); 710 | $fs->add($f); 711 | } 712 | 713 | 714 | $customer->add($fs); 715 | } 716 | 717 | $fields->add($customer); 718 | 719 | 720 | return $fields; 721 | } 722 | 723 | public function install() { 724 | $admin = $this->templates->get("admin"); 725 | 726 | $list = $this->modules->get('ProcessList'); 727 | 728 | $shop = $this->pages->get("template=admin, name=shop, parent=$admin->id"); 729 | 730 | if(!$shop->id) { 731 | $shop = new Page(); 732 | $shop->template = $admin; 733 | $shop->parent = $this->pages->get($this->config->adminRootPageID); 734 | $shop->title = 'Shop'; 735 | $shop->name = 'shop'; 736 | $shop->addStatus(Page::statusHidden); 737 | //$shop->process = $list; 738 | $shop->save(); 739 | } 740 | 741 | 742 | if ($shop->id) { 743 | $orders = new Page(); 744 | $orders->template = $this->templates->get("admin"); 745 | $orders->parent = $shop; 746 | $orders->title = 'Orders'; 747 | //$orders->name = 'orders'; 748 | $orders->process = $this; 749 | $orders->save(); 750 | } 751 | 752 | //shop/settings/ 753 | $settings = $this->pages->get("template=admin, name=settings, parent=$shop->id"); 754 | if (!$settings->id) { 755 | $settings = new Page(); 756 | $settings->template = $admin; 757 | $settings->parent = $shop; 758 | $settings->title = 'Shop settings'; 759 | $settings->name = 'settings'; 760 | $settings->save(); 761 | } 762 | 763 | 764 | //shop/settings/statuses/ 765 | $statuses = $this->pages->get("template=admin, name=statuses, parent=$settings->id"); 766 | if (!$statuses->id) { 767 | $statuses = new Page(); 768 | $statuses->template = $admin; 769 | $statuses->parent = $settings; 770 | $statuses->title = 'Order statuses'; 771 | $statuses->name = 'statuses'; 772 | $statuses->save(); 773 | } 774 | 775 | 776 | //shop/settings/statuses/completed/ 777 | $p = $this->pages->get("template=admin, name=completed, parent=$statuses->id"); 778 | if(!$p->id) { 779 | $p = new Page(); 780 | $p->template = $admin; 781 | $p->parent = $statuses; 782 | $p->title = 'Completed'; 783 | $p->name = 'completed'; 784 | $p->save(); 785 | } else { 786 | $p = ''; 787 | } 788 | 789 | //shop/settings/statuses/in-progress/ 790 | $p = $this->pages->get("template=admin, name=in-progress, parent=$statuses->id"); 791 | if(!$p->id) { 792 | $p = new Page(); 793 | $p->template = $admin; 794 | $p->parent = $statuses; 795 | $p->title = 'In Progress'; 796 | $p->name = 'in-progress'; 797 | $p->save(); 798 | } else { 799 | $p = ''; 800 | } 801 | 802 | //shop/settings/statuses/cancelled/ 803 | $p = $this->pages->get("template=admin, name=cancelled, parent=$statuses->id"); 804 | if(!$p->id) { 805 | $p = new Page(); 806 | $p->template = $admin; 807 | $p->parent = $statuses; 808 | $p->title = 'Cancelled'; 809 | $p->name = 'cancelled'; 810 | $p->save(); 811 | } else { 812 | $p = ''; 813 | } 814 | 815 | $oifg = $this->fieldgroups->get('sc-order-item'); 816 | if (!$oifg->id) { 817 | $oifg = new Fieldgroup(); 818 | $oifg->name = 'sc-order-item'; 819 | $oifg->add($this->fields->get('title')); 820 | $oifg->save(); 821 | } 822 | 823 | $oi = $this->templates->get('sc-order-item'); 824 | if(!$oi->id) { 825 | $oi = new Template(); 826 | $oi->name = 'sc-order-item'; 827 | $oi->fieldgroup = $oifg; 828 | $oi->pageLabelField = 'title'; 829 | $oi->noChildren = 1; 830 | $oi->flags = Template::flagSystem; 831 | $oi->save(); 832 | } 833 | 834 | $fg = $this->fieldgroups->get('sc-order-item'); 835 | if(!$fg->id) { 836 | $fg = new Fieldgroup(); 837 | $fg->name = 'sc-order'; 838 | $fg->add($this->fields->get('title')); 839 | $fg->save(); 840 | } 841 | 842 | $t = $this->templates->get('sc-order'); 843 | if(!$t->id) { 844 | $t = new Template(); 845 | $t->name = 'sc-order'; 846 | $t->fieldgroup = $fg; 847 | $t->pageLabelField = 'title'; 848 | $t->parentTemplates = array($admin->id); 849 | $t->childTemplates = array($oi->id); 850 | $t->flags = Template::flagSystem; 851 | $t->save(); 852 | } 853 | 854 | $f = $this->fields->get('sc_price'); 855 | if (!$f) { 856 | $f = new Field(); 857 | $f->type = $this->modules->get("FieldtypeFloat"); 858 | $f->name = 'sc_price'; 859 | $f->precision = 2; 860 | $f->label = 'Price of the product'; 861 | $f->save(); 862 | } 863 | 864 | $fg->add($f); 865 | $fg->save(); 866 | $oifg->add($f); 867 | $oifg->save(); 868 | 869 | $f = $this->fields->get('sc_qty'); 870 | if (!$f) { 871 | $f = new Field(); 872 | $f->type = $this->modules->get("FieldtypeInteger"); 873 | $f->name = 'sc_qty'; 874 | $f->label = 'Quantity'; 875 | $f->save(); 876 | } 877 | $oifg->add($f); 878 | $oifg->save(); 879 | 880 | if (!$this->fields->get('sc_product')) { 881 | $f = new Field(); 882 | $f->type = $this->modules->get("FieldtypePage"); 883 | $f->name = 'sc_product'; 884 | $f->label = 'Product'; 885 | $f->derefAsPage = FieldtypePage::derefAsPageOrNullPage; 886 | $f->inputfield = 'InputfieldPageListSelect'; 887 | $f->flags = Field::flagSystem | Field::flagPermanent; 888 | $f->save(); 889 | } 890 | $oifg->add($f); 891 | $oifg->save(); 892 | 893 | if (!$this->fields->get('sc_customer')) { 894 | $f = new Field(); 895 | $f->type = $this->modules->get("FieldtypePage"); 896 | $f->name = 'sc_customer'; 897 | $f->label = 'Customer'; 898 | $f->inputfield = 'InputfieldPageListSelect'; 899 | $f->derefAsPage = FieldtypePage::derefAsPageOrFalse; 900 | $f->parent_id = $this->config->usersPageID; 901 | $f->template_id = $this->config->userTemplateID; 902 | $f->flags = Field::flagSystem | Field::flagPermanent; 903 | $f->save(); 904 | } 905 | $fg->add($f); 906 | $fg->save(); 907 | 908 | if (!$this->fields->get('sc_status')) { 909 | $f = new Field(); 910 | $f->type = $this->modules->get("FieldtypePage"); 911 | $f->name = 'sc_status'; 912 | $f->label = 'Order Status'; 913 | $f->inputfield = 'InputfieldSelect'; 914 | $f->derefAsPage = FieldtypePage::derefAsPageOrFalse; 915 | $f->parent_id = $statuses->id; 916 | $f->flags = Field::flagSystem | Field::flagPermanent; 917 | $f->save(); 918 | } 919 | $fg->add($f); 920 | $fg->save(); 921 | 922 | if (!$this->fields->get('sc_firstname')) { 923 | $f = new Field(); 924 | $f->type = $this->modules->get("FieldtypeText"); 925 | $f->name = 'sc_firstname'; 926 | $f->label = 'First name'; 927 | $f->flags = Field::flagSystem | Field::flagPermanent; 928 | $f->save(); 929 | } 930 | $fg->add($f); 931 | $fg->save(); 932 | 933 | if (!$this->fields->get('sc_lastname')) { 934 | $f = new Field(); 935 | $f->type = $this->modules->get("FieldtypeText"); 936 | $f->name = 'sc_lastname'; 937 | $f->label = 'Last name'; 938 | $f->flags = Field::flagSystem | Field::flagPermanent; 939 | $f->save(); 940 | } 941 | $fg->add($f); 942 | $fg->save(); 943 | 944 | if (!$this->fields->get('sc_streetaddress')) { 945 | $f = new Field(); 946 | $f->type = $this->modules->get("FieldtypeText"); 947 | $f->name = 'sc_streetaddress'; 948 | $f->label = 'Street address'; 949 | $f->flags = Field::flagSystem | Field::flagPermanent; 950 | $f->save(); 951 | } 952 | $fg->add($f); 953 | $fg->save(); 954 | 955 | if (!$this->fields->get('sc_zip')) { 956 | $f = new Field(); 957 | $f->type = $this->modules->get("FieldtypeText"); 958 | $f->name = 'sc_zip'; 959 | $f->label = 'ZIP / Postal code'; 960 | $f->flags = Field::flagSystem | Field::flagPermanent; 961 | $f->save(); 962 | } 963 | $fg->add($f); 964 | $fg->save(); 965 | 966 | if (!$this->fields->get('sc_city')) { 967 | $f = new Field(); 968 | $f->type = $this->modules->get("FieldtypeText"); 969 | $f->name = 'sc_city'; 970 | $f->label = 'City'; 971 | $f->flags = Field::flagSystem | Field::flagPermanent; 972 | $f->save(); 973 | } 974 | $fg->add($f); 975 | $fg->save(); 976 | 977 | if (!$this->fields->get('sc_country')) { 978 | $f = new Field(); 979 | $f->type = $this->modules->get("FieldtypeText"); 980 | $f->name = 'sc_country'; 981 | $f->label = 'Country'; 982 | $f->flags = Field::flagSystem | Field::flagPermanent; 983 | $f->save(); 984 | } 985 | $fg->add($f); 986 | $fg->save(); 987 | 988 | if (!$this->fields->get('sc_phone')) { 989 | $f = new Field(); 990 | $f->type = $this->modules->get("FieldtypeText"); 991 | $f->name = 'sc_phone'; 992 | $f->label = 'Phone'; 993 | $f->flags = Field::flagSystem | Field::flagPermanent; 994 | $f->save(); 995 | } 996 | $fg->add($f); 997 | $fg->save(); 998 | 999 | if (!$this->fields->get('sc_greetings')) { 1000 | $f = new Field(); 1001 | $f->type = $this->modules->get("FieldtypeTextarea"); 1002 | $f->name = 'sc_greetings'; 1003 | $f->label = 'Greetings for shop'; 1004 | $f->flags = Field::flagSystem | Field::flagPermanent; 1005 | $f->save(); 1006 | } 1007 | $fg->add($f); 1008 | $fg->save(); 1009 | 1010 | if (!$this->fields->get('sc_custom1')) { 1011 | $f = new Field(); 1012 | $f->type = $this->modules->get("FieldtypeTextarea"); 1013 | $f->name = 'sc_custom1'; 1014 | $f->label = 'Custom field #1'; 1015 | $f->flags = Field::flagSystem | Field::flagPermanent; 1016 | $f->save(); 1017 | } 1018 | $fg->add($f); 1019 | $fg->save(); 1020 | 1021 | if (!$this->fields->get('sc_custom2')) { 1022 | $f = new Field(); 1023 | $f->type = $this->modules->get("FieldtypeTextarea"); 1024 | $f->name = 'sc_custom2'; 1025 | $f->label = 'Custom field #2'; 1026 | $f->flags = Field::flagSystem | Field::flagPermanent; 1027 | $f->save(); 1028 | } 1029 | $fg->add($f); 1030 | $fg->save(); 1031 | 1032 | if (!$this->fields->get('sc_paid')) { 1033 | $f = new Field(); 1034 | $f->type = $this->modules->get("FieldtypeDatetime"); 1035 | $f->name = 'sc_paid'; 1036 | $f->label = 'Paid'; 1037 | $f->dateOutputFormat = $this->config->dateFormat; 1038 | $f->dateInputFormat = $this->config->dateFormat; 1039 | $f->flags = Field::flagSystem | Field::flagPermanent; 1040 | $f->save(); 1041 | } 1042 | $fg->add($f); 1043 | $fg->save(); 1044 | 1045 | if (!$this->fields->get('sc_processed')) { 1046 | $f = new Field(); 1047 | $f->type = $this->modules->get("FieldtypeDatetime"); 1048 | $f->name = 'sc_processed'; 1049 | $f->label = 'Processed'; 1050 | $f->dateOutputFormat = $this->config->dateFormat; 1051 | $f->dateInputFormat = $this->config->dateFormat; 1052 | $f->flags = Field::flagSystem | Field::flagPermanent; 1053 | $f->save(); 1054 | } 1055 | $fg->add($f); 1056 | $fg->save(); 1057 | 1058 | if (!$this->fields->get('sc_paymentmethod')) { 1059 | $f = new Field(); 1060 | $f->type = $this->modules->get("FieldtypeText"); 1061 | $f->name = 'sc_paymentmethod'; 1062 | $f->label = 'Payment Method used'; 1063 | $f->flags = Field::flagSystem | Field::flagPermanent; 1064 | $f->save(); 1065 | } 1066 | $fg->add($f); 1067 | $fg->save(); 1068 | 1069 | if (!$this->fields->get('sc_shippingoption')) { 1070 | $f = new Field(); 1071 | $f->type = $this->modules->get("FieldtypeText"); 1072 | $f->name = 'sc_shippingoption'; 1073 | $f->label = 'Shipping Option used'; 1074 | $f->flags = Field::flagSystem | Field::flagPermanent; 1075 | $f->save(); 1076 | } 1077 | $fg->add($f); 1078 | $fg->save(); 1079 | 1080 | if (!$this->fields->get('sc_paymentid')) { 1081 | $f = new Field(); 1082 | $f->type = $this->modules->get("FieldtypeText"); 1083 | $f->name = 'sc_paymentid'; 1084 | $f->label = 'Payment ID returned from payment processor'; 1085 | $f->flags = Field::flagSystem | Field::flagPermanent; 1086 | $f->save(); 1087 | } 1088 | $fg->add($f); 1089 | $fg->save(); 1090 | 1091 | if (!$this->fields->get('sc_session')) { 1092 | $f = new Field(); 1093 | $f->type = $this->modules->get("FieldtypeText"); 1094 | $f->name = 'sc_session'; 1095 | $f->label = 'Session ID'; 1096 | $f->flags = Field::flagSystem | Field::flagPermanent; 1097 | $f->save(); 1098 | } 1099 | $fg->add($f); 1100 | $fg->save(); 1101 | 1102 | $f = $this->fields->get('email'); 1103 | $fg->add($f); 1104 | $fg->save(); 1105 | 1106 | // Only allowed parent template for sc-order-item is sc-order 1107 | $oi->parentTemplates = array($t->id); 1108 | $oi->save(); 1109 | 1110 | 1111 | $fg = $this->fieldgroups->get('sc-checkout'); 1112 | if(!$fg->id) { 1113 | $fg = new Fieldgroup(); 1114 | $fg->name = 'sc-checkout'; 1115 | $fg->add($this->fields->get('title')); 1116 | $fg->save(); 1117 | } 1118 | 1119 | $t = $this->templates->get('sc-checkout'); 1120 | if(!$t->id) { 1121 | $t = new Template(); 1122 | $t->name = 'sc-checkout'; 1123 | $t->fieldgroup = $fg; 1124 | $t->urlSegments = 1; 1125 | $t->pageLabelField = 'title'; 1126 | // $t->flags = Template::flagSystem; 1127 | $t = $t->save(); 1128 | } 1129 | 1130 | $p = $this->pages->get("template=$t"); 1131 | if(!$p->id) { 1132 | $p = new Page(); 1133 | $p->template = $t; 1134 | $p->parent = $this->pages->get(1); 1135 | $p->title = 'Checkout'; 1136 | $p->addStatus(Page::statusHidden); 1137 | $p->save(); 1138 | } 1139 | 1140 | 1141 | } 1142 | 1143 | 1144 | 1145 | public function uninstall() { 1146 | $p = $this->pages->get('template=sc-checkout, include=all'); 1147 | if ($p->id) $p->delete(); 1148 | 1149 | $p = $this->pages->get('template=admin, name=shop, include=all'); 1150 | if ($p->id) $this->pages->delete($p, true); // Recursive delete 1151 | 1152 | $t = $this->templates->get('sc-checkout'); 1153 | if ($t->id) { 1154 | $t->flags = Template::flagSystemOverride; 1155 | $t->flags = 0; 1156 | $this->templates->delete($t); 1157 | $fg = $this->fieldgroups->get('sc-checkout'); 1158 | if ($fg->id) $this->fieldgroups->delete($fg); 1159 | } 1160 | 1161 | $to = $this->templates->get('sc-order'); 1162 | if ($to->id) { 1163 | $to->flags = Template::flagSystemOverride; 1164 | $to->flags = 0; 1165 | $this->templates->delete($to); 1166 | $fg = $this->fieldgroups->get('sc-order'); 1167 | if ($fg->id) $this->fieldgroups->delete($fg); 1168 | } 1169 | 1170 | $toi = $this->templates->get('sc-order-item'); 1171 | if ($toi->id) { 1172 | $toi->flags = Template::flagSystemOverride; 1173 | $toi->flags = 0; 1174 | $this->templates->delete($toi); 1175 | $fg = $this->fieldgroups->get('sc-order-item'); 1176 | if ($fg->id) $this->fieldgroups->delete($fg); 1177 | } 1178 | 1179 | 1180 | // Remove all other sc_fields but sc_price 1181 | $scFields = $this->fields->find("name^=sc_,name!=sc_price,name!=sc_qty"); 1182 | foreach($scFields as $f) { 1183 | $f->flags = Field::flagSystemOverride; 1184 | $f->flags = 0; 1185 | $this->fields->delete($f); 1186 | } 1187 | } 1188 | 1189 | /** 1190 | * adds missing sc_phone field in v001 1191 | */ 1192 | protected function updateAddPhoneField() { 1193 | 1194 | $f = new Field(); 1195 | $f->type = $this->modules->get("FieldtypeText"); 1196 | $f->name = 'sc_phone'; 1197 | $f->label = 'Phone'; 1198 | $f->flags = Field::flagSystem | Field::flagPermanent; 1199 | $f->save(); 1200 | 1201 | $fg = $this->fieldgroups->get('sc-order-item'); 1202 | $fg->add($f); 1203 | $fg->save(); 1204 | $this->message("Created and added missing sc_phone field in ShoppinCheckout.module."); 1205 | } 1206 | } 1207 | --------------------------------------------------------------------------------