From 6dfad7e9cd1e84e06705d48af0346d9d371a5eaf Mon Sep 17 00:00:00 2001 From: Ben Smith Date: Mon, 30 Aug 2010 13:09:23 +1200 Subject: [PATCH] - Paypal IPN and order tracking. There are 3 statuses for an order 1. Awaiting Payment (red) - this will stay awaiting payment until either the admin confirms payment or paypal sends it IPN message to confirm that CC payment was successfull. 2. Payment Confirmed (green) - this means payment has been received but the item hasn't been posted. To mark as completed you will need to view the order and click "confirm order delivery" 3. completed. These aren't shown on the view orders but the order can still be viewed by typing in the order number. - Side bar integration, Basket contents appears on the sidebar. --- .../basket/controllers/admin_configure.php | 118 ++++++- .../controllers/admin_postage_bands.php | 35 +- .../controllers/admin_product_lines.php | 35 +- modules/basket/controllers/basket.php | 303 +++++++++++++--- modules/basket/css/basket.css | 16 +- modules/basket/helpers/basket.php | 311 ++++++++++++++--- modules/basket/helpers/basket_block.php | 21 ++ modules/basket/helpers/basket_event.php | 30 +- modules/basket/helpers/basket_installer.php | 125 ++++++- modules/basket/helpers/basket_theme.php | 22 +- modules/basket/helpers/postage_band.php | 6 +- modules/basket/helpers/product.php | 56 +-- modules/basket/libraries/Paypal.php | 330 ++++++++++++++++++ modules/basket/libraries/Session_Basket.php | 20 +- modules/basket/models/ipn_message.php | 16 + modules/basket/models/order.php | 56 +++ modules/basket/module.info | 2 +- modules/basket/views/add_to_basket.html.php | 7 +- .../basket/views/add_to_basket_ajax.html.php | 2 +- modules/basket/views/admin_configure.html.php | 11 +- .../basket/views/admin_postage_bands.html.php | 21 +- .../basket/views/admin_product_lines.html.php | 16 +- modules/basket/views/admin_templates.html.php | 8 + modules/basket/views/basket-side-bar.html.php | 42 +++ modules/basket/views/basket.html.php | 5 + modules/basket/views/checkout.html.php | 38 +- modules/basket/views/confirm_order.html.php | 17 +- modules/basket/views/order_complete.html.php | 10 +- modules/basket/views/paypal_redirect.html.php | 4 + modules/basket/views/pew1.html.php | 16 + modules/basket/views/pew2.html.php | 17 + modules/basket/views/print_order.html.php | 20 ++ modules/basket/views/view_basket.html.php | 100 +++--- modules/basket/views/view_ipn.html.php | 46 +++ modules/basket/views/view_order.html.php | 18 + modules/basket/views/view_orders.html.php | 67 ++++ 36 files changed, 1696 insertions(+), 271 deletions(-) create mode 100644 modules/basket/helpers/basket_block.php create mode 100644 modules/basket/libraries/Paypal.php create mode 100644 modules/basket/models/ipn_message.php create mode 100644 modules/basket/models/order.php create mode 100644 modules/basket/views/admin_templates.html.php create mode 100644 modules/basket/views/basket-side-bar.html.php create mode 100644 modules/basket/views/paypal_redirect.html.php create mode 100644 modules/basket/views/pew1.html.php create mode 100644 modules/basket/views/pew2.html.php create mode 100644 modules/basket/views/print_order.html.php create mode 100644 modules/basket/views/view_ipn.html.php create mode 100644 modules/basket/views/view_order.html.php create mode 100644 modules/basket/views/view_orders.html.php diff --git a/modules/basket/controllers/admin_configure.php b/modules/basket/controllers/admin_configure.php index 58a246ea..7831734d 100644 --- a/modules/basket/controllers/admin_configure.php +++ b/modules/basket/controllers/admin_configure.php @@ -27,14 +27,12 @@ class Admin_Configure_Controller extends Controller { $form = basket::get_configure_form(); if (request::method() == "post") { - // @todo move the "save" part of this into a separate controller function access::verify_csrf(); if ($form->validate()) { basket::extractForm($form); message::success(t("Basket Module Configured!")); - //url::redirect("admin/recaptcha"); } } else @@ -45,11 +43,121 @@ class Admin_Configure_Controller extends Controller $view = new Admin_View("admin.html"); $view->content = new View("admin_configure.html"); - - $view->content->form = $form; - //$view->content->products = ORM::factory("product")->orderby("name")->find_all(); print $view; } + + /** + * the index page of the user homes admin + */ + public function templates() + { + $form = basket::get_template_form(); + if (request::method() == "post") { + access::verify_csrf(); + + if ($form->validate()) { + + basket::extractTemplateForm($form); + message::success(t("Basket Module Configured!")); + } + } + else + { + basket::populateTemplateForm($form); + } + + $view = new Admin_View("admin.html"); + $view->content = new View("admin_templates.html"); + + $view->content->form = $form; + + print $view; + } + + public function paypal_encrypt_wizard_step1() + { + $view = new Admin_View("admin.html"); + $view->content = new View("pew1.html"); + + $view->content->form = self::keyGenerationForm(); + + print $view; + + } + + public function paypal_encrypt_wizard_step2() + { + access::verify_csrf(); + + $form = self::keyGenerationForm(); + + if (!$form->validate()) { + + self::paypal_encrypt_wizard_step1(); + return; + } + + $ssldir = str_replace('\\','/',VARPATH.'certificate'); + $ssldir= rtrim($ssldir, '/').'/'; + + if ( ! is_dir($ssldir)) + { + // Create the upload directory + mkdir($ssldir, 0777, TRUE); + } + + $prkeyfile = $ssldir . "myprvkey.pem"; + $pubcertfile = $ssldir . "mypubcert.pem"; + $certreqfile = $ssldir . "mycertreq.pem"; + + $dn = array("countryName" => $form->encrypt->countryName->value, + "stateOrProvinceName" => $form->encrypt->stateOrProvinceName->value, + "localityName" => $form->encrypt->localityName->value, + "organizationName" => $form->encrypt->organizationName->value, + "organizationalUnitName" => $form->encrypt->organizationalUnitName->value, + "commonName" => $form->encrypt->commonName->value, + "emailAddress" => $form->encrypt->emailAddress->value); + $privkeypass = $form->encrypt->privKeyPass->value; + $numberofdays = 365; + $config = array( + "private_key_bits" => 1024 + ); + + $privkey = openssl_pkey_new($config); + $csr = openssl_csr_new($dn, $privkey); + $sscert = openssl_csr_sign($csr, null, $privkey, $numberofdays); + openssl_x509_export($sscert, $publickey); + openssl_pkey_export($privkey, $privatekey, $privkeypass); + openssl_csr_export($csr, $csrStr); + + openssl_x509_export_to_file($sscert, $pubcertfile); + openssl_pkey_export_to_file ($privkey, $prkeyfile, $privkeypass); + openssl_csr_export_to_file($csr, $certreqfile); + + //echo "Your Public Certificate has been saved to " . $pubcertfile . "

"; + //echo "Your Private Key has been saved to " . $prkeyfile . "

"; + //echo "Your Certificate Request has been saved to " . $certreqfile . "

"; + + //echo $privatekey; // Will hold the exported PriKey + //echo $publickey; // Will hold the exported PubKey + //echo $csrStr; // Will hold the exported Certificate + } + + private function keyGenerationForm() + { + $form = new Forge("admin/configure/paypal_encrypt_wizard_step2", "", "post", array("id" => "generateKeys", "name" =>"generateKeys")); + $group = $form->group("encrypt")->label(t("Key Generation Details")); + $group->input("countryName")->label(t("Country Name"))->id("countryName"); + $group->input("stateOrProvinceName")->label(t("State or Province Name"))->id("stateOrProvinceName"); + $group->input("localityName")->label(t("Locality Name"))->id("localityName"); + $group->input("organizationName")->label(t("Organization Name"))->id("organizationName"); + $group->input("organizationalUnitName")->label(t("Organizational Unit Name"))->id("organizationalUnitName"); + $group->input("commonName")->label(t("Common Name"))->id("commonName"); + $group->input("emailAddress")->label(t("E-Mail Address"))->id("emailAddress"); + $group->input("privKeyPass")->label(t("Private Key Pass"))->id("privkeypass"); + return $form; + } + } diff --git a/modules/basket/controllers/admin_postage_bands.php b/modules/basket/controllers/admin_postage_bands.php index f843d0ca..535ad4ed 100644 --- a/modules/basket/controllers/admin_postage_bands.php +++ b/modules/basket/controllers/admin_postage_bands.php @@ -27,7 +27,7 @@ class Admin_Postage_Bands_Controller extends Controller { $view = new Admin_View("admin.html"); $view->content = new View("admin_postage_bands.html"); - $view->content->postage_bands = ORM::factory("postage_band")->orderby("name")->find_all(); + $view->content->postage_bands = ORM::factory("postage_band")->order_by("name")->find_all(); print $view; } @@ -43,8 +43,8 @@ class Admin_Postage_Bands_Controller extends Controller $form = postage_band::get_add_form_admin(); $valid = $form->validate(); $name = $form->add_postage->inputs["name"]->value; - $postage = ORM::factory("postage_band")->where("name", $name)->find(); - if ($postage->loaded) { + $postage = ORM::factory("postage_band")->where("name","=", $name)->find(); + if ($postage->loaded()) { $form->add_postage->inputs["name"]->add_error("in_use", 1); $valid = false; } @@ -59,16 +59,15 @@ class Admin_Postage_Bands_Controller extends Controller $postage->save(); message::success(t("Created postage band %postage_name", array( "postage_name" => html::clean($postage->name)))); - print json_encode(array("result" => "success")); + print json::reply(array("result" => "success")); } else { - print json_encode(array("result" => "error", - "form" => $form->__toString())); + print $form; } } public function delete_postage_band_form($id) { $postage = ORM::factory("postage_band", $id); - if (!$postage->loaded) { + if (!$postage->loaded()) { kohana::show_404(); } print postage_band::get_delete_form_admin($postage); @@ -82,7 +81,7 @@ class Admin_Postage_Bands_Controller extends Controller } $postage = ORM::factory("postage_band", $id); - if (!$postage->loaded) { + if (!$postage->loaded()) { kohana::show_404(); } @@ -91,21 +90,20 @@ class Admin_Postage_Bands_Controller extends Controller $name = $postage->name; $postage->delete(); } else { - print json_encode(array("result" => "error", - "form" => $form->__toString())); + print $form; } $message = t("Deleted user %postage_band", array("postage_band" => html::clean($name))); log::success("user", $message); message::success($message); - print json_encode(array("result" => "success")); + print json::reply(array("result" => "success")); } public function edit_postage_band($id) { access::verify_csrf(); $postage = ORM::factory("postage_band", $id); - if (!$postage->loaded) { + if (!$postage->loaded()) { kohana::show_404(); } @@ -115,10 +113,10 @@ class Admin_Postage_Bands_Controller extends Controller $new_name = $form->edit_postage->inputs["name"]->value; if ($new_name != $postage->name && ORM::factory("postage_band") - ->where("name", $new_name) - ->where("id !=", $postage->id) + ->where("name", "=", $new_name) + ->where("id","!=", $postage->id) ->find() - ->loaded) { + ->loaded()) { $form->edit_postage->inputs["name"]->add_error("in_use", 1); $valid = false; } else { @@ -133,16 +131,15 @@ class Admin_Postage_Bands_Controller extends Controller message::success(t("Changed postage band %postage_name", array("postage_name" => html::clean($postage->name)))); - print json_encode(array("result" => "success")); + print json::reply(array("result" => "success")); } else { - print json_encode(array("result" => "error", - "form" => $form->__toString())); + print $form; } } public function edit_postage_band_form($id) { $postage = ORM::factory("postage_band", $id); - if (!$postage->loaded) { + if (!$postage->loaded()) { kohana::show_404(); } diff --git a/modules/basket/controllers/admin_product_lines.php b/modules/basket/controllers/admin_product_lines.php index f063ad36..6fd7054a 100644 --- a/modules/basket/controllers/admin_product_lines.php +++ b/modules/basket/controllers/admin_product_lines.php @@ -27,7 +27,7 @@ class Admin_Product_Lines_Controller extends Controller { $view = new Admin_View("admin.html"); $view->content = new View("admin_product_lines.html"); - $view->content->products = ORM::factory("product")->orderby("name")->find_all(); + $view->content->products = ORM::factory("product")->order_by("name")->find_all(); print $view; } @@ -43,8 +43,8 @@ class Admin_Product_Lines_Controller extends Controller $form = product::get_add_form_admin(); $valid = $form->validate(); $name = $form->add_product->inputs["name"]->value; - $product = ORM::factory("product")->where("name", $name)->find(); - if ($product->loaded) { + $product = ORM::factory("product")->where("name", "=", $name)->find(); + if ($product->loaded()) { $form->add_product->inputs["name"]->add_error("in_use", 1); $valid = false; } @@ -60,16 +60,15 @@ class Admin_Product_Lines_Controller extends Controller $product->save(); message::success(t("Created product %product_name", array( "product_name" => html::clean($product->name)))); - print json_encode(array("result" => "success")); + print json::reply(array("result" => "success")); } else { - print json_encode(array("result" => "error", - "form" => $form->__toString())); + print $form; } } public function delete_product_form($id) { $product = ORM::factory("product", $id); - if (!$product->loaded) { + if (!$product->loaded()) { kohana::show_404(); } print product::get_delete_form_admin($product); @@ -83,7 +82,7 @@ class Admin_Product_Lines_Controller extends Controller } $product = ORM::factory("product", $id); - if (!$product->loaded) { + if (!$product->loaded()) { kohana::show_404(); } @@ -92,21 +91,20 @@ class Admin_Product_Lines_Controller extends Controller $name = $product->name; $product->delete(); } else { - print json_encode(array("result" => "error", - "form" => $form->__toString())); + print $form; } $message = t("Deleted user %product_name", array("product_name" => html::clean($name))); log::success("user", $message); message::success($message); - print json_encode(array("result" => "success")); + print json::reply(array("result" => "success")); } public function edit_product($id) { access::verify_csrf(); $product = ORM::factory("product", $id); - if (!$product->loaded) { + if (!$product->loaded()) { kohana::show_404(); } @@ -116,10 +114,10 @@ class Admin_Product_Lines_Controller extends Controller $new_name = $form->edit_product->inputs["name"]->value; if ($new_name != $product->name && ORM::factory("product") - ->where("name", $new_name) - ->where("id !=", $product->id) + ->where("name", "=", $new_name) + ->where("id","!=", $product->id) ->find() - ->loaded) { + ->loaded()) { $form->edit_product->inputs["name"]->add_error("in_use", 1); $valid = false; } else { @@ -135,16 +133,15 @@ class Admin_Product_Lines_Controller extends Controller message::success(t("Changed product %product_name", array("product_name" => html::clean($product->name)))); - print json_encode(array("result" => "success")); + print json::reply(array("result" => "success")); } else { - print json_encode(array("result" => "error", - "form" => $form->__toString())); + print $form; } } public function edit_product_form($id) { $product = ORM::factory("product", $id); - if (!$product->loaded) { + if (!$product->loaded()) { kohana::show_404(); } diff --git a/modules/basket/controllers/basket.php b/modules/basket/controllers/basket.php index 2e85f60c..a4ecbee3 100644 --- a/modules/basket/controllers/basket.php +++ b/modules/basket/controllers/basket.php @@ -19,19 +19,173 @@ */ class Basket_Controller extends Controller { - public function view_basket() { + public function temp(){ + $db = Database::instance(); + $db->query("ALTER TABLE {orders} ADD COLUMN `method` int(9) DEFAULT 0;"); + } + public function view_basket($pp="") { $template = new Theme_View("page.html", "basket"); + $basket = Session_Basket::get(); + if (isset($pp)){ + if ($pp=="nopp"){ + $basket->disablepp(); + } + elseif ($pp=="ppon"){ + $basket->enablepp(); + } + } + $view = new View("view_basket.html"); - $view->basket = Session_Basket::get(); + $view->basket = $basket; + $template->content = $view; print $template; } - private function getCheckoutForm(){ + public function preview($id) { + $item = ORM::factory("item", $id); + + print ""; + + } + + public function view_orders() { + self::check_view_orders(); + $template = new Theme_View("page.html", "basket"); + + $incomplete_orders = ORM::factory("order")->where('status',"<",20)->find_all(); + + $view = new View("view_orders.html"); + + $view->orders = $incomplete_orders; + + $template->content = $view; + + print $template; + } + + + public function view_ipn($orderid){ + self::check_view_orders(); + + $template = new Theme_View("page.html", "basket"); + + $order = ORM::factory("order")->where("id","=",$orderid)->find(); + $ipn_messages = ORM::factory("ipn_message")->where("key","=",$orderid)->find_all(); + //$ipn_messages = ORM::factory("ipn_message")->find_all(); + + $view = new View("view_ipn.html"); + + $view->order = $order; + $view->ipn_messages = $ipn_messages; + + $template->content = $view; + + print $template; + + } + + public function check_view_orders() { + if (!basket::can_view_orders()){ + die("Invalid access."); + } + } + + public function print_order($id){ + + access::verify_csrf(); + self::check_view_orders(); + + + $prefix = basket::getOrderPrefix(); + $length = strlen($prefix); + if (strlen($id)>$length ){ + if ($prefix === strtolower(substr($id,0,$length ))){ + $id = substr($id,$length); + } + } + $order = ORM::factory("order", $id); + $view = new View("print_order.html"); + + if ($order->loaded()){ + $view->order = str_replace(array("\r\n", "\n", "\r"),"
",$order->text); + }else{ + $view->order = "Order ".$id." not found."; + } + print $view; + } + + public function show_order($id){ + + access::verify_csrf(); + self::check_view_orders(); + $prefix = basket::getOrderPrefix(); + $length = strlen($prefix); + if (strlen($id)>$length ){ + if ($prefix === strtolower(substr($id,0,$length ))){ + $id = substr($id,$length); + } + } + + $order = ORM::factory("order", $id); + + if ($order->loaded()){ + $view = new View("view_order.html"); + $view->order = $order; + print $view; + }else{ + print "Order ".$id." not found."; + } + } + + public function show_ipn($id){ + access::verify_csrf(); + self::check_view_orders(); + $ipn_message = ORM::factory("ipn_message", $id); + + if ($ipn_message->loaded()){ + print $ipn_message->text; + }else{ + print "IPN Message ".$id." not found."; + } + + } + + public function confirm_order_delivery($id){ + access::verify_csrf(); + self::check_view_orders(); + $order = ORM::factory("order", $id); + + if ($order->loaded()){ + if ($order->status == 2) + { + $order->status = 20; + $order->save(); + } + } + url::redirect("basket/view_orders"); + } + + public function confirm_order_payment($id){ + access::verify_csrf(); + self::check_view_orders(); + $order = ORM::factory("order", $id); + + if ($order->loaded()){ + if ($order->status == 1) + { + $order->status = 2; + $order->save(); + } + } + url::redirect("basket/view_orders"); + } + + private function getCheckoutForm(){ $form = new Forge("basket/confirm", "", "post", array("id" => "checkout", "name" =>"checkout")); $group = $form->group("contact")->label(t("Contact Details")); $group->input("fullname")->label(t("Name"))->id("fullname"); @@ -42,6 +196,7 @@ class Basket_Controller extends Controller { $group->input("postcode")->label(t("Postcode"))->id("postcode"); $group->input("email")->label(t("E-Mail Address"))->id("email"); $group->input("phone")->label(t("Telephone Number"))->id("phone"); + $group->hidden("paypal")->id("paypal"); return $form; } @@ -80,6 +235,12 @@ class Basket_Controller extends Controller { if ($valid){ $basket = Session_Basket::get(); + + if (!isset($basket->contents ) || count($basket->contents) == 0) { + self::view_basket(); + return; + } + $basket->name = $form->contact->fullname->value; $basket->house = $form->contact->house->value; $basket->street = $form->contact->street->value; @@ -89,14 +250,33 @@ class Basket_Controller extends Controller { $basket->email = $form->contact->email->value; $basket->phone = $form->contact->phone->value; + $paypal=$form->contact->paypal->value=="true"; $template = new Theme_View("page.html", "basket"); - $form = new Forge("basket/complete", "", "post", array("id" => "confirm", "name" =>"confirm")); - $view = new View("confirm_order.html"); - $view->basket = $basket; - $template->content = $view; - $view->form = $form; - print $template; + if ($paypal){ + // create a prelimary order + $order = basket::createOrder($basket, Order_Model::PAYMENT_PAYPAL); + $paypal = new Paypal(); + + // create the order first + $view = new View("paypal_redirect.html"); + $view ->form = $paypal->process($basket, + url::site("basket/paypal_complete/$order->id", "http"), + url::site("basket/paypal_cancel/$order->id", "http"), + url::site("basket/paypal_ipn/$order->id", "http")); + $template->content = $view; + print $template; + + // redirect to paypal + }else + { + $form = new Forge("basket/complete", "", "post", array("id" => "confirm", "name" =>"confirm")); + $view = new View("confirm_order.html"); + $view->basket = $basket; + $template->content = $view; + $view->form = $form; + print $template; + } } else { @@ -105,53 +285,80 @@ class Basket_Controller extends Controller { } } - public function complete () { - access::verify_csrf(); + function paypal_ipn($id){ + $order = ORM::factory("order")->where("id","=",$id)->find(); + if ($order->loaded()){ + + $paypal = new Paypal(); + + if ($paypal->validate_ipn($id)){ + if ($paypal->ipn_data['payment_status'] == "Completed"){ + + $order->status = Order_Model::PAYMENT_CONFIRMED; + + // send e-mails + basket::send_order($order); + basket::send_invoice($order); + + $order->save(); + } + return; + } + print "invalid access. tut tut!"; + } + return; + + } + + public function paypal_complete($id) { + $order = ORM::factory("order")->where("id","=",$id)->find(); $basket = Session_Basket::get(); + $basket->clear(); + $this->_complete($order); + } - //$admin_address = basket::getEmailAddress(); - $postage = $basket->postage_cost(); - $product_cost = $basket->cost(); + public function paypal_cancel($id){ + $order = ORM::factory("order")->where("id","=",$id)->find(); - $admin_email = "Order for : -".$basket->name." -".$basket->house." -".$basket->street." -".$basket->suburb." -".$basket->town." -".$basket->postcode." -".$basket->email." -".$basket->phone." -Placed at ".date("d F Y - H:i" ,time())." -Cost of Ordered Products = ".$product_cost." -Postage and Packaging Costs + ".$postage." -Total Owed ".($product_cost+$postage)." Total in ".basket::getCurrency()." - -Items Ordered: - -"; - - // create the order items - foreach ($basket->contents as $basket_item){ - $item = $basket_item->getItem(); - $prod = ORM::factory("product", $basket_item->product); - $admin_email = $admin_email." -".$item->title." - ".$item->url()." -".$prod->name." - ".$prod->description." -".$basket_item->quantity." @ ".$prod->cost." - -"; + if ($order->loaded()){ + $order->delete(); } + $this->checkout(); + } - $from = "From: ".basket::getEmailAddress(); - mail(basket::getEmailAddress(), "Order from ".$basket->name, $admin_email, $from); + public function complete () { + access::verify_csrf(); + $basket = Session_Basket::get(); + + if (!isset($basket->contents ) || count($basket->contents) == 0) { + self::view_basket(); + return; + } + + // create order + $order = basket::createOrder($basket, Order_Model::PAYMENT_OFFLINE); $basket->clear(); + // send e-mails + basket::send_order($order); + basket::send_invoice($order); + + + $this->_complete($order); + } + + private function _complete($order){ $template = new Theme_View("page.html", "basket"); $view = new View("order_complete.html"); + $ordernumber = basket::getOrderPrefix().$order->id; + $view->ordernumber = $ordernumber; + $view->order = $order; + $view->total_cost = $order->cost; + $template->content = $view; + print $template; } @@ -189,7 +396,11 @@ Items Ordered: $form->add_to_basket->product->value, $form->add_to_basket->quantity->value); - print json_encode(array("result" => "success")); + $item = ORM::factory("item", $form->add_to_basket->id->value); + + Session::instance()->set("redirect_home", $item->parent_id); + + print json::reply(array("result" => "success")); } else { @@ -205,7 +416,7 @@ Items Ordered: // get the item to add $item = ORM::factory("item", $id); - if (!$item->loaded) + if (!$item->loaded()) { //TODO die("Not loaded id"); diff --git a/modules/basket/css/basket.css b/modules/basket/css/basket.css index f660685d..107b8229 100644 --- a/modules/basket/css/basket.css +++ b/modules/basket/css/basket.css @@ -1,5 +1,17 @@ #basket {float:right;} #add_to_basket {float:right} -#basketForm {max-width:200px} -#basketThumb {float:left; padding:10px 10px 0 0;} +#basketForm {max-width:200px;float:left;} +#basketThumb {float:left; padding:10px;} #basketThumb img{max-width:100px;} +#payment {float:right; width:50%} +#checkout input, +#checkout select, +#checkout textarea { + display: block; + clear: both; + padding: .2em; + width: 100%; +} +#sidebar-basket {max-height:300px; overflow-y:auto; overflow-x:hidden;} +.order-status-1 a{color:#AA0000 !important} +.order-status-2 a{color:#00AA00 !important} diff --git a/modules/basket/helpers/basket.php b/modules/basket/helpers/basket.php index 45ab988f..b9be6726 100644 --- a/modules/basket/helpers/basket.php +++ b/modules/basket/helpers/basket.php @@ -40,6 +40,26 @@ class basket_Core { "MXN" => "Mexican Peso"); static $format= array( + "AUD" => "$", + "CAD" => "$", + "EUR" => "€", + "GBP" => "£", + "JPY" => "¥", + "USD" => "$", + "NZD" => "$", + "CHF" => "", + "HKD" => "$", + "SGD" => "$", + "SEK" => "", + "DKK" => "", + "PLN" => "", + "NOK" => "", + "HUF" => "", + "CZK" => "", + "ILS" => "", + "MXN" => ""); + + static $formatweb= array( "AUD" => "$", "CAD" => "$", "EUR" => "€", @@ -60,37 +80,108 @@ class basket_Core { "MXN" => ""); - static function get_configure_form() { - $form = new Forge("admin/configure", "", "post", array("id" => "gConfigureForm")); - $group = $form->group("configure")->label(t("Configure Basket")); - $group->input("email")->label(t("Offline Paying Email Address"))->id("gOrderEmailAddress"); - $group->dropdown("currency") - ->label(t("Currency")) - ->options(self::$currencies); + static public function can_view_orders() + { + if (identity::active_user()->admin){ + return true; + } - $group->checkbox("paypal")->label(t("Use Paypal"))->id("gPaypal"); - $group->input("paypal_account")->label(t("Paypal E-Mail Address"))->id("gPaypalAddress"); + print identity::active_user(); + foreach (identity::active_user()->groups() as $group){ + if ($group->name == 'shop'){ + return true; + } + } + + return false; + } + + + static function get_configure_form() { + $form = new Forge("admin/configure", "", "post", array("id" => "g-configure-form")); + $group = $form->group("configure")->label(t("Configure Basket")); + $group->input("email")->label(t("Offline Paying Email Address"))->id("g-order-email-address"); + $group->dropdown("currency") + ->label(t("Currency")) + ->options(self::$currencies); + + $group->checkbox("side_bar")->label(t("Use only side bar"))->id("g-side-bar-only"); + + $group->checkbox("paypal")->label(t("Use Paypal"))->id("g-paypal"); + $group->input("paypal_account")->label(t("Paypal E-Mail Address"))->id("g-paypal-address"); + $group->checkbox("allow_pickup")->label(t("Allow Product Pickup"))->id("g-allow-pickup"); + $group->input("order_prefix")->label(t("Order Number Prefix"))->id("g-order-prefix"); + $group->submit("")->value(t("Save")); + return $form; + } + + static function get_template_form() { + $form = new Forge("admin/configure/templates", "", "post", array("id" => "g-configure-form")); + $group = $form->group("configure")->label(t("Configure Basket")); + $group->textarea("payment_details")->label(t("Payment Details Description"))->id("g-payment-details"); + $group->textarea("order_complete_page")->label(t("Order Complete Page"))->id("g-order-complete_page"); + $group->input("order_complete_email_subject")->label(t("Order Complete Email Subject"))->id("g-order-complete_email_subject"); + $group->textarea("order_complete_email")->label(t("Order Complete Email"))->id("g-order-complete_email"); $group->submit("")->value(t("Save")); return $form; } static function populateForm($form){ - $form->configure->email->value(basket::getEmailAddress()); - $form->configure->paypal->checked(basket::isPaypal()); - $form->configure->paypal_account->value(basket::getPaypalAccount()); - $form->configure->currency->selected(basket::getCurrency()); + $form->configure->email->value(basket::getEmailAddress()); + $form->configure->side_bar->checked(basket::is_side_bar_only()); + $form->configure->paypal->checked(basket::isPaypal()); + $form->configure->paypal_account->value(basket::getPaypalAccount()); + $form->configure->currency->selected(basket::getCurrency()); + $form->configure->allow_pickup->checked(basket::isAllowPickup()); + $form->configure->order_prefix->value(basket::getOrderPrefix()); + } + + static function populateTemplateForm($form){ + $form->configure->payment_details->value(basket::getPaymentDetails()); + $form->configure->order_complete_page->value(basket::getOrderCompletePage()); + $form->configure->order_complete_email_subject->value(basket::getOrderCompleteEmailSubject()); + $form->configure->order_complete_email->value(basket::getOrderCompleteEmail()); } static function extractForm($form){ - $email = $form->configure->email->value; - $isPaypal = $form->configure->paypal->value; - $paypal_account = $form->configure->paypal_account->value; - $currency = $form->configure->currency->selected; - basket::setEmailAddress($email); - basket::setPaypal($isPaypal); - basket::setPaypalAccount($paypal_account); - basket::setCurrency($currency); + $email = $form->configure->email->value; + $is_side_bar = $form->configure->side_bar->value; + $isPaypal = $form->configure->paypal->value; + $paypal_account = $form->configure->paypal_account->value; + $currency = $form->configure->currency->selected; + $allow_pickup = $form->configure->allow_pickup->value; + $order_prefix = $form->configure->order_prefix->value; + basket::setEmailAddress($email); + basket::set_side_bar_only($is_side_bar); + basket::setPaypal($isPaypal); + basket::setPaypalAccount($paypal_account); + basket::setCurrency($currency); + basket::setAllowPickup($allow_pickup); + basket::setOrderPrefix($order_prefix); } + static function extractTemplateForm($form){ + $payment_details = $form->configure->payment_details->value; + $order_complete_page = $form->configure->order_complete_page->value; + $order_complete_email_subject = $form->configure->order_complete_email_subject->value; + $order_complete_email = $form->configure->order_complete_email->value; + basket::setPaymentDetails($payment_details); + basket::setOrderCompletePage($order_complete_page); + basket::setOrderCompleteEmailSubject($order_complete_email_subject); + basket::setOrderCompleteEmail($order_complete_email); + } + + static public function is_side_bar_only() + { + return module::get_var("basket","is_side_bar_only"); + + } + + static public function set_side_bar_only($value) + { + module::set_var("basket","is_side_bar_only",$value); + + } + static function getEmailAddress(){ return module::get_var("basket","email"); @@ -113,10 +204,51 @@ class basket_Core { return $cur; } + static function getPaymentDetails(){ + return module::get_var("basket","payment_details"); + } + + static function getOrderPrefix(){ + return module::get_var("basket","order_prefix"); + } + + static function isAllowPickup(){ + return module::get_var("basket","allow_pickup"); + } + + static function getOrderCompletePage(){ + return module::get_var("basket","order_complete_page"); + } + + static function getOrderCompleteEmail(){ + return module::get_var("basket","order_complete_email"); + } + + static function getOrderCompleteEmailSubject(){ + return module::get_var("basket","order_complete_email_subject"); + } + static function formatMoney($money){ return self::$format[self::getCurrency()].number_format($money,2); } + static function formatMoneyForWeb($money){ + return self::$formatweb[self::getCurrency()].number_format($money,2); + } + + static function replaceStrings($string, $key_values) { + // Replace x_y before replacing x. + krsort($key_values, SORT_STRING); + + $keys = array(); + $values = array(); + foreach ($key_values as $key => $value) { + $keys[] = "%$key"; + $values[] = $value; + } + return str_replace($keys, $values, $string); + } + static function setEmailAddress($email){ module::set_var("basket","email",$email); } @@ -133,32 +265,129 @@ class basket_Core { module::set_var("basket","currency",$currency); } - static function generatePaypalForm($session_basket){ - $form = " -
- - - -"; + static function setPaymentDetails($details){ + module::set_var("basket","payment_details",$details); + } - $postage = $session_basket->postage_cost(); - if ($postage > 0) { - $form = $form." -"; + static function setAllowPickup($allow_pickup){ + module::set_var("basket","allow_pickup",$allow_pickup); + } + + static function setOrderPrefix($order_prefix){ + module::set_var("basket","order_prefix",strtolower($order_prefix)); + } + + static function setOrderCompletePage($details){ + module::set_var("basket","order_complete_page",$details); + } + + static function setOrderCompleteEmail($details){ + module::set_var("basket","order_complete_email",$details); + } + + static function setOrderCompleteEmailSubject($details){ + module::set_var("basket","order_complete_email_subject",$details); + } + + static function createOrder($basket, $method){ + + $order = ORM::factory("order"); + $order->text = "processing"; + $order->save(); + + $ordernumber = basket::getOrderPrefix().$order->id; + + //$admin_address = basket::getEmailAddress(); + $postage = $basket->postage_cost(); + $product_cost = $basket->cost(); + $ppon = $basket->ispp(); + + $text = " + Order Number : ".$ordernumber." + + for : +".$basket->name." +".$basket->house." +".$basket->street." +".$basket->suburb." +".$basket->town." +".$basket->postcode." +".$basket->email." +".$basket->phone." +Placed at ".date("d F Y - H:i" ,time())." +Cost of Ordered Products = ".$product_cost; + if ($ppon){ + $text = $text." +Postage and Packaging Costs + ".$postage." +Total Owed ".($product_cost+$postage)." Total in ".basket::getCurrency(); + } + else{ + $text = $text." +Person has chosen to pick up product. +Total Owed ".($product_cost)." Total in ".basket::getCurrency(); + } + $text = $text." + +Items Ordered: + +"; + + // create the order items + foreach ($basket->contents as $basket_item){ + $item = $basket_item->getItem(); + $prod = ORM::factory("product", $basket_item->product); + $text = $text." +".$item->title." - ".$item->url()." +".$prod->name." - ".$prod->description." +".$basket_item->quantity." @ ".$prod->cost." + +"; } - $id = 1; - foreach ($session_basket->contents as $key => $basket_item){ - $form = $form." -getCode()."\"/> -cost_per\"/> -quantity\"/>"; - $id++; + if ($ppon){ + $total_cost = ($product_cost+$postage); + } + else{ + $total_cost = $product_cost; } - $form = $form."
"; + $order->name = $basket->name; + $order->email = $basket->email; + $order->cost = $total_cost; + $order->text = $text; + $order->status = Order_Model::WAITING_PAYMENT; + $order->method = $method; + $order->save(); + + //$basket->clear(); + + return $order; + } + + public function send_order($order){ + + $from = "From: ".basket::getEmailAddress(); + $ordernumber = basket::getOrderPrefix().$order->id; + + mail(basket::getEmailAddress(), "Order ".$ordernumber." from ".$order->name, $order->text, $from); + + } + + public function send_invoice($order) + { + + $from = "From: ".basket::getEmailAddress(); + $ordernumber = basket::getOrderPrefix().$order->id; + $invoice_email = basket::replaceStrings(basket::getOrderCompleteEmail(),Array( + "name"=>$order->name, + "order_number"=> $ordernumber, + "total_cost" =>basket::formatMoney($order->cost), + "order_details"=>$order->text)); + + mail($order->email, + basket::replaceStrings(basket::getOrderCompleteEmailSubject(),Array("order_number"=>$ordernumber)), + $invoice_email, $from); - return $form; } } \ No newline at end of file diff --git a/modules/basket/helpers/basket_block.php b/modules/basket/helpers/basket_block.php new file mode 100644 index 00000000..9263a68e --- /dev/null +++ b/modules/basket/helpers/basket_block.php @@ -0,0 +1,21 @@ + t("Basket")); + } + + static function get($block_id, $theme) { + $block = ""; + switch ($block_id) { + case "shopping": + $block = new Block(); + $block->css_id = "g-view-basket"; + $block->title = t("Basket"); + $block->content = new View("basket-side-bar.html"); + $block->content->basket = Session_Basket::get(); + break; + } + return $block; + } +} \ No newline at end of file diff --git a/modules/basket/helpers/basket_event.php b/modules/basket/helpers/basket_event.php index ade43e96..c22a48c6 100644 --- a/modules/basket/helpers/basket_event.php +++ b/modules/basket/helpers/basket_event.php @@ -34,6 +34,11 @@ class basket_event_Core{ ->label(t("Configure")) ->url(url::site("admin/configure"))); $basket_menu->append( + Menu::factory("link") + ->id("templates") + ->label(t("Templates")) + ->url(url::site("admin/configure/templates"))); + $basket_menu->append( Menu::factory("link") ->id("product_line") ->label(t("Product Lines")) @@ -43,15 +48,20 @@ class basket_event_Core{ ->id("postage_bands") ->label(t("Postage Bands")) ->url(url::site("admin/postage_bands"))); + $basket_menu->append( + Menu::factory("link") + ->id("view_orders") + ->label(t("View Orders")) + ->url(url::site("basket/view_orders"))); } static function item_edit_form($item, $form){ $group = $form->group("products")->label(t("Available Products")); - $product_override = ORM::factory("product_override")->where('item_id', $item->id)->find(); + $product_override = ORM::factory("product_override")->where('item_id', "=", $item->id)->find(); $group->checkbox("all")->label("No products except.."); - if ($product_override->loaded){ + if ($product_override->loaded()){ $group->all->checked($product_override->none); } @@ -63,11 +73,11 @@ class basket_event_Core{ $cost = $product->cost; $checked = false; - if ($product_override->loaded){ + if ($product_override->loaded()){ $item_product = ORM::factory("item_product") - ->where('product_override_id', $product_override->id) - ->where('product_id', $product->id)->find(); - if ($item_product->loaded){ + ->where('product_override_id', "=", $product_override->id) + ->where('product_id', "=", $product->id)->find(); + if ($item_product->loaded()){ $checked = $item_product->include; if ($item_product->cost != -1){ $cost = $item_product->cost; @@ -82,7 +92,7 @@ class basket_event_Core{ } static function item_edit_form_completed($item, $form){ - $product_override = ORM::factory("product_override")->where('item_id', $item->id)->find(); + $product_override = ORM::factory("product_override")->where('item_id', "=", $item->id)->find(); if ($form->products->all->checked) { @@ -93,8 +103,8 @@ class basket_event_Core{ foreach ($products as $product){ $p_group = $form->products->__get("product_$product->id"); $item_product = ORM::factory("item_product") - ->where('product_override_id', $product_override->id) - ->where('product_id', $product->id)->find(); + ->where('product_override_id', "=", $product_override->id) + ->where('product_id', "=", $product->id)->find(); $item_product->include = $p_group->__get("exclude_$product->id")->checked; $item_product->cost = $p_group->__get("cost_$product->id")->value; @@ -105,7 +115,7 @@ class basket_event_Core{ } else { - if ($product_override->loaded){ + if ($product_override->loaded()){ $product_override->delete(); } } diff --git a/modules/basket/helpers/basket_installer.php b/modules/basket/helpers/basket_installer.php index c514f4ca..b92cd694 100644 --- a/modules/basket/helpers/basket_installer.php +++ b/modules/basket/helpers/basket_installer.php @@ -57,14 +57,69 @@ class basket_installer PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + $db->query("CREATE TABLE IF NOT EXISTS {orders} ( + `id` int(9) NOT NULL auto_increment, + `status` int(9) DEFAULT 0, + `name` varchar(1024), + `email` varchar(1024), + `cost` DECIMAL(10,2) default 0, + `method` int(9) DEFAULT 0, + `text` TEXT NOT NULL, + PRIMARY KEY (`id`)) + ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + + $db->query("CREATE TABLE IF NOT EXISTS `ipn_messages` ( + `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, + `date` int(11) NOT NULL, + `key` varchar(20) NOT NULL, + `txn_id` varchar(20) NOT NULL, + `status` varchar(20) NOT NULL, + `success` bool default false, + `text` text, + PRIMARY KEY (`id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + + postage_band::create("No Postage",0,0); product::create("4x6",5,"4\"x6\" print",1); product::create("8x10",25,"8\"x10\" print",1); product::create("8x12",30,"8\"x12\" print",1); + basket::setPaymentDetails( +"

Use the following options to pay for this order.

+

Send a chequre to..

+

Visit the shop..

+

By using internet banking..

" + ); + basket::setOrderPrefix("ORDER"); + basket::setOrderCompletePage( +"

Your order number is %order_number. To pay for this order please either:

+

- Send a cheque for %total_cost to with reference %order_number..

+

- Visit the shop and quote the order %order_number..

+

- Transfer %total_cost using internet banking with reference %order_number..

+

Order will be processed as soon as payment is received. You should receive an e-mail with your order details shortly.

" + ); + basket::setOrderCompleteEmail( +"Hi %name, - module::set_version("basket", 2); +Thank you for your order the order details are below. To pay for this order please either: + +- Send a cheque for %total_cost to with reference %order_number.. +- Visit the shop and quote the order %order_number.. +- Transfer %total_cost using internet banking with reference %order_number.. + +Order will be processed as soon as payment is received. For order pick-ups please visit.. + +Order Details +------------- +%order_details + +Thanks"); + basket::setOrderCompleteEmailSubject( +"Photography Order %order_number"); + + module::set_version("basket", 4); } @@ -89,6 +144,73 @@ class basket_installer module::set_version("basket", $version = 2); } + + if ($version == 2) { + $db->query("CREATE TABLE IF NOT EXISTS {orders} ( + `id` int(9) NOT NULL auto_increment, + `text` TEXT NOT NULL, + PRIMARY KEY (`id`)) + ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + basket::setPaymentDetails( +"

Use the following options to pay for this order.

+

Send a chequre to..

+

Visit the shop..

+

By using internet banking..

" + ); + basket::setOrderPrefix("ORDER"); + basket::setOrderCompletePage( +"

Your order number is %order_number. To pay for this order please either:

+

- Send a cheque for %total_cost to with reference %order_number..

+

- Visit the shop and quote the order %order_number..

+

- Transfer %total_cost using internet banking with reference %order_number..

+

Order will be processed as soon as payment is received. You should receive an e-mail with your order details shortly.

" + ); + basket::setOrderCompleteEmail( +"Hi %name, + +Thank you for your order the order details are below. To pay for this order please either: + +- Send a cheque for %total_cost to with reference %order_number.. +- Visit the shop and quote the order %order_number.. +- Transfer %total_cost using internet banking with reference %order_number.. + +Order will be processed as soon as payment is received. For order pick-ups please visit.. + +Order Details +------------- +%order_details + +Thanks"); + basket::setOrderCompleteEmailSubject( +"Photography Order %order_number"); + + module::set_version("basket", $version = 3); + } + + if ($version ==3 ){ + $db->query("ALTER TABLE {orders} ADD COLUMN `status` int(9) DEFAULT 0;"); + + $db->query("CREATE TABLE IF NOT EXISTS {ipn_messages} ( + `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, + `date` int(11) NOT NULL, + `key` varchar(20) NOT NULL, + `txn_id` varchar(20) NOT NULL, + `status` varchar(20) NOT NULL, + `success` bool default false, + `text` text, + PRIMARY KEY (`id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + module::set_version("basket", $version = 4); + + } + + if ($version==4){ + $db->query("ALTER TABLE {orders} ADD COLUMN `name` varchar(1024);"); + $db->query("ALTER TABLE {orders} ADD COLUMN `email` varchar(1024);"); + $db->query("ALTER TABLE {orders} ADD COLUMN `method` int(9) DEFAULT 0;"); + $db->query("ALTER TABLE {orders} ADD COLUMN `cost` DECIMAL(10,2) default 0"); + module::set_version("basket", $version = 5); + } } static function uninstall(){ @@ -97,5 +219,6 @@ class basket_installer $db->query("DROP TABLE IF EXISTS {product_overrides}"); $db->query("DROP TABLE IF EXISTS {item_products}"); $db->query("DROP TABLE IF EXISTS {postage_bands}"); + $db->query("DROP TABLE IF EXISTS {orders}"); } } diff --git a/modules/basket/helpers/basket_theme.php b/modules/basket/helpers/basket_theme.php index b8d513e9..1ad535e5 100644 --- a/modules/basket/helpers/basket_theme.php +++ b/modules/basket/helpers/basket_theme.php @@ -24,10 +24,15 @@ class basket_theme_Core { } static function header_top($theme) { - $view = new View("basket.html"); - $view->basket = Session_Basket::get(); - return $view->render(); + if (!basket::is_side_bar_only()) + { + $view = new View("basket.html"); + + $view->basket = Session_Basket::get(); + return $view->render(); + } + return ""; } static function admin_head($theme) { @@ -36,12 +41,15 @@ class basket_theme_Core { } } static function photo_top($theme){ - if ( product::isForSale($theme->item()->id)){ - $view = new View("add_to_basket.html"); + if (!basket::is_side_bar_only()) + { + if ( product::isForSale($theme->item()->id)){ + $view = new View("add_to_basket.html"); - $view->item = $theme->item(); + $view->item = $theme->item(); - return $view->render(); + return $view->render(); + } } return ""; } diff --git a/modules/basket/helpers/postage_band.php b/modules/basket/helpers/postage_band.php index b69f84c4..ca679d53 100644 --- a/modules/basket/helpers/postage_band.php +++ b/modules/basket/helpers/postage_band.php @@ -28,7 +28,6 @@ class postage_band_Core { $group->input("per_item")->label(t("Per Item"))->id("gPetItem"); $group->submit("")->value(t("Add Postage Band")); $postage = ORM::factory("postage_band"); - $form->add_rules_from($postage); return $form; } @@ -44,7 +43,6 @@ class postage_band_Core { value($postage->per_item); $group->submit("")->value(t("Modify Postage Band")); - $form->add_rules_from($postage); return $form; } @@ -67,8 +65,8 @@ class postage_band_Core { * @return User_Model */ static function create($name, $flatrate, $peritemcost) { - $postage = ORM::factory("postage_band")->where("name", $name)->find(); - if ($postage->loaded) { + $postage = ORM::factory("postage_band")->where("name", "=", $name)->find(); + if ($postage->loaded()) { throw new Exception("@todo postage already EXISTS $name"); } diff --git a/modules/basket/helpers/product.php b/modules/basket/helpers/product.php index cb4261a0..a4a13c8c 100644 --- a/modules/basket/helpers/product.php +++ b/modules/basket/helpers/product.php @@ -31,11 +31,11 @@ class product_Core { ->options(postage_band::getPostageArray()); $group->submit("")->value(t("Add Product")); $product = ORM::factory("product"); - $form->add_rules_from($product); return $form; } static function get_edit_form_admin($product) { + $form = new Forge("admin/product_lines/edit_product/$product->id", "", "post", array("id" => "gEditProductForm")); $group = $form->group("edit_product")->label(t("Edit Product")); @@ -51,7 +51,6 @@ class product_Core { ->selected($product->postage_band_id); $group->submit("")->value(t("Modify Product")); - $form->add_rules_from($product); return $form; } @@ -74,8 +73,8 @@ class product_Core { * @return User_Model */ static function create($name, $cost, $description, $postage_band) { - $product = ORM::factory("product")->where("name", $name)->find(); - if ($product->loaded) { + $product = ORM::factory("product")->where("name", "=", $name)->find(); + if ($product->loaded()) { throw new Exception("@todo USER_ALREADY_EXISTS $name"); } @@ -90,9 +89,9 @@ class product_Core { static function getProductArray($id){ $producta = array(); // check for product override - $product_override = ORM::factory("product_override")->where('item_id', $id)->find(); + $product_override = ORM::factory("product_override")->where('item_id', "=", $id)->find(); - if (!$product_override->loaded){ + if (!$product_override->loaded()){ // no override found so check parents // check parents for product override $item = ORM::factory("item",$id); @@ -100,24 +99,25 @@ class product_Core { $parents = $item->parents(); foreach ($parents as $parent){ // check for product override - $product_override = ORM::factory("product_override")->where('item_id', $parent->id)->find(); - if ($product_override->loaded){ - break; + $temp_override = ORM::factory("product_override")->where('item_id', "=", $parent->id)->find(); + if ($temp_override ->loaded()){ + $product_override = $temp_override; + //break; } - } + } } $products = ORM::factory("product")->find_all(); foreach ($products as $product){ $show = true; $cost = $product->cost; - if ($product_override->loaded){ + if ($product_override->loaded()){ $show = !$product_override->none; $item_product = ORM::factory("item_product") - ->where('product_override_id', $product_override->id) - ->where('product_id', $product->id)->find(); + ->where('product_override_id', "=", $product_override->id) + ->where('product_id', "=", $product->id)->find(); - if ($item_product->loaded){ + if ($item_product->loaded()){ $cost = $item_product->cost; if (!$show){ $show = $item_product->include; @@ -127,7 +127,7 @@ class product_Core { if ($show) { - $producta[$product->id] = $product->description." (".basket::formatMoney($cost).")"; + $producta[$product->id] = html::clean($product->description)." (".basket::formatMoneyForWeb($cost).")"; } } @@ -136,10 +136,12 @@ class product_Core { static function isForSale($id){ + try + { // check for product override - $product_override = ORM::factory("product_override")->where('item_id', $id)->find(); + $product_override = ORM::factory("product_override")->where('item_id', "=", $id)->find(); - if (!$product_override->loaded){ + if (!$product_override->loaded()){ // no override found so check parents // check parents for product override $item = ORM::factory("item",$id); @@ -147,24 +149,25 @@ class product_Core { $parents = $item->parents(); foreach ($parents as $parent){ // check for product override - $product_override = ORM::factory("product_override")->where('item_id', $parent->id)->find(); - if ($product_override->loaded){ - break; + $temp_override = ORM::factory("product_override")->where('item_id', "=", $parent->id)->find(); + if ($temp_override ->loaded()){ + $product_override = $temp_override; + //break; } } } $products = ORM::factory("product")->find_all(); - if ($product_override->loaded && $product_override->none){ + if ($product_override->loaded() && $product_override->none){ foreach ($products as $product){ $item_product = ORM::factory("item_product") - ->where('product_override_id', $product_override->id) - ->where('product_id', $product->id)->find(); + ->where('product_override_id', "=", $product_override->id) + ->where('product_id', "=", $product->id)->find(); - if ($item_product->loaded){ + if ($item_product->loaded()){ if ($item_product->include){ return true; @@ -177,5 +180,10 @@ class product_Core { } else { return count($products) > 0; } + } + catch (Exception $e) + { + echo $e; + } } } \ No newline at end of file diff --git a/modules/basket/libraries/Paypal.php b/modules/basket/libraries/Paypal.php new file mode 100644 index 00000000..c18ee058 --- /dev/null +++ b/modules/basket/libraries/Paypal.php @@ -0,0 +1,330 @@ +add_field('business', 'somebody@domain.com'); + * $p->add_field('first_name', $_POST['first_name']); + * ... (add all your fields in the same manor) + * $p->submit_paypal_post(); + * + * To process an IPN, have your IPN processing file contain: + * + * $p = new paypal_class; + * if ($p->validate_ipn()) { + * ... (IPN is verified. Details are in the ipn_data() array) + * } + * + * + * In case you are new to paypal, here is some information to help you: + * + * 1. Download and read the Merchant User Manual and Integration Guide from + * http://www.paypal.com/en_US/pdf/integration_guide.pdf. This gives + * you all the information you need including the fields you can pass to + * paypal (using add_field() with this class) aswell as all the fields + * that are returned in an IPN post (stored in the ipn_data() array in + * this class). It also diagrams the entire transaction process. + * + * 2. Create a "sandbox" account for a buyer and a seller. This is just + * a test account(s) that allow you to test your site from both the + * seller and buyer perspective. The instructions for this is available + * at https://developer.paypal.com/ as well as a great forum where you + * can ask all your paypal integration questions. Make sure you follow + * all the directions in setting up a sandbox test environment, including + * the addition of fake bank accounts and credit cards. + * + ******************************************************************************* + */ + +class Paypal_Core { + + var $last_error; // holds the last error encountered + + var $ipn_response; // holds the IPN response from paypal + public $ipn_data = array(); // array contains the POST values for IPN + + var $fields = array(); // array holds the fields to submit to paypal + + + public function __construct() + { + // initialization constructor. Called when class is created. + + // sandbox paypal + + //$this->paypal_url = "https://www.sandbox.paypal.com/cgi-bin/webscr"; + //$this->secure_url = "ssl://www.sandbox.paypal.com"; + + // normal paypal + $this->paypal_url = "https://www.paypal.com/cgi-bin/webscr"; + $this->secure_url = "ssl://www.paypal.com"; + + $this->last_error = ''; + + //$this->ipn_log_file = Kohana::log_directory().Kohana::config('paypal.ipn_logfile'); + //$this->ipn_log = true; + $this->ipn_response = ''; + + // populate $fields array with a few default values. See the paypal + // documentation for a list of fields and their data types. These defaul + // values can be overwritten by the calling script. + + + } + + function add_field($field, $value) { + + // adds a key=>value pair to the fields array, which is what will be + // sent to paypal as POST variables. If the value is already in the + // array, it will be overwritten. + + $this->fields["$field"] = $value; + } + + public function process($session_basket, $return_url, $cancel_url, $notify_url){ + + $this->add_field('rm','2'); + $this->add_field('cmd','_cart'); + $this->add_field('upload','1'); + + $this->add_field('currency_code', basket::getCurrency()); + $this->add_field('business', basket::getPaypalAccount()); + + // IPN stuff + $this->add_field('return', $return_url); + $this->add_field('cancel_return', $cancel_url); + $this->add_field('notify_url', $notify_url); + + // postage + if ($session_basket->ispp()){ + $postage = $session_basket->postage_cost(); + if ($postage > 0) { + $this->add_field('shipping_1',$postage); + } + } + + // basket contents + $id = 1; + foreach ($session_basket->contents as $key => $basket_item){ + $this->add_field("item_name_$id", $basket_item->getCode()); + $this->add_field("amount_$id", $basket_item->cost_per); + $this->add_field("quantity_$id",$basket_item->quantity); + $id++; + } + + // shipping address + $this->add_field("payer_email", $session_basket->email); + $this->add_field("address_name", $session_basket->name); + $this->add_field("address_street", $session_basket->house." ".$session_basket->street); + $this->add_field("address_city", $session_basket->town); + $this->add_field("address_zip", $session_basket->postcode); + $this->add_field("contact_phone", $session_basket->phone); + + $string = "
paypal_url."\">\n"; + + foreach ($this->fields as $name => $value) { + $string = $string."\n"; + } + + $string = $string."
"; + return $string; + } + + function validate_ipn($key) { + + // parse the paypal URL + $url_parsed=parse_url($this->paypal_url); + + // generate the post string from the _POST vars aswell as load the + // _POST vars into an arry so we can play with them from the calling + // script. + $post_string = 'cmd=_notify-validate'; + foreach ($_POST as $field=>$value) { + $this->ipn_data["$field"] = $value; + $value = urlencode(stripslashes($value)); + $value = preg_replace('/(.*[^%^0^D])(%0A)(.*)/i','${1}%0D%0A${3}',$value); + $post_string .= '&'.$field.'='.$value; + } + + // open the connection to paypal + + $fp = fsockopen($this->secure_url,443,$err_num,$err_str,30); + if(!$fp) { + + // could not open the connection. If loggin is on, the error message + // will be in the log. + $this->last_error = "fsockopen error no. $errnum: $errstr"; + $this->log_ipn_results($key,false); + return false; + + } else { + + // Post the data back to paypal + fputs($fp, "POST ".$url_parsed['path']." HTTP/1.1\r\n"); + fputs($fp, "Host: ".$url_parsed['host']."\r\n"); + fputs($fp, "Content-type: application/x-www-form-urlencoded\r\n"); + + fputs($fp, "Content-length: ".strlen($post_string)."\r\n\r\n"); + //fputs($fp, "Connection: close\r\n\r\n"); + fputs($fp, $post_string . "\r\n\r\n"); + + // loop through the response from the server and append to variable + while(!feof($fp)) { + $this->ipn_response .= fgets($fp, 1024); + } + + fclose($fp); // close connection + + } + + if (stristr($this->ipn_response,"VERIFIED")===false) + { + // Invalid IPN transaction. Check the log for details. + $this->last_error = 'IPN Validation Failed. '.$url_parsed['host'].'\\'.$url_parsed['path']; + $this->log_ipn_results($key,false); + return false; + } + else{ + + // Valid IPN transaction. + + // check recievers e-mail + $business = basket::getPaypalAccount(); + + if ($this->ipn_data['receiver_email']!=$business) + { + $this->last_error = 'receivers e-mail did not match '.$business; + $this->log_ipn_results($key,false); + return false; + } + + // if confirmed check message has not been received already + if ($this->ipn_data['payment_status'] == "Completed"){ + + $message = ORM::factory("ipn_message") + ->where('key',"=",$key) + ->where('status',"=",'completed') + ->where('txn_id',"=",$this->ipn_data['txn_id'])->find(); + + if ($message->loaded()){ + $this->last_error = 'Message alread received.'; + $this->log_ipn_results($key,false); + return false; + } + } + + $this->log_ipn_results($key,true); + return true; + + } + + } + + function log_ipn_results($key, $success) { + + // Timestamp + $text = '['.date('m/d/Y g:i A').'] - '; + + $message = ORM::factory("ipn_message"); + $message->date = time(); + $message->key = $key; + $message->txn_id = $this->ipn_data['txn_id']; + $message->status = $this->ipn_data['payment_status']; + $message->success = $success; + + // Success or failure being logged? + if ($success) $text .= "SUCCESS!\n"; + else $text .= 'FAIL: '.$this->last_error."\n"; + + // Log the POST variables + $text .= "IPN POST Vars from Paypal:\n"; + foreach ($this->ipn_data as $key=>$value) { + $text .= "$key=$value \n"; + } + + // Log the response from the paypal server + $text .= "\nIPN Response from Paypal Server:\n ".$this->ipn_response; + + $message->text = $text; + $message->save(); + } + + function dump_fields() { + + // Used for debugging, this function will output all the field/value pairs + // that are currently defined in the instance of the class using the + // add_field() function. + + echo "

paypal_class->dump_fields() Output:

"; + echo " + + + + "; + + ksort($this->fields); + foreach ($this->fields as $key => $value) { + echo ""; + } + + echo "
Field NameValue
$key".urldecode($value)." 

"; + } +} + + + diff --git a/modules/basket/libraries/Session_Basket.php b/modules/basket/libraries/Session_Basket.php index 11de4db0..c73cfe36 100644 --- a/modules/basket/libraries/Session_Basket.php +++ b/modules/basket/libraries/Session_Basket.php @@ -90,14 +90,32 @@ class Session_Basket_Core { public $email = ""; public $phone = ""; + public $ppenabled = true; + public function clear(){ if (isset($this->contents)){ foreach ($this->contents as $key => $item){ unset($this->contents[$key]); } } + $this->ppenabled = true; } + public function enablepp() + { + $this->ppenabled = true; + } + + public function disablepp() + { + $this->ppenabled = false; + } + + public function ispp(){ + return $this->ppenabled; + } + + private function create_key($product, $id){ return "$product _ $id"; } @@ -116,7 +134,7 @@ class Session_Basket_Core { $key = $this->create_key($product, $id); if (isset($this->contents[$key])){ - $this->contents[$key]->add($id, $quantity); + $this->contents[$key]->add($quantity); } else { $this->contents[$key] = new basket_item($product, $id, $quantity); diff --git a/modules/basket/models/ipn_message.php b/modules/basket/models/ipn_message.php new file mode 100644 index 00000000..c6f75ffa --- /dev/null +++ b/modules/basket/models/ipn_message.php @@ -0,0 +1,16 @@ +date); + } + + public function json_encode(){ + $toReturn = array( + 'id' => $this->id, + 'date' => $this->formatedTime(), + 'text' => text::convertText($this->text)); + return $toReturn; + } +} \ No newline at end of file diff --git a/modules/basket/models/order.php b/modules/basket/models/order.php new file mode 100644 index 00000000..1dc1a627 --- /dev/null +++ b/modules/basket/models/order.php @@ -0,0 +1,56 @@ +id." ".$this->name." ".$this->status(); + } + + public function status(){ + switch ($this->status){ + case 1: + return "Waiting Payment"; + case 2: + return "Payment Confirmed"; + case 20: + return "Complete"; + + default: + return "Unknown"; + } + } + + public function payment_method(){ + switch ($this->method){ + case 1: + return "through Paypal"; + case 2: + return "offline"; + + default: + return "Unknown"; + } + } +} diff --git a/modules/basket/module.info b/modules/basket/module.info index 5bf1f888..559c59aa 100644 --- a/modules/basket/module.info +++ b/modules/basket/module.info @@ -1,3 +1,3 @@ name = "Shopping Basket" description = "Provides a simple shopping basket and checkout with paypal integration" -version = 2 +version = 5 diff --git a/modules/basket/views/add_to_basket.html.php b/modules/basket/views/add_to_basket.html.php index aad7f892..a21a74d8 100644 --- a/modules/basket/views/add_to_basket.html.php +++ b/modules/basket/views/add_to_basket.html.php @@ -1,8 +1,5 @@ -
- id") ?>" - title="" - class="gDialogLink"> - Add To Basket +id") ?>" title="" class="g-dialog-link"> +
\ No newline at end of file diff --git a/modules/basket/views/add_to_basket_ajax.html.php b/modules/basket/views/add_to_basket_ajax.html.php index f4e38d83..137ecef2 100644 --- a/modules/basket/views/add_to_basket_ajax.html.php +++ b/modules/basket/views/add_to_basket_ajax.html.php @@ -1,5 +1,5 @@ -
+
<?= $item->title?>
diff --git a/modules/basket/views/admin_configure.html.php b/modules/basket/views/admin_configure.html.php index 5f3ae012..9960fe01 100644 --- a/modules/basket/views/admin_configure.html.php +++ b/modules/basket/views/admin_configure.html.php @@ -1,8 +1,7 @@ -
-

-

- -

- +
+

+

+

+
\ No newline at end of file diff --git a/modules/basket/views/admin_postage_bands.html.php b/modules/basket/views/admin_postage_bands.html.php index 436c2a83..d79fe380 100644 --- a/modules/basket/views/admin_postage_bands.html.php +++ b/modules/basket/views/admin_postage_bands.html.php @@ -21,7 +21,7 @@
" - class="gDialogLink gButtonLink right ui-icon-left ui-state-default ui-corner-all" + class="g-dialog-link g-button right ui-icon-left ui-state-default ui-corner-all" title=""> @@ -31,8 +31,8 @@ -
- +
+
@@ -41,25 +41,26 @@ $postage_band): ?> - "> + "> - diff --git a/modules/basket/views/admin_product_lines.html.php b/modules/basket/views/admin_product_lines.html.php index 29cca094..f20f2a37 100644 --- a/modules/basket/views/admin_product_lines.html.php +++ b/modules/basket/views/admin_product_lines.html.php @@ -18,10 +18,10 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ ?> -
name) ?> - flat_rate) ?> + flat_rate) ?> - per_item) ?> + per_item) ?> + id") ?>" open_text="" - class="gPanelLink gButtonLink ui-state-default ui-corner-all ui-icon-left"> - + class="g-panel-link g-button ui-state-default ui-corner-all ui-icon-left"> + id") ?>" - class="gDialogLink gButtonLink ui-state-default ui-corner-all ui-icon-left"> + class="g-dialog-link g-button ui-state-default ui-corner-all ui-icon-left"> +
+
+
@@ -47,7 +47,7 @@ name) ?> - diff --git a/modules/basket/views/admin_templates.html.php b/modules/basket/views/admin_templates.html.php new file mode 100644 index 00000000..263293c4 --- /dev/null +++ b/modules/basket/views/admin_templates.html.php @@ -0,0 +1,8 @@ + +
+

+

+ +

+ +
\ No newline at end of file diff --git a/modules/basket/views/basket-side-bar.html.php b/modules/basket/views/basket-side-bar.html.php new file mode 100644 index 00000000..d6ac00d7 --- /dev/null +++ b/modules/basket/views/basket-side-bar.html.php @@ -0,0 +1,42 @@ +page_type != 'basket'){ + if (basket::can_view_orders()){ + ?>
" title="">item(); + if ($item->is_photo() && product::isForSale($theme->item()->id)){ + ?>

+id") ?>" +title="">

+contents) && ($basket->size() > 0)) { + ?>
- cost) ?> + cost) ?> description) ?> @@ -57,14 +57,14 @@ + id") ?>" open_text="" - class="gPanelLink gButtonLink ui-state-default ui-corner-all ui-icon-left"> + class="g-panel-link g-button ui-state-default ui-corner-all ui-icon-left"> id") ?>" - class="gDialogLink gButtonLink ui-state-default ui-corner-all ui-icon-left"> + class="g-dialog-link g-button ui-state-default ui-corner-all ui-icon-left">
contents as $key => $prod_details){ + + ?>"> + + + +"> + +
getItem(); + + ?><?= $item->title?>
+quantity) ?> x product_description()) ?>
cost?>cost); ?>" class="g-button ui-state-default ui-corner-all ui-icon-left">
Totalispp()?basket::formatMoneyForWeb($total + $postage):basket::formatMoneyForWeb($total)?>

+

" title="">

+ page_type != 'basket'): ?> + + " + title="">View Orders + contents) && ($basket->size() > 0)): ?>
" diff --git a/modules/basket/views/checkout.html.php b/modules/basket/views/checkout.html.php index f651d633..b9ceaa2d 100644 --- a/modules/basket/views/checkout.html.php +++ b/modules/basket/views/checkout.html.php @@ -41,7 +41,7 @@ function ci(v) return true; } -function so(){ +function so(g){ var p=true; var d=document.checkout; if(!ci(d.fullname)){p=false;} @@ -49,16 +49,38 @@ function so(){ if(!ci(d.phone)){p=false;} if (p) { + d.paypal.value=g; d.submit(); } } -
- + diff --git a/modules/basket/views/confirm_order.html.php b/modules/basket/views/confirm_order.html.php index a14305d8..60ff14ac 100644 --- a/modules/basket/views/confirm_order.html.php +++ b/modules/basket/views/confirm_order.html.php @@ -24,8 +24,8 @@ function so(){document.confirm.submit();}

Basket Summary

-
- +
+
@@ -48,18 +48,18 @@ function so(){document.confirm.submit();} quantity) ?> postage_cost();?> 0):?> "> - + "> - +
- cost)) ?> + cost) ?>
Postage and Packagingispp()?"":"style=\"text-decoration:line-through\""; ?>>Postage and Packagingispp()?"":"style=\"text-decoration:line-through\""; ?>>
Total Costcost() + $postage))?>Total Costispp()?basket::formatMoneyForWeb($basket->cost() + $postage):basket::formatMoneyForWeb($basket->cost()); ?>
@@ -80,9 +80,10 @@ E-mail : email ?>
Telephone : phone ?> -" class="left gButtonLink ui-state-default ui-corner-all ui-icon-left"> +
diff --git a/modules/basket/views/order_complete.html.php b/modules/basket/views/order_complete.html.php index 1287f058..5f384732 100644 --- a/modules/basket/views/order_complete.html.php +++ b/modules/basket/views/order_complete.html.php @@ -18,7 +18,13 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ ?> -
+
+

Thankyou for your order

-You will be contacted soon to arrange payment and delivery. +method == Order_Model::PAYMENT_PAYPAL){ +?>Your order will be confirmed when Paypal has finished processing your order.$ordernumber, "total_cost"=>basket::formatMoneyForWeb($total_cost)));?>
\ No newline at end of file diff --git a/modules/basket/views/paypal_redirect.html.php b/modules/basket/views/paypal_redirect.html.php new file mode 100644 index 00000000..26a2438b --- /dev/null +++ b/modules/basket/views/paypal_redirect.html.php @@ -0,0 +1,4 @@ + +

Processing

If you are not automatically redirected to + paypal within 5 seconds Click Here.

+ diff --git a/modules/basket/views/pew1.html.php b/modules/basket/views/pew1.html.php new file mode 100644 index 00000000..c751f7cb --- /dev/null +++ b/modules/basket/views/pew1.html.php @@ -0,0 +1,16 @@ + + \ No newline at end of file diff --git a/modules/basket/views/pew2.html.php b/modules/basket/views/pew2.html.php new file mode 100644 index 00000000..41324d2d --- /dev/null +++ b/modules/basket/views/pew2.html.php @@ -0,0 +1,17 @@ + + \ No newline at end of file diff --git a/modules/basket/views/print_order.html.php b/modules/basket/views/print_order.html.php new file mode 100644 index 00000000..63598131 --- /dev/null +++ b/modules/basket/views/print_order.html.php @@ -0,0 +1,20 @@ + + + +Print Order + + + + + + + + + + + \ No newline at end of file diff --git a/modules/basket/views/view_basket.html.php b/modules/basket/views/view_basket.html.php index 94d9bf4a..891d2647 100644 --- a/modules/basket/views/view_basket.html.php +++ b/modules/basket/views/view_basket.html.php @@ -18,32 +18,50 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ ?> -
+ +
+
contents ) && count($basket->contents) > 0): ?> - - - - - " - class="right gButtonLink ui-state-default ui-corner-all ui-icon-right"> - - - " - class="right gButtonLink ui-state-default ui-corner-all ui-icon-right"> - - - -

- -

-
+ $(document).ready(function(){ + $("#pickup").click(function(){ + if (this.checked) + { + window.location = ""; + } + else + { + window.location = ""; + } + }); + }) + + + " + class="right g-button ui-state-default ui-corner-all ui-icon-right"> + + +
+
contents ) && count($basket->contents) > 0): ?> @@ -63,7 +81,9 @@ - postage_cost();?> 0):?> - "> - + "> + + + "> - "> - + + "> +
getItem(); ?>
+ <?= $item->title?> +
@@ -74,50 +94,44 @@ cost?> - cost)) ?> + cost); ?> - + " - class="gButtonLink ui-state-default ui-corner-all ui-icon-left"> + class="g-button ui-state-default ui-corner-all ui-icon-left">
Postage and Packaging
ispp()?"":"style=\"text-decoration:line-through\""; ?>>Postage and Packagingispp()?"":"style=\"text-decoration:line-through\""; ?>> +
ispp()?"":"checked"; ?>/> Select if you wish to pick up the photos.
Total Cost
Total Costispp()?basket::formatMoneyForWeb($total + $postage):basket::formatMoneyForWeb($total)?>
+ Shopping Basket is Empty
+
\ No newline at end of file diff --git a/modules/basket/views/view_ipn.html.php b/modules/basket/views/view_ipn.html.php new file mode 100644 index 00000000..6c19e27f --- /dev/null +++ b/modules/basket/views/view_ipn.html.php @@ -0,0 +1,46 @@ + +

IPN Messages for title()?>

+">Back to orders +
+ + +
+

+
+ diff --git a/modules/basket/views/view_order.html.php b/modules/basket/views/view_order.html.php new file mode 100644 index 00000000..d735e26f --- /dev/null +++ b/modules/basket/views/view_order.html.php @@ -0,0 +1,18 @@ + +

title()?>

+Payment is payment_method()?>status==Order_Model::WAITING_PAYMENT){ + ?>
id)."?csrf=$csrf";?>">Confirm Order Payment status==Order_Model::PAYMENT_CONFIRMED){ + ?>
id)."?csrf=$csrf";?>">Confirm Order Delivery method==Order_Model::PAYMENT_PAYPAL){ + ?>
id);?>">View Paypal IPN Messages
+",$order->text);?> \ No newline at end of file diff --git a/modules/basket/views/view_orders.html.php b/modules/basket/views/view_orders.html.php new file mode 100644 index 00000000..a5c90e1c --- /dev/null +++ b/modules/basket/views/view_orders.html.php @@ -0,0 +1,67 @@ + +
+
+ + + + + + +
+
+
+ +
+

+
+