FINAL suepr merge step : added all modules to this super repos

This commit is contained in:
Bachir Soussi Chiadmi
2015-04-19 16:46:59 +02:00
7585 changed files with 1723356 additions and 18 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

View File

@@ -0,0 +1,858 @@
<?php
/**
* @file
* Shopping cart and checkout tests.
*/
/**
* Tests the cart and checkout functionality.
*/
class UbercartCartCheckoutTestCase extends UbercartTestHelper {
public static function getInfo() {
return array(
'name' => 'Cart and checkout',
'description' => 'Ensures the cart and checkout process is functioning for both anonymous and authenticated users.',
'group' => 'Ubercart',
);
}
/**
* Overrides DrupalWebTestCase::setUp().
*/
function setUp() {
$modules = array('uc_payment', 'uc_payment_pack', 'uc_roles');
$permissions = array('administer permissions');
parent::setUp($modules, $permissions);
}
/**
* Creates a new order.
*/
function createOrder($fields = array()) {
$order = uc_order_new();
foreach ($fields as $key => $value) {
$order->$key = $value;
}
if (empty($order->primary_email)) {
$order->primary_email = $this->randomString() . '@example.org';
}
if (!isset($fields['products'])) {
$item = clone $this->product;
$item->qty = 1;
$item->price = $item->sell_price;
$item->data = array();
$order->products = array($item);
}
$order->order_total = uc_order_get_total($order, TRUE);
$order->line_items = uc_order_load_line_items($order, TRUE);
uc_order_save($order);
return $order;
}
function testCartAPI() {
// Test the empty cart.
$items = uc_cart_get_contents();
$this->assertEqual($items, array(), 'Cart is an empty array.');
// Add an item to the cart.
uc_cart_add_item($this->product->nid);
$items = uc_cart_get_contents();
$this->assertEqual(count($items), 1, 'Cart contains one item.');
$item = reset($items);
$this->assertEqual($item->nid, $this->product->nid, 'Cart item nid is correct.');
$this->assertEqual($item->qty, 1, 'Cart item quantity is correct.');
// Add more of the same item.
$qty = mt_rand(1, 100);
uc_cart_add_item($this->product->nid, $qty);
$items = uc_cart_get_contents();
$this->assertEqual(count($items), 1, 'Updated cart contains one item.');
$item = reset($items);
$this->assertEqual($item->qty, $qty + 1, 'Updated cart item quantity is correct.');
// Set the quantity and data.
$qty = mt_rand(1, 100);
$item->qty = $qty;
$item->data['updated'] = TRUE;
uc_cart_update_item($item);
$items = uc_cart_get_contents();
$item = reset($items);
$this->assertEqual($item->qty, $qty, 'Set cart item quantity is correct.');
$this->assertTrue($item->data['updated'], 'Set cart item data is correct.');
// Add an item with different data to the cart.
uc_cart_add_item($this->product->nid, 1, array('test' => TRUE));
$items = uc_cart_get_contents();
$this->assertEqual(count($items), 2, 'Updated cart contains two items.');
// Remove the items.
foreach ($items as $item) {
uc_cart_remove_item($item->nid, NULL, $item->data);
}
// @TODO: remove the need for this
uc_cart_get_contents(NULL, 'rebuild');
$items = uc_cart_get_contents();
$this->assertEqual(count($items), 0, 'Cart is empty after removal.');
// Empty the cart.
uc_cart_add_item($this->product->nid);
uc_cart_empty();
$items = uc_cart_get_contents();
$this->assertEqual($items, array(), 'Cart is emptied correctly.');
}
function testCart() {
module_enable(array('uc_cart_entity_test'));
// Test the empty cart.
$this->drupalGet('cart');
$this->assertText('There are no products in your shopping cart.');
// Add an item to the cart.
$this->drupalPost('node/' . $this->product->nid, array(), t('Add to cart'));
$this->assertText($this->product->title . ' added to your shopping cart.');
$this->assertText('hook_uc_cart_item_insert fired');
// Test the cart page.
$this->drupalGet('cart');
$this->assertText($this->product->title, t('The product is in the cart.'));
$this->assertFieldByName('items[0][qty]', 1, t('The product quantity is 1.'));
// Add the item again.
$this->drupalPost('node/' . $this->product->nid, array(), t('Add to cart'));
$this->assertText('Your item(s) have been updated.');
$this->assertText('hook_uc_cart_item_update fired');
// Test the cart page again.
$this->drupalGet('cart');
$this->assertFieldByName('items[0][qty]', 2, t('The product quantity is 2.'));
// Update the quantity.
$qty = mt_rand(3, 100);
$this->drupalPost('cart', array('items[0][qty]' => $qty), t('Update cart'));
$this->assertText('Your cart has been updated.');
$this->assertFieldByName('items[0][qty]', $qty, t('The product quantity was updated.'));
$this->assertText('hook_uc_cart_item_update fired');
// Update the quantity to zero.
$this->drupalPost('cart', array('items[0][qty]' => 0), t('Update cart'));
$this->assertText('Your cart has been updated.');
$this->assertText('There are no products in your shopping cart.');
$this->assertText('hook_uc_cart_item_delete fired');
// Test the remove item button.
$this->drupalPost('node/' . $this->product->nid, array(), t('Add to cart'));
$this->drupalPost('cart', array(), t('Remove'));
$this->assertText($this->product->title . ' removed from your shopping cart.');
$this->assertText('There are no products in your shopping cart.');
$this->assertText('hook_uc_cart_item_delete fired');
}
function testCartMerge() {
// Add an item to the cart as an anonymous user.
$this->drupalLogin($this->customer);
$this->drupalPost('node/' . $this->product->nid, array(), t('Add to cart'));
$this->assertText($this->product->title . ' added to your shopping cart.');
$this->drupalLogout();
// Add an item to the cart as an anonymous user.
$this->drupalPost('node/' . $this->product->nid, array(), t('Add to cart'));
$this->assertText($this->product->title . ' added to your shopping cart.');
// Log in and check the items are merged.
$this->drupalLogin($this->customer);
$this->drupalGet('cart');
$this->assertText($this->product->title, t('The product remains in the cart after logging in.'));
$this->assertFieldByName('items[0][qty]', 2, t('The product quantity is 2.'));
}
function testDeletedCartItem() {
// Add a product to the cart, then delete the node.
$this->drupalPost('node/' . $this->product->nid, array(), t('Add to cart'));
node_delete($this->product->nid);
// Test that the cart is empty.
$this->drupalGet('cart');
$this->assertText('There are no products in your shopping cart.');
$this->assertEqual(uc_cart_get_total_qty(), 0, 'There are no items in the cart.');
}
function testMaximumQuantityRule() {
// Enable the example maximum quantity rule.
$rule = rules_config_load('uc_cart_maximum_product_qty');
$rule->active = TRUE;
$rule->save();
// Try to add more items than allowed to the cart.
$this->drupalPost('node/' . $this->product->nid, array(), t('Add to cart'));
$this->drupalPost('cart', array('items[0][qty]' => 11), t('Update cart'));
// Test the restriction was applied.
$this->assertText('You are only allowed to order a maximum of 10 of ' . $this->product->title . '.');
$this->assertFieldByName('items[0][qty]', 10);
}
function testCheckout() {
// Allow customer to specify username and password, but don't log in after checkout.
$settings = array(
'uc_cart_new_account_name' => TRUE,
'uc_cart_new_account_password' => TRUE,
'uc_new_customer_login' => FALSE,
);
$this->drupalLogin($this->adminUser);
$this->drupalPost('admin/store/settings/checkout', $settings, t('Save configuration'));
$this->drupalLogout();
$new_user = new stdClass();
$new_user->name = $this->randomName(20);
$new_user->pass_raw = $this->randomName(20);
// Test as anonymous user.
$this->drupalPost('node/' . $this->product->nid, array(), t('Add to cart'));
$this->checkout(array(
'panes[customer][new_account][name]' => $new_user->name,
'panes[customer][new_account][pass]' => $new_user->pass_raw,
'panes[customer][new_account][pass_confirm]' => $new_user->pass_raw,
));
$this->assertRaw('Your order is complete!');
$this->assertText($new_user->name, 'Username is shown on screen.');
$this->assertNoText($new_user->pass_raw, 'Password is not shown on screen.');
// Check that cart is now empty.
$this->drupalGet('cart');
$this->assertText('There are no products in your shopping cart.');
// Test new account email.
$mail = $this->drupalGetMails(array('id' => 'user_register_no_approval_required'));
$mail = array_pop($mail);
$this->assertTrue(strpos($mail['body'], $new_user->name) !== FALSE, 'Mail body contains username.');
// Test invoice email.
$mail = $this->drupalGetMails(array('subject' => 'Your Order at Ubercart'));
$mail = array_pop($mail);
$this->assertTrue(strpos($mail['body'], $new_user->name) !== FALSE, 'Invoice body contains username.');
$this->assertFalse(strpos($mail['body'], $new_user->pass_raw) !== FALSE, 'Mail body does not contain password.');
// Check that the password works.
$this->drupalLogout();
$this->drupalLogin($new_user);
// Test again as authenticated user.
$this->drupalPost('node/' . $this->product->nid, array(), t('Add to cart'));
$this->checkout();
$this->assertRaw('Your order is complete!');
$this->assertRaw('While logged in');
// Test again with generated username and password.
$this->drupalLogout();
$this->drupalPost('node/' . $this->product->nid, array(), t('Add to cart'));
$this->checkout();
$this->assertRaw('Your order is complete!');
// Test new account email.
$mail = $this->drupalGetMails(array('id' => 'user_register_no_approval_required'));
$mail = array_pop($mail);
$new_user = new stdClass();
$new_user->name = $mail['params']['account']->name;
$new_user->pass_raw = $mail['params']['account']->password;
$this->assertTrue(!empty($new_user->name), 'New username is not empty.');
$this->assertTrue(!empty($new_user->pass_raw), 'New password is not empty.');
$this->assertTrue(strpos($mail['body'], $new_user->name) !== FALSE, 'Mail body contains username.');
// Test invoice email.
$mail = $this->drupalGetMails(array('subject' => 'Your Order at Ubercart'));
$mail = array_pop($mail);
$this->assertTrue(strpos($mail['body'], $new_user->name) !== FALSE, 'Invoice body contains username.');
$this->assertTrue(strpos($mail['body'], $new_user->pass_raw) !== FALSE, 'Invoice body contains password.');
// We can check the password now we know it.
$this->assertText($new_user->name, 'Username is shown on screen.');
$this->assertText($new_user->pass_raw, 'Password is shown on screen.');
// Check that the password works.
$this->drupalLogout();
$this->drupalLogin($new_user);
// Test again with an existing email address
$this->drupalLogout();
$this->drupalPost('node/' . $this->product->nid, array(), t('Add to cart'));
$this->checkout(array('panes[customer][primary_email]' => $this->customer->mail));
$this->assertRaw('Your order is complete!');
$this->assertRaw('order has been attached to the account we found');
}
function testCheckoutNewUsername() {
// Configure the checkout for this test.
$this->drupalLogin($this->adminUser);
$settings = array(
// Allow customer to specify username.
'uc_cart_new_account_name' => TRUE,
// Disable address panes.
'uc_pane_delivery_enabled' => FALSE,
'uc_pane_billing_enabled' => FALSE,
);
$this->drupalPost('admin/store/settings/checkout/panes', $settings, t('Save configuration'));
$this->drupalLogout();
// Test with an account that already exists.
$this->drupalPost('node/' . $this->product->nid, array(), t('Add to cart'));
$edit = array(
'panes[customer][primary_email]' => $this->randomName(8) . '@example.com',
'panes[customer][new_account][name]' => $this->adminUser->name,
);
$this->drupalPost('cart/checkout', $edit, 'Review order');
$this->assertText('The username ' . $this->adminUser->name . ' is already taken.');
// Let the account be automatically created instead.
$edit = array(
'panes[customer][primary_email]' => $this->randomName(8) . '@example.com',
'panes[customer][new_account][name]' => '',
);
$this->drupalPost('cart/checkout', $edit, 'Review order');
$this->drupalPost(NULL, array(), 'Submit order');
$this->assertText('Your order is complete!');
$this->assertText('A new account has been created');
}
function testCheckoutBlockedUser() {
// Block user after checkout.
$settings = array(
'uc_new_customer_status_active' => FALSE,
);
$this->drupalLogin($this->adminUser);
$this->drupalPost('admin/store/settings/checkout', $settings, t('Save configuration'));
$this->drupalLogout();
// Test as anonymous user.
$this->drupalPost('node/' . $this->product->nid, array(), t('Add to cart'));
$this->checkout();
$this->assertRaw('Your order is complete!');
// Test new account email.
$mail = $this->drupalGetMails(array('id' => 'user_register_pending_approval'));
$this->assertTrue(!empty($mail), 'Blocked user email found.');
$mail = $this->drupalGetMails(array('id' => 'user_register_no_approval_required'));
$this->assertTrue(empty($mail), 'No unblocked user email found.');
}
function testCheckoutLogin() {
// Log in after checkout.
variable_set('uc_new_customer_login', TRUE);
// Test checkout.
$this->drupalGet('node/' . $this->product->nid);
$this->drupalPost(NULL, array(), t('Add to cart'));
$this->assertNotNull($this->session_id, 'Session ID is set.');
$session_id = $this->session_id;
$this->checkout();
$this->assertRaw('Your order is complete!');
$this->assertRaw('you are already logged in');
// Confirm login.
$this->assertNotNull($this->session_id, 'Session ID is set.');
$this->assertNotIdentical($this->session_id, $session_id, 'Session ID has changed.');
$this->drupalGet('<front>');
$this->assertText('My account', 'User is logged in.');
// Check that cart is now empty.
$this->drupalGet('cart');
$this->assertText('There are no products in your shopping cart.');
}
function testCheckoutComplete() {
// Payment notification is received first.
$order_data = array('primary_email' => 'simpletest@ubercart.org');
$order = $this->createOrder($order_data);
uc_payment_enter($order->order_id, 'SimpleTest', $order->order_total);
$output = uc_cart_complete_sale($order);
// Check that a new account was created.
$this->assertTrue(strpos($output['#message'], 'new account has been created') !== FALSE, 'Checkout message mentions new account.');
// 2 e-mails: new account, customer invoice
$mails = $this->drupalGetMails();
$this->assertEqual(count($mails), 2, '2 e-mails were sent.');
variable_del('drupal_test_email_collector');
$password = $mails[0]['params']['account']->password;
$this->assertTrue(!empty($password), 'New password is not empty.');
// In D7, new account emails do not contain the password.
//$this->assertTrue(strpos($mails[0]['body'], $password) !== FALSE, 'Mail body contains password.');
// Different user, sees the checkout page first.
$order_data = array('primary_email' => 'simpletest2@ubercart.org');
$order = $this->createOrder($order_data);
$output = uc_cart_complete_sale($order, TRUE);
uc_payment_enter($order->order_id, 'SimpleTest', $order->order_total);
// 2 e-mails: new account, customer invoice
$mails = $this->drupalGetMails();
$this->assertEqual(count($mails), 2, '2 e-mails were sent.');
variable_del('drupal_test_email_collector');
$password = $mails[0]['params']['account']->password;
$this->assertTrue(!empty($password), 'New password is not empty.');
// In D7, new account emails do not contain the password.
//$this->assertTrue(strpos($mails[0]['body'], $password) !== FALSE, 'Mail body contains password.');
// Same user, new order.
$order = $this->createOrder($order_data);
$output = uc_cart_complete_sale($order, TRUE);
uc_payment_enter($order->order_id, 'SimpleTest', $order->order_total);
// Check that no new account was created.
$this->assertTrue(strpos($output['#message'], 'order has been attached to the account') !== FALSE, 'Checkout message mentions existing account.');
// 1 e-mail: customer invoice
$mails = $this->drupalGetMails();
$this->assertEqual(count($mails), 1, '1 e-mail was sent.');
variable_del('drupal_test_email_collector');
}
function testCheckoutRoleAssignment() {
// Add role assignment to the test product.
$rid = $this->drupalCreateRole(array('access content'));
$this->drupalLogin($this->adminUser);
$this->drupalPost('node/' . $this->product->nid . '/edit/features', array('feature' => 'role'), t('Add'));
$this->drupalPost(NULL, array('uc_roles_role' => $rid), t('Save feature'));
// Process an anonymous, shippable order.
$item = clone $this->product;
$item->qty = 1;
$item->price = $item->sell_price;
$item->data = array('shippable' => TRUE);
$order = $this->createOrder(array(
'products' => array($item),
));
uc_payment_enter($order->order_id, 'SimpleTest', $order->order_total);
// Find the order uid.
$uid = db_query("SELECT uid FROM {uc_orders} ORDER BY order_id DESC")->fetchField();
$account = user_load($uid);
$this->assertTrue(isset($account->roles[$rid]), 'New user was granted role.');
$order = uc_order_load($order->order_id);
$this->assertEqual($order->order_status, 'payment_received', 'Shippable order was set to payment received.');
// 3 e-mails: new account, customer invoice, role assignment
$mails = $this->drupalGetMails();
$this->assertEqual(count($mails), 3, '3 e-mails were sent.');
variable_del('drupal_test_email_collector');
// Test again with an existing email address and a non-shippable order.
$item->data = array('shippable' => FALSE);
$order = $this->createOrder(array(
'primary_email' => $this->customer->mail,
'products' => array($item),
));
uc_payment_enter($order->order_id, 'SimpleTest', $order->order_total);
$account = user_load($this->customer->uid);
$this->assertTrue(isset($account->roles[$rid]), 'Existing user was granted role.');
$order = uc_order_load($order->order_id);
$this->assertEqual($order->order_status, 'completed', 'Non-shippable order was set to completed.');
// 2 e-mails: customer invoice, role assignment
$mails = $this->drupalGetMails();
$this->assertEqual(count($mails), 2, '2 e-mails were sent.');
variable_del('drupal_test_email_collector');
}
/**
* Tests that cart orders are marked abandoned after a timeout.
*/
function testCartOrderTimeout() {
$this->drupalPost('node/' . $this->product->nid, array(), t('Add to cart'));
$this->drupalPost('cart', array(), 'Checkout');
$this->assertText(
t('Enter your billing address and information here.'),
t('Viewed cart page: Billing pane has been displayed.')
);
// Build the panes.
$zone_id = db_query_range('SELECT zone_id FROM {uc_zones} WHERE zone_country_id = :country ORDER BY rand()', 0, 1, array('country' => variable_get('uc_store_country', 840)))->fetchField();
$oldname = $this->randomName(10);
$edit = array(
'panes[delivery][delivery_first_name]' => $oldname,
'panes[delivery][delivery_last_name]' => $this->randomName(10),
'panes[delivery][delivery_street1]' => $this->randomName(10),
'panes[delivery][delivery_city]' => $this->randomName(10),
'panes[delivery][delivery_zone]' => $zone_id,
'panes[delivery][delivery_postal_code]' => mt_rand(10000, 99999),
'panes[billing][billing_first_name]' => $this->randomName(10),
'panes[billing][billing_last_name]' => $this->randomName(10),
'panes[billing][billing_street1]' => $this->randomName(10),
'panes[billing][billing_city]' => $this->randomName(10),
'panes[billing][billing_zone]' => $zone_id,
'panes[billing][billing_postal_code]' => mt_rand(10000, 99999),
);
// If the email address has not been set, and the user has not logged in,
// add a primary email address.
if (!isset($edit['panes[customer][primary_email]']) && !$this->loggedInUser) {
$edit['panes[customer][primary_email]'] = $this->randomName(8) . '@example.com';
}
// Submit the checkout page.
$this->drupalPost('cart/checkout', $edit, t('Review order'));
$order_id = db_query("SELECT order_id FROM {uc_orders} WHERE delivery_first_name = :name", array(':name' => $oldname))->fetchField();
if ($order_id) {
// Go to a different page, then back to order - make sure order_id is the same.
$this->drupalGet('<front>');
$this->drupalPost('node/' . $this->product->nid, array(), t('Add to cart'));
$this->drupalPost('cart', array(), 'Checkout');
$this->assertRaw($oldname, 'Customer name was unchanged.');
$this->drupalPost('cart/checkout', $edit, t('Review order'));
$new_order_id = db_query("SELECT order_id FROM {uc_orders} WHERE delivery_first_name = :name", array(':name' => $edit['panes[delivery][delivery_first_name]']))->fetchField();
$this->assertEqual($order_id, $new_order_id, 'Original order_id was reused.');
// Jump 10 minutes into the future.
db_update('uc_orders')
->fields(array(
'modified' => time() - UC_CART_ORDER_TIMEOUT - 1,
))
->condition('order_id', $order_id)
->execute();
$old_order = uc_order_load($order_id);
// Go to a different page, then back to order - verify that we are using a new order.
$this->drupalGet('<front>');
$this->drupalPost('cart', array(), 'Checkout');
$this->assertNoRaw($oldname, 'Customer name was cleared after timeout.');
$newname = $this->randomName(10);
$edit['panes[delivery][delivery_first_name]'] = $newname;
$this->drupalPost('cart/checkout', $edit, t('Review order'));
$new_order_id = db_query("SELECT order_id FROM {uc_orders} WHERE delivery_first_name = :name", array(':name' => $newname))->fetchField();
$this->assertNotEqual($order_id, $new_order_id, 'New order was created after timeout.');
// Verify that the status of old order is abandoned.
$old_order = uc_order_load($order_id, TRUE);
$this->assertEqual($old_order->order_status, 'abandoned', 'Original order was marked abandoned.');
}
else {
$this->fail('No order was created.');
}
}
function testCustomerInformationCheckoutPane() {
// Log in as a customer and add an item to the cart.
$this->drupalLogin($this->customer);
$this->drupalPost('node/' . $this->product->nid, array(), t('Add to cart'));
$this->drupalPost('cart', array(), 'Checkout');
// Test the customer information pane.
$mail = $this->customer->mail;
$this->assertText('Customer information');
$this->assertText('Order information will be sent to your account e-mail listed below.');
$this->assertText('E-mail address: ' . $mail);
// Use the 'edit' link to change the email address on the account.
$new_mail = $this->randomName() . '@example.com';
$this->clickLink('edit');
$data = array(
'current_pass' => $this->customer->pass_raw,
'mail' => $new_mail,
);
$this->drupalPost(NULL, $data, 'Save');
// Test the updated email address.
$this->assertText('Order information will be sent to your account e-mail listed below.');
$this->assertNoText('E-mail address: ' . $mail);
$this->assertText('E-mail address: ' . $new_mail);
}
}
/**
* Tests the cart settings page.
*/
class UbercartCartSettingsTestCase extends UbercartTestHelper {
public static function getInfo() {
return array(
'name' => 'Cart settings',
'description' => 'Tests the cart settings page.',
'group' => 'Ubercart',
);
}
function testAddToCartRedirect() {
$this->drupalLogin($this->adminUser);
$this->drupalGet('admin/store/settings/cart');
$this->assertField(
'uc_add_item_redirect',
t('Add to cart redirect field exists')
);
$redirect = $this->randomName(8);
$this->drupalPost(
'admin/store/settings/cart',
array('uc_add_item_redirect' => $redirect),
t('Save configuration')
);
$this->drupalPost(
'node/' . $this->product->nid,
array(),
t('Add to cart')
);
$url_pass = ($this->getUrl() == url($redirect, array('absolute' => TRUE)));
$this->assertTrue(
$url_pass,
t('Add to cart redirect takes user to the correct URL.')
);
$this->drupalPost(
'admin/store/settings/cart',
array('uc_add_item_redirect' => '<none>'),
t('Save configuration')
);
$this->drupalPost('node/' . $this->product->nid, array(), t('Add to cart'), array('query' => array('test' => 'querystring')));
$url = url('node/' . $this->product->nid, array('absolute' => TRUE, 'query' => array('test' => 'querystring')));
$this->assertTrue($this->getUrl() == $url, 'Add to cart no-redirect works with a query string.');
}
function testMinimumSubtotal() {
$this->drupalLogin($this->adminUser);
$this->drupalGet('admin/store/settings/cart');
$this->assertField(
'uc_minimum_subtotal',
t('Minimum order subtotal field exists')
);
$minimum_subtotal = mt_rand(2, 9999);
$this->drupalPost(
NULL,
array('uc_minimum_subtotal' => $minimum_subtotal),
t('Save configuration')
);
// Create two products, one below the minimum price, and one above the minimum price.
$product_below_limit = $this->createProduct(array('sell_price' => $minimum_subtotal - 1));
$product_above_limit = $this->createProduct(array('sell_price' => $minimum_subtotal + 1));
$this->drupalLogout();
// Check to see if the lower priced product triggers the minimum price logic.
$this->drupalPost(
'node/' . $product_below_limit->nid,
array(),
t('Add to cart')
);
$this->drupalPost('cart',
array(),
t('Checkout')
);
$this->assertRaw(
'The minimum order subtotal for checkout is',
t('Prevented checkout below the minimum order total.')
);
// Add another product to the cart, and verify that we land on the checkout page.
$this->drupalPost(
'node/' . $product_above_limit->nid,
array(),
t('Add to cart')
);
$this->drupalPost(
'cart',
array(),
t('Checkout')
);
$this->assertText('Enter your billing address and information here.');
}
function testEmptyCart() {
// Test that the feature is not enabled by default.
$this->drupalPost('node/' . $this->product->nid, array(), 'Add to cart');
$this->assertNoRaw('Empty cart');
// Test the admin settings itself.
$this->drupalLogin($this->adminUser);
$this->drupalGet('admin/store/settings/cart');
$this->assertField('uc_cart_empty_button', 'Empty cart button checkbox found.');
$this->drupalPost(NULL, array('uc_cart_empty_button' => TRUE), 'Save configuration');
// Test the feature itself.
$this->drupalGet('cart');
$this->drupalPost(NULL, array(), 'Empty cart');
$this->assertText('Are you sure you want to empty your shopping cart? ');
$this->drupalPost(NULL, array(), 'Confirm');
$this->assertText('There are no products in your shopping cart.');
}
function testContinueShopping() {
// Continue shopping link should take you back to the product page.
$this->drupalPost(
'node/' . $this->product->nid,
array(),
t('Add to cart')
);
$this->assertLink(
t('Continue shopping'),
0,
t('Continue shopping link appears on the page.')
);
$links = $this->xpath('//a[@href="' . url('node/' . $this->product->nid, array('absolute' => FALSE)) . '"]');
$this->assertTrue(
isset($links[0]),
t('Continue shopping link returns to the product page.')
);
$this->drupalLogin($this->adminUser);
$this->drupalGet('admin/store/settings/cart');
$this->assertField(
'uc_continue_shopping_type',
t('Continue shopping element display field exists')
);
$this->assertField(
'uc_continue_shopping_url',
t('Default continue shopping link URL field exists')
);
// Test continue shopping button that sends users to a fixed URL.
$settings = array(
'uc_continue_shopping_type' => 'button',
'uc_continue_shopping_use_last_url' => FALSE,
'uc_continue_shopping_url' => $this->randomName(8),
);
$this->drupalPost(
NULL,
$settings,
t('Save configuration')
);
$this->drupalPost(
'cart',
array(),
t('Continue shopping')
);
$url_pass = ($this->getUrl() == url($settings['uc_continue_shopping_url'],
array('absolute' => TRUE)));
$this->assertTrue(
$url_pass,
t('Continue shopping button takes the user to the correct URL.')
);
}
function testCartBreadcrumb() {
$this->drupalLogin($this->adminUser);
$this->drupalGet('admin/store/settings/cart');
$this->assertField(
'uc_cart_breadcrumb_text',
t('Custom cart breadcrumb text field exists')
);
$this->assertField(
'uc_cart_breadcrumb_url',
t('Custom cart breadcrumb URL')
);
$settings = array(
'uc_cart_breadcrumb_text' => $this->randomName(8),
'uc_cart_breadcrumb_url' => $this->randomName(7),
);
$this->drupalPost(
NULL,
$settings,
t('Save configuration')
);
$this->drupalPost(
'node/' . $this->product->nid,
array(),
t('Add to cart')
);
$this->assertLink(
$settings['uc_cart_breadcrumb_text'],
0,
t('The breadcrumb link text is set correctly.')
);
$links = $this->xpath('//a[@href="' . url($settings['uc_cart_breadcrumb_url']) . '"]');
$this->assertTrue(
isset($links[0]),
t('The breadcrumb link is set correctly.')
);
}
}
/**
* Tests the checkout settings page.
*/
class UbercartCheckoutSettingsTestCase extends UbercartTestHelper {
public static function getInfo() {
return array(
'name' => 'Checkout settings',
'description' => 'Tests the checkout settings page.',
'group' => 'Ubercart',
);
}
function testEnableCheckout() {
$this->drupalLogin($this->adminUser);
$this->drupalGet('admin/store/settings/checkout');
$this->assertField(
'uc_checkout_enabled',
t('Enable checkout field exists')
);
$this->drupalPost(
'admin/store/settings/checkout',
array('uc_checkout_enabled' => FALSE),
t('Save configuration')
);
$this->drupalPost(
'node/' . $this->product->nid,
array(),
t('Add to cart')
);
$this->assertNoRaw(t('Checkout'));
$buttons = $this->xpath('//input[@value="' . t('Checkout') . '"]');
$this->assertFalse(
isset($buttons[0]),
t('The checkout button is not shown.')
);
}
function testAnonymousCheckout() {
$this->drupalLogin($this->adminUser);
$this->drupalGet('admin/store/settings/checkout');
$this->assertField(
'uc_checkout_anonymous',
t('Anonymous checkout field exists')
);
$this->drupalPost(
'admin/store/settings/checkout',
array('uc_checkout_anonymous' => FALSE),
t('Save configuration')
);
$this->drupalLogout();
$this->drupalPost(
'node/' . $this->product->nid,
array(),
t('Add to cart')
);
$this->drupalPost(
'cart',
array(), 'Checkout');
$this->assertNoText(
'Enter your billing address and information here.',
t('The checkout page is not displayed.')
);
}
}

View File

@@ -0,0 +1,11 @@
name = Cart item entity test module
core = 7.x
dependencies[] = uc_cart
hidden = TRUE
; Information added by Drupal.org packaging script on 2013-12-17
version = "7.x-3.6"
core = "7.x"
project = "ubercart"
datestamp = "1387304010"

View File

@@ -0,0 +1,22 @@
<?php
/**
* Implements hook_uc_cart_item_insert().
*/
function uc_cart_entity_test_uc_cart_item_insert($entity) {
drupal_set_message('hook_uc_cart_item_insert fired');
}
/**
* Implements hook_uc_cart_item_update().
*/
function uc_cart_entity_test_uc_cart_item_update($entity) {
drupal_set_message('hook_uc_cart_item_update fired');
}
/**
* Implements hook_uc_cart_item_delete().
*/
function uc_cart_entity_test_uc_cart_item_delete($entity) {
drupal_set_message('hook_uc_cart_item_delete fired');
}

View File

@@ -0,0 +1,459 @@
<?php
/**
* @file
* Cart administration menu items.
*/
/**
* General settings for the shopping cart.
*
* @see uc_cart_cart_settings_form_validate()
* @ingroup forms
*/
function uc_cart_cart_settings_form($form, &$form_state) {
// Put fieldsets into vertical tabs
$form['cart-settings'] = array(
'#type' => 'vertical_tabs',
'#attached' => array(
'js' => array(
'vertical-tabs' => drupal_get_path('module', 'uc_cart') . '/uc_cart.admin.js',
),
),
);
$form['general'] = array(
'#type' => 'fieldset',
'#title' => t('Basic settings'),
'#group' => 'cart-settings',
);
$panes = uc_cart_cart_pane_list(NULL);
$form['general']['panes'] = array(
'#theme' => 'uc_pane_sort_table',
'#pane_prefix' => 'uc_cap',
'#draggable' => 'uc-cart-pane-weight',
);
foreach ($panes as $id => $pane) {
$form['general']['panes'][$id]['uc_cap_' . $id . '_enabled'] = array(
'#type' => 'checkbox',
'#title' => check_plain($pane['title']),
'#default_value' => variable_get('uc_cap_'. $pane['id'] .'_enabled', $pane['enabled']),
);
$form['general']['panes'][$id]['uc_cap_' . $id . '_weight'] = array(
'#type' => 'weight',
'#delta' => 10,
'#default_value' => variable_get('uc_cap_'. $pane['id'] .'_weight', $pane['weight']),
'#attributes' => array('class' => array('uc-cart-pane-weight')),
);
}
$form['general']['uc_cart_add_item_msg'] = array(
'#type' => 'checkbox',
'#title' => t('Display a message when a customer adds an item to their cart.'),
'#default_value' => variable_get('uc_cart_add_item_msg', TRUE),
);
$form['general']['uc_add_item_redirect'] = array(
'#type' => 'textfield',
'#title' => t('Add to cart redirect'),
'#description' => t('Enter the page to redirect to when a customer adds an item to their cart, or &lt;none&gt; for no redirect.'),
'#default_value' => variable_get('uc_add_item_redirect', 'cart'),
'#size' => 32,
'#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q='),
);
$form['general']['uc_cart_empty_button'] = array(
'#type' => 'checkbox',
'#title' => t('Show an "Empty cart" button on the cart page.'),
'#default_value' => variable_get('uc_cart_empty_button', FALSE),
);
$form['general']['uc_minimum_subtotal'] = array(
'#type' => 'uc_price',
'#title' => t('Minimum order subtotal'),
'#description' => t('Customers will not be allowed to check out if the subtotal of items in their cart is less than this amount.'),
'#default_value' => variable_get('uc_minimum_subtotal', 0),
);
$form['lifetime'] = array(
'#type' => 'fieldset',
'#title' => t('Cart lifetime'),
'#description' => t('Set the length of time that products remain in the cart. Cron must be running for this feature to work.'),
'#group' => 'cart-settings',
);
$durations = array(
'singular' => array(
'minutes' => t('minute'),
'hours' => t('hour'),
'days' => t('day'),
'weeks' => t('week'),
'years' => t('year'),
),
'plural' => array(
'minutes' => t('minutes'),
'hours' => t('hours'),
'days' => t('days'),
'weeks' => t('weeks'),
'years' => t('years'),
),
);
$form['lifetime']['anonymous'] = array(
'#type' => 'fieldset',
'#title' => t('Anonymous users'),
'#attributes' => array('class' => array('uc-inline-form', 'clearfix')),
);
$form['lifetime']['anonymous']['uc_cart_anon_duration'] = array(
'#type' => 'select',
'#title' => t('Duration'),
'#options' => drupal_map_assoc(range(1, 60)),
'#default_value' => variable_get('uc_cart_anon_duration', '4'),
);
$form['lifetime']['anonymous']['uc_cart_anon_unit'] = array(
'#type' => 'select',
'#title' => t('Units'),
'#options' => array(
'minutes' => t('Minute(s)'),
'hours' => t('Hour(s)'),
'days' => t('Day(s)'),
'weeks' => t('Week(s)'),
'years' => t('Year(s)'),
),
'#default_value' => variable_get('uc_cart_anon_unit', 'hours'),
);
$form['lifetime']['authenticated'] = array(
'#type' => 'fieldset',
'#title' => t('Authenticated users'),
'#attributes' => array('class' => array('uc-inline-form', 'clearfix')),
);
$form['lifetime']['authenticated']['uc_cart_auth_duration'] = array(
'#type' => 'select',
'#title' => t('Duration'),
'#options' => drupal_map_assoc(range(1, 60)),
'#default_value' => variable_get('uc_cart_auth_duration', '1'),
);
$form['lifetime']['authenticated']['uc_cart_auth_unit'] = array(
'#type' => 'select',
'#title' => t('Units'),
'#options' => array(
'hours' => t('Hour(s)'),
'days' => t('Day(s)'),
'weeks' => t('Week(s)'),
'years' => t('Year(s)'),
),
'#default_value' => variable_get('uc_cart_auth_unit', 'years'),
);
$form['continue_shopping'] = array(
'#type' => 'fieldset',
'#title' => t('Continue shopping element'),
'#description' => t('These settings control the <em>continue shopping</em> option on the cart page.'),
'#group' => 'cart-settings',
);
$form['continue_shopping']['uc_continue_shopping_type'] = array(
'#type' => 'radios',
'#title' => t('<em>Continue shopping</em> element'),
'#options' => array(
'link' => t('Text link'),
'button' => t('Button'),
'none' => t('Do not display'),
),
'#default_value' => variable_get('uc_continue_shopping_type', 'link'),
);
$form['continue_shopping']['uc_continue_shopping_use_last_url'] = array(
'#type' => 'checkbox',
'#title' => t('Make <em>continue shopping</em> go back to the last item that was added to the cart.'),
'#description' => t('If this is disabled or the item is unavailable, the URL specified below will be used.'),
'#default_value' => variable_get('uc_continue_shopping_use_last_url', TRUE),
);
$form['continue_shopping']['uc_continue_shopping_url'] = array(
'#type' => 'textfield',
'#title' => t('Default <em>continue shopping</em> destination'),
'#default_value' => variable_get('uc_continue_shopping_url', ''),
'#size' => 32,
'#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q='),
);
$form['breadcrumb'] = array(
'#type' => 'fieldset',
'#title' => t('Cart breadcrumb'),
'#description' => t('Drupal automatically adds a <em>Home</em> breadcrumb to the cart page, or you can use these settings to specify a custom breadcrumb.'),
'#group' => 'cart-settings',
);
$form['breadcrumb']['uc_cart_breadcrumb_text'] = array(
'#type' => 'textfield',
'#title' => t('Cart page breadcrumb text'),
'#description' => t('Leave blank to use the default <em>Home</em> breadcrumb.'),
'#default_value' => variable_get('uc_cart_breadcrumb_text', ''),
);
$form['breadcrumb']['uc_cart_breadcrumb_url'] = array(
'#type' => 'textfield',
'#title' => t('Cart page breadcrumb destination'),
'#default_value' => variable_get('uc_cart_breadcrumb_url', ''),
'#size' => 32,
'#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q='),
);
return system_settings_form($form);
}
/**
* Form validation for uc_cart_cart_settings_form().
*
* @see uc_cart_cart_settings_form()
*/
function uc_cart_cart_settings_form_validate($form, &$form_state) {
if (!is_numeric($form_state['values']['uc_minimum_subtotal']) || $form_state['values']['uc_minimum_subtotal'] < 0 || $form_state['values']['uc_minimum_subtotal'] === '-0') {
form_set_error('uc_minimum_subtotal', t('Minimum order subtotal should be a non-negative number.'));
}
}
/**
* General checkout settings.
*
* @ingroup forms
*/
function uc_cart_checkout_settings_form($form, &$form_state) {
// Put fieldsets into vertical tabs
$form['checkout-settings'] = array(
'#type' => 'vertical_tabs',
'#attached' => array(
'js' => array(
'vertical-tabs' => drupal_get_path('module', 'uc_cart') . '/uc_cart.admin.js',
),
),
);
$form['checkout'] = array(
'#type' => 'fieldset',
'#title' => t('Basic settings'),
'#group' => 'checkout-settings',
);
$form['checkout']['uc_checkout_enabled'] = array(
'#type' => 'checkbox',
'#title' => t('Enable checkout.'),
'#description' => t('Disable this to use only third party checkout services, such as PayPal Express Checkout.'),
'#default_value' => variable_get('uc_checkout_enabled', TRUE),
);
$form['anonymous'] = array(
'#type' => 'fieldset',
'#title' => t('Anonymous checkout'),
'#group' => 'checkout-settings',
);
$form['anonymous']['uc_checkout_anonymous'] = array(
'#type' => 'checkbox',
'#title' => t('Enable anonymous checkout.'),
'#description' => t('Disable this to force users to log in before the checkout page.'),
'#default_value' => variable_get('uc_checkout_anonymous', TRUE),
);
$anon_state = array('visible' => array('input[name="uc_checkout_anonymous"]' => array('checked' => TRUE)));
$form['anonymous']['uc_cart_mail_existing'] = array(
'#type' => 'checkbox',
'#title' => t("Allow anonymous customers to use an existing account's email address."),
'#default_value' => variable_get('uc_cart_mail_existing', TRUE),
'#description' => t('If enabled, orders will be attached to the account matching the email address. If disabled, anonymous users using a registered email address must log in or use a different email address.'),
'#states' => $anon_state,
);
$form['anonymous']['uc_cart_email_validation'] = array(
'#type' => 'checkbox',
'#title' => t('Require e-mail confirmation for anonymous customers.'),
'#default_value' => variable_get('uc_cart_email_validation', FALSE),
'#states' => $anon_state,
);
$form['anonymous']['uc_cart_new_account_name'] = array(
'#type' => 'checkbox',
'#title' => t('Allow new customers to specify a username.'),
'#default_value' => variable_get('uc_cart_new_account_name', FALSE),
'#states' => $anon_state,
);
$form['anonymous']['uc_cart_new_account_password'] = array(
'#type' => 'checkbox',
'#title' => t('Allow new customers to specify a password.'),
'#default_value' => variable_get('uc_cart_new_account_password', FALSE),
'#states' => $anon_state,
);
$form['anonymous']['uc_new_customer_email'] = array(
'#type' => 'checkbox',
'#title' => t('Send new customers a separate e-mail with their account details.'),
'#default_value' => variable_get('uc_new_customer_email', TRUE),
'#states' => $anon_state,
);
$form['anonymous']['uc_new_customer_login'] = array(
'#type' => 'checkbox',
'#title' => t('Log in new customers after checkout.'),
'#default_value' => variable_get('uc_new_customer_login', FALSE),
'#states' => $anon_state,
);
$form['anonymous']['uc_new_customer_status_active'] = array(
'#type' => 'checkbox',
'#title' => t('Set new customer accounts to active.'),
'#description' => t('Uncheck to create new accounts but make them blocked.'),
'#default_value' => variable_get('uc_new_customer_status_active', TRUE),
'#states' => $anon_state,
);
$panes = _uc_checkout_pane_list();
$form['checkout']['panes'] = array(
'#theme' => 'uc_pane_sort_table',
'#pane_prefix' => 'uc_pane',
'#draggable' => 'uc-checkout-pane-weight',
);
foreach ($panes as $id => $pane) {
$form['checkout']['panes'][$id]['uc_pane_' . $id . '_enabled'] = array(
'#type' => 'checkbox',
'#title' => check_plain($pane['title']),
'#default_value' => variable_get('uc_pane_' . $id . '_enabled', $pane['enabled']),
);
$form['checkout']['panes'][$id]['uc_pane_' . $id . '_weight'] = array(
'#type' => 'weight',
'#default_value' => variable_get('uc_pane_' . $id . '_weight', $pane['weight']),
'#attributes' => array('class' => array('uc-checkout-pane-weight')),
);
$form['checkout']['panes'][$id]['#weight'] = variable_get('uc_pane_' . $id . '_weight', $pane['weight']);
$null = NULL;
$pane_settings = $pane['callback']('settings', $null, array());
if (is_array($pane_settings)) {
$form['pane_' . $id] = $pane_settings + array(
'#type' => 'fieldset',
'#title' => t('@pane pane', array('@pane' => $pane['title'])),
'#group' => 'checkout-settings',
);
}
}
$form['checkout']['uc_cart_default_same_address'] = array(
'#type' => 'checkbox',
'#title' => t('Use the same address for billing and delivery by default.'),
'#default_value' => variable_get('uc_cart_default_same_address', FALSE),
);
$form['checkout']['uc_cart_delivery_not_shippable'] = array(
'#type' => 'checkbox',
'#title' => t('Hide delivery information when carts have no shippable items.'),
'#default_value' => variable_get('uc_cart_delivery_not_shippable', TRUE),
);
$form['checkout']['uc_use_next_buttons'] = array(
'#type' => 'checkbox',
'#title' => t('Use collapsing checkout panes with <em>Next</em> buttons.'),
'#default_value' => variable_get('uc_use_next_buttons', FALSE),
);
$form['checkout']['uc_collapse_current_pane'] = array(
'#type' => 'checkbox',
'#title' => t('Collapse pane after its <em>Next</em> button is clicked.'),
'#default_value' => variable_get('uc_collapse_current_pane', TRUE),
'#states' => array(
'visible' => array(
'input[name="uc_checkout_enabled"]' => array('checked' => TRUE),
'input[name="uc_use_next_buttons"]' => array('checked' => TRUE),
),
),
);
$form['instructions'] = array(
'#type' => 'fieldset',
'#title' => t('Instruction messages'),
'#group' => 'checkout-settings',
);
$form['instructions']['uc_checkout_instructions'] = array(
'#type' => 'textarea',
'#title' => t('Checkout instructions'),
'#description' => t('Provide instructions for customers at the top of the checkout screen.'),
'#default_value' => variable_get('uc_checkout_instructions', ''),
'#rows' => 3,
);
$form['instructions']['uc_checkout_review_instructions'] = array(
'#type' => 'textarea',
'#title' => t('Checkout review instructions'),
'#description' => t('Provide instructions for customers at the top of the checkout review screen.'),
'#default_value' => variable_get('uc_checkout_review_instructions', uc_get_message('review_instructions')),
'#rows' => 3,
);
$form['completion_messages'] = array(
'#type' => 'fieldset',
'#title' => t('Completion messages'),
'#group' => 'checkout-settings',
);
$form['completion_messages']['uc_cart_checkout_complete_page'] = array(
'#type' => 'textfield',
'#title' => t('Alternate checkout completion page'),
'#description' => t('Leave blank to use the default completion page (recommended).'),
'#default_value' => variable_get('uc_cart_checkout_complete_page', ''),
'#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q='),
'#size' => 16,
);
$form['completion_messages']['uc_msg_order_submit'] = array(
'#type' => 'textarea',
'#title' => t('Message header'),
'#description' => t('Header for message displayed after a user checks out.'),
'#default_value' => variable_get('uc_msg_order_submit', uc_get_message('completion_message')),
'#rows' => 3,
);
$form['completion_messages']['uc_msg_order_logged_in'] = array(
'#type' => 'textarea',
'#title' => t('Logged in users'),
'#description' => t('Message displayed upon checkout for a user who is logged in.'),
'#default_value' => variable_get('uc_msg_order_logged_in', uc_get_message('completion_logged_in')),
'#rows' => 3,
);
$form['completion_messages']['uc_msg_order_existing_user'] = array(
'#type' => 'textarea',
'#title' => t('Existing users'),
'#description' => t("Message displayed upon checkout for a user who has an account but wasn't logged in."),
'#default_value' => variable_get('uc_msg_order_existing_user', uc_get_message('completion_existing_user')),
'#rows' => 3,
'#states' => $anon_state,
);
$form['completion_messages']['uc_msg_order_new_user'] = array(
'#type' => 'textarea',
'#title' => t('New users'),
'#description' => t("Message displayed upon checkout for a new user whose account was just created. You may use the special tokens !new_username for the username of a newly created account and !new_password for that account's password."),
'#default_value' => variable_get('uc_msg_order_new_user', uc_get_message('completion_new_user')),
'#rows' => 3,
'#states' => $anon_state,
);
$form['completion_messages']['uc_msg_order_new_user_logged_in'] = array(
'#type' => 'textarea',
'#title' => t('New logged in users'),
'#description' => t('Message displayed upon checkout for a new user whose account was just created and also <em>"Login users when new customer accounts are created at checkout."</em> is set on the <a href="!user_login_setting_ur">checkout settings</a>.', array('!user_login_setting_ur' => 'admin/store/settings/checkout')),
'#default_value' => variable_get('uc_msg_order_new_user_logged_in', uc_get_message('completion_new_user_logged_in')),
'#rows' => 3,
'#states' => $anon_state,
);
$form['completion_messages']['uc_msg_continue_shopping'] = array(
'#type' => 'textarea',
'#title' => t('Continue shopping message'),
'#description' => t('Message displayed upon checkout to direct customers to another part of your site.'),
'#default_value' => variable_get('uc_msg_continue_shopping', uc_get_message('continue_shopping')),
'#rows' => 3,
);
if (module_exists('token')) {
$form['completion_messages']['token_tree'] = array(
'#markup' => theme('token_tree', array('token_types' => array('uc_order', 'site', 'store'))),
);
}
return system_settings_form($form);
}
/**
* Checkout rules configuration.
*/
function uc_cart_checkout_rules() {
$conditions = array(
'event' => 'uc_checkout_complete',
'plugin' => 'reaction rule',
);
$options = array(
'base path' => 'admin/store/settings/checkout/rules',
'show plugin' => FALSE,
);
$content['rules'] = rules_ui()->overviewTable($conditions, $options);
return $content;
}

View File

@@ -0,0 +1,38 @@
/**
* @file
* Utility functions to display settings summaries on vertical tabs.
*/
(function ($) {
Drupal.behaviors.ucCartAdminFieldsetSummaries = {
attach: function (context) {
$('fieldset#edit-lifetime', context).drupalSetSummary(function(context) {
return Drupal.t('Anonymous users') + ': '
+ $('#edit-uc-cart-anon-duration', context).val() + ' '
+ $('#edit-uc-cart-anon-unit', context).val() + '<br />'
+ Drupal.t('Authenticated users') + ': '
+ $('#edit-uc-cart-auth-duration', context).val() + ' '
+ $('#edit-uc-cart-auth-unit', context).val();
});
$('fieldset#edit-checkout', context).drupalSetSummary(function(context) {
if ($('#edit-uc-checkout-enabled').is(':checked')) {
return Drupal.t('Checkout is enabled.');
}
else {
return Drupal.t('Checkout is disabled.');
}
});
$('fieldset#edit-anonymous', context).drupalSetSummary(function(context) {
if ($('#edit-uc-checkout-anonymous').is(':checked')) {
return Drupal.t('Anonymous checkout is enabled.');
}
else {
return Drupal.t('Anonymous checkout is disabled.');
}
});
}
};
})(jQuery);

View File

@@ -0,0 +1,483 @@
<?php
/**
* @file
* Hooks provided by the Cart module.
*/
/**
* @addtogroup hooks
* @{
*/
/**
* Performs extra processing when an item is added to the shopping cart.
*
* Some modules need to be able to hook into the process of adding items to a
* cart. For example, an inventory system may need to check stock levels and
* prevent an out of stock item from being added to a customer's cart. This hook
* lets developers squeeze right in at the end of the process after the product
* information is all loaded and the product is about to be added to the cart.
* In the event that a product should not be added to the cart, you simply have
* to return a failure message described below. This hook may also be used
* simply to perform some routine action when products are added to the cart.
*
* @param $nid
* The node ID of the product.
* @param $qty
* The quantity being added.
* @param $data
* The data array, including attributes and model number adjustments.
*
* @return
* The function can use this data to whatever purpose to see if the item
* can be added to the cart or not. The function should return an array
* containing the result array. (This is due to the nature of Drupal's
* module_invoke_all() function. You must return an array within an array
* or other module data will end up getting ignored.) At this moment,
* there are only three keys:
* - success: TRUE or FALSE for whether the specified quantity of the item
* may be added to the cart or not; defaults to TRUE.
* - message: The fail message to display in the event of a failure; if
* omitted, Ubercart will display a default fail message.
* - silent: Return TRUE to suppress the display of any messages; useful
* when a module simply needs to do some other processing during an add
* to cart or fail silently.
*/
function hook_uc_add_to_cart($nid, $qty, $data) {
if ($qty > 1) {
$result[] = array(
'success' => FALSE,
'message' => t('Sorry, you can only add one of those at a time.'),
);
}
return $result;
}
/**
* Adds extra information to a cart item's "data" array.
*
* This is effectively the submit handler of any alterations to the Add to Cart
* form. It provides a standard way to store the extra information so that it
* can be used by hook_uc_add_to_cart().
*
* @param $form_values
* The values submitted to the Add to Cart form.
*
* @return
* An array of data to be merged into the item added to the cart.
*/
function hook_uc_add_to_cart_data($form_values) {
$node = node_load($form_values['nid']);
return array('module' => 'uc_product', 'shippable' => $node->shippable);
}
/**
* Controls the display of an item in the cart.
*
* Product type modules allow the creation of nodes that can be added to the
* cart. The cart determines how they are displayed through this hook. This is
* especially important for product kits, because it may be displayed as a
* single unit in the cart even though it is represented as several items.
*
* This hook is only called for the module that owns the cart item in
* question, as set in $item->module.
*
* @param $item
* The item in the cart to display.
*
* @return
* A form array containing the following elements:
* - "nid"
* - #type: value
* - #value: The node id of the $item.
* - "module"
* - #type: value
* - #value: The module implementing this hook and the node represented by
* $item.
* - "remove"
* - #type: submit
* - #value: t('Remove'); when clicked, will remove $item from the cart.
* - "description"
* - #type: markup
* - #value: Themed markup (usually an unordered list) displaying extra
* information.
* - "title"
* - #type: markup
* - #value: The displayed title of the $item.
* - "#total"
* - type: float
* - value: Numeric price of $item. Notice the '#' signifying that this is
* not a form element but just a value stored in the form array.
* - "data"
* - #type: hidden
* - #value: The serialized $item->data.
* - "qty"
* - #type: textfield
* - #value: The quantity of $item in the cart. When "Update cart" is
* clicked, the customer's input is saved to the cart.
*/
function hook_uc_cart_display($item) {
$node = node_load($item->nid);
$element = array();
$element['nid'] = array('#type' => 'value', '#value' => $node->nid);
$element['module'] = array('#type' => 'value', '#value' => 'uc_product');
$element['remove'] = array('#type' => 'checkbox');
$element['title'] = array(
'#markup' => node_access('view', $node) ? l($item->title, 'node/' . $node->nid) : check_plain($item->title),
);
$element['#total'] = $item->price * $item->qty;
$element['data'] = array('#type' => 'hidden', '#value' => serialize($item->data));
$element['qty'] = array(
'#type' => 'textfield',
'#default_value' => $item->qty,
'#size' => 5,
'#maxlength' => 6
);
if ($description = uc_product_get_description($item)) {
$element['description'] = array('#markup' => $description);
}
return $element;
}
/**
* Act on a cart item before it is about to be created or updated.
*
* @param $entity
* The cart item entity object.
*/
function hook_uc_cart_item_presave($entity) {
$entity->changed = REQUEST_TIME;
}
/**
* Act on cart item entities when inserted.
*
* @param $entity
* The cart item entity object.
*/
function hook_uc_cart_item_insert($entity) {
drupal_set_message(t('An item was added to your cart'));
}
/**
* Act on cart item entities when updated.
*
* @param $entity
* The cart item entity object.
*/
function hook_uc_cart_item_update($entity) {
drupal_set_message(t('An item was updated in your cart'));
}
/**
* Act on cart item entities when deleted.
*
* @param $entity
* The cart item entity object.
*/
function hook_uc_cart_item_delete($entity) {
drupal_set_message(t('An item was deleted from your cart'));
}
/**
* Registers callbacks for a cart pane.
*
* The default cart view page displays a table of the cart contents and a few
* simple form features to manage the cart contents. For a module to add
* information to this page, it must use hook_uc_cart_pane() to define extra
* panes that may be ordered to appear above or below the default information.
*
* @param $items
* The current contents of the shopping cart.
*
* @return
* The function is expected to return an array of pane arrays, keyed by the
* internal ID of the pane, each with the following members:
* - "title"
* - type: string
* - value: The name of the cart pane displayed to the user. Use t().
* - "enabled"
* - type: boolean
* - value: Whether the pane is enabled by default or not.
* (Defaults to TRUE.)
* - "weight"
* - type: integer
* - value: The weight of the pane to determine its display order.
* (Defaults to 0.)
* - "body"
* - type: array
* - value: The body of the pane to be rendered on the cart view screen.
*
* The body gets printed to the screen if it is on the cart view page. For the
* settings page, the body field is ignored. You may want your function to
* check for a NULL argument before processing any queries or foreach() loops.
*/
function hook_uc_cart_pane($items) {
$body = array();
if (!is_null($items)) {
$body = drupal_get_form('uc_cart_view_form', $items) + array(
'#prefix' => '<div id="cart-form-pane">',
'#suffix' => '</div>',
);
}
$panes['cart_form'] = array(
'title' => t('Default cart form'),
'enabled' => TRUE,
'weight' => 0,
'body' => $body,
);
return $panes;
}
/**
* Alters cart pane definitions.
*
* @param $panes
* The array of pane information in the format defined in hook_uc_cart_pane(),
* passed by reference.
* @param $items
* The array of item information.
*/
function hook_uc_cart_pane_alter(&$panes, $items) {
$panes['cart_form']['body'] = drupal_get_form('my_custom_pane_form_builder', $items);
}
/**
* Takes action when checkout is completed.
*
* @param $order
* The resulting order object from the completed checkout.
* @param $account
* The customer that completed checkout, either the current user, or the
* account created for an anonymous customer.
*/
function hook_uc_checkout_complete($order, $account) {
// Get previous records of customer purchases.
$nids = array();
$result = db_query("SELECT uid, nid, qty FROM {uc_customer_purchases} WHERE uid = :uid", array(':uid' => $account->uid));
foreach ($result as $record) {
$nids[$record->nid] = $record->qty;
}
// Update records with new data.
$record = array('uid' => $account->uid);
foreach ($order->products as $product) {
$record['nid'] = $product->nid;
if (isset($nids[$product->nid])) {
$record['qty'] = $nids[$product->nid] + $product->qty;
db_write_record($record, 'uc_customer_purchases', array('uid', 'nid'));
}
else {
$record['qty'] = $product->qty;
db_write_record($record, 'uc_customer_purchases');
}
}
}
/**
* Takes action immediately before bringing up the checkout page.
*
* Use drupal_goto() in the hook implementation to abort checkout and
* enforce restrictions on the order.
*
* @param $order
* The order object to check out.
*/
function hook_uc_cart_checkout_start($order) {
$account = user_load($order->uid);
if (is_array($account->roles) && in_array('administrator', $account->roles)) {
drupal_set_message(t('Administrators may not purchase products.', 'error'));
drupal_goto('cart');
}
}
/**
* Registers callbacks for a checkout pane.
*
* The checkout screen for Ubercart is a compilation of enabled checkout panes.
* A checkout pane can be used to display order information, collect data from
* the customer, or interact with other panes. Panes are defined in enabled
* modules with hook_uc_checkout_pane() and displayed and processed through
* specified callback functions. Some of the settings for each pane are
* configurable from the checkout settings page with defaults being specified
* in the hooks.
*
* The default panes are defined in uc_cart.module in the function
* uc_cart_checkout_pane(). These include panes to display the contents of the
* shopping cart and to collect essential site user information, a shipping
* address, a payment address, and order comments. Other included modules offer
* panes for shipping and payment purposes as well.
*
* @return
* An array of checkout pane arrays, keyed by the internal ID of the pane,
* each with the following members:
* - title:
* - type: string
* - value: The name of the pane as it appears on the checkout form.
* - desc:
* - type: string
* - value: A short description of the pane for the admin pages.
* - callback:
* - type: string
* - value: The name of the callback function for this pane.
* - weight:
* - type: integer
* - value: Default weight of the pane, defining its order on the checkout
* form.
* - enabled:
* - type: boolean
* - value: Optional. Whether or not the pane is enabled by default.
* Defaults to TRUE.
* - process:
* - type: boolean
* - value: Optional. Whether or not this pane needs to be processed when
* the checkout form is submitted. Defaults to TRUE.
* - collapsible:
* - type: boolean
* - value: Optional. Whether or not this pane is displayed as a collapsible
* fieldset. Defaults to TRUE.
* - shippable:
* - type: boolean
* - value: Optional. If TRUE, the pane is only shown if the cart is
* shippable. Defaults to NULL.
*
* @see http://www.ubercart.org/docs/developer/245/checkout
*/
function hook_uc_checkout_pane() {
$panes['cart'] = array(
'callback' => 'uc_checkout_pane_cart',
'title' => t('Cart contents'),
'desc' => t("Display the contents of a customer's shopping cart."),
'weight' => 1,
'process' => FALSE,
'collapsible' => FALSE,
);
return $panes;
}
/**
* Builds and proceses a pane defined by hook_uc_checkout_pane().
*
* @param $op
* The operation the pane is performing. Possible values are "view",
* "process", "review", and "settings".
* @param $order
* The order being viewed or edited.
* @param $form
* The order's edit form. NULL for non-edit ops.
* @param &$form_state
* The form state array of the edit form. NULL for non-edit ops.
*
* @return
* Varies according to the value of $op:
* - view: An array with two keys, "contents" and an optional "description".
* "contents" is a form array to collect the checkout data for the pane. The
* description provides help text for the pane as a whole.
* - process: A boolean indicating that checkout should continue. During this
* op, $order should be modified with the values in
* $form_state['values']['panes'][PANE_ID].
* - review: An array containing review sections. A review section contains
* "title" and "data" keys which have HTML to be displayed on the checkout
* review page.
* - settings: A settings form which can be used with system_settings_form().
*/
function uc_checkout_pane_callback($op, $order, $form = NULL, &$form_state = NULL) {
// uc_checkout_pane_comments()
switch ($op) {
case 'view':
$description = t('Use this area for special instructions or questions regarding your order.');
if (!empty($order->order_id)) {
$default = db_query("SELECT message FROM {uc_order_comments} WHERE order_id = :id", array(':id' => $order->order_id))->fetchField();
}
else {
$default = NULL;
}
$contents['comments'] = array(
'#type' => 'textarea',
'#title' => t('Order comments'),
'#default_value' => $default,
);
return array('description' => $description, 'contents' => $contents);
case 'process':
if ($form_state['values']['panes']['comments']['comments']) {
db_delete('uc_order_comments')
->condition('order_id', $order->order_id)
->execute();
uc_order_comment_save($order->order_id, 0, $form_state['values']['panes']['comments']['comments'], 'order', uc_order_state_default('post_checkout'), TRUE);
}
return TRUE;
case 'review':
$review = NULL;
$result = db_query("SELECT message FROM {uc_order_comments} WHERE order_id = :id", array(':id' => $order->order_id));
if ($comment = $result->fetchObject()) {
$review[] = array('title' => t('Comment'), 'data' => check_plain($comment->message));
}
return $review;
}
}
/**
* Alters checkout pane definitions.
*
* @param $panes
* Array with the panes information as defined in hook_uc_checkout_pane(),
* passed by reference.
*/
function hook_uc_checkout_pane_alter(&$panes) {
$panes['cart']['callback'] = 'my_custom_module_callback';
}
/**
* Handles requests to update a cart item.
*
* @param $nid
* Node id of the cart item.
* @param $data
* Array of extra information about the item.
* @param $qty
* The quantity of this item in the cart.
* @param $cid
* The cart id. Defaults to NULL, which indicates that the current user's cart
* should be retrieved with uc_cart_get_id().
*/
function hook_uc_update_cart_item($nid, $data = array(), $qty, $cid = NULL) {
if (!$nid) return NULL;
$cid = !(is_null($cid) || empty($cid)) ? $cid : uc_cart_get_id();
if ($qty < 1) {
uc_cart_remove_item($nid, $cid, $data);
}
else {
db_update('uc_cart_products')
->fields(array(
'qty' => $qty,
'changed' => REQUEST_TIME,
))
->condition('nid', $nid)
->condition('cart_id', $cid)
->condition('data', serialize($data))
->execute();
}
// Rebuild the items hash
uc_cart_get_contents(NULL, 'rebuild');
if (!strpos(request_uri(), 'cart', -4)) {
drupal_set_message(t('Your item(s) have been updated.'));
}
}
/**
* @} End of "addtogroup hooks".
*/

View File

@@ -0,0 +1,55 @@
<?php
/**
* @file
*
* Contains the controller for uc_cart_item entities.
*/
class UcCartItemController extends EntityAPIController {
/**
* Overrides EntityAPIController::attachLoad().
*/
public function attachLoad(&$items, $revision_id = FALSE) {
foreach ($items as &$item) {
$product = uc_product_load_variant($item->nid, $item->data);
// Merge in fields from the product.
foreach ($product as $key => $value) {
$item->$key = $value;
}
$item->module = $item->data['module'];
}
parent::attachLoad($items, $revision_id);
}
/**
* Saves a cart item entity.
*
* Cart items are deleted if saved with a quantity of zero.
*/
public function save($item, DatabaseTransaction $transaction = NULL) {
if ($item->qty < 1) {
if (isset($item->cart_item_id)) {
parent::delete(array($item->cart_item_id), $transaction);
}
}
else {
$item->changed = REQUEST_TIME;
parent::save($item, $transaction);
}
}
/**
* Overrides EntityAPIController::buildContent().
*/
public function buildContent($product, $view_mode = 'full', $langcode = NULL, $content = array()) {
$content += module_invoke($product->data['module'], 'uc_cart_display', $product);
if (!empty($content)) {
$content['cart_item_id'] = array(
'#type' => 'hidden',
'#value' => isset($product->cart_item_id) ? $product->cart_item_id : 0,
);
}
return parent::buildContent($product, $view_mode, $langcode, $content);
}
}

View File

@@ -0,0 +1,174 @@
/**
* @file
* Styles for uc_cart module.
*/
.order-review-table {
border: solid 1px #999;
font-size: .9em;
line-height: 1.4em;
margin: auto;
width: auto;
}
.order-review-table td {
padding-bottom: 0.2em;
padding-top: 0.1em;
vertical-align: top;
}
.order-review-table .pane-title-row {
background-color: #ddd;
border: solid 1px #999;
font-weight: bold;
padding: .5em 1em;
text-align: center;
}
.order-review-table .title-col {
font-weight: bold;
padding-left: 3em;
text-align: right;
white-space: nowrap;
}
.order-review-table .data-col {
padding-right: 3em;
}
.order-review-table .row-border-top {
border-top: solid 1px #999;
}
.order-review-table .row-border-bottom {
border-bottom: solid 1px #999;
}
.order-review-table .review-button-row {
background-color: #ddd;
border: solid 1px #999;
}
.order-review-table .review-button-row td {
padding-top: 1em;
text-align: right;
}
.order-review-table .review-button-row div,
.order-review-table .review-button-row form {
display: inline;
}
/* I cannot testify for any of the data below.. it's a hodge podge. */
.next-button {
margin-top: 1em;
text-align: right;
}
#uc-cart-view-form table {
width: 100%;
}
#uc-cart-view-form td {
vertical-align: top;
}
#uc-cart-view-form img {
padding-right: .8em;
float: left;
}
.address-book-icon {
position: relative;
margin-left: 2px;
top: 2px;
}
/**
* CSS rules for the cart form at /cart.
*/
#uc-cart-view-form th {
white-space: nowrap;
}
#uc-cart-view-form td.desc {
width: 100%;
}
#uc-cart-view-form td.total,
#uc-cart-view-form td.subtotal {
white-space: nowrap;
}
#uc-cart-view-form .form-actions {
margin-top: 0;
text-align: right;
}
#uc-cart-view-form .form-actions a {
float: left;
margin: .5em;
}
#uc-cart-view-form #edit-continue-shopping {
float: left;
margin-left: .5em;
}
#uc-cart-view-form .form-actions input {
vertical-align: middle;
}
.uc-cart-checkout-button {
float: right;
clear: right;
}
.uc-cart-checkout-button-separator {
text-align: center;
}
/**
* CSS rules for the cart review table.
*/
td.qty {
text-align: center;
white-space: nowrap;
}
td.price {
text-align: right;
white-space: nowrap;
}
td.products {
width: 100%;
}
td.subtotal {
text-align: right;
}
#subtotal-title {
font-weight: bold;
}
/**
* CSS rules for the default checkout panes.
*/
.uc-cart-checkout-form .uc-store-address-field .form-item label {
padding: 5px 6px 6px;
}
.uc-cart-checkout-form .form-item {
margin-bottom: 2px;
margin-top: 2px;
}
/**
* CSS rules for the bottom of the checkout form at /cart/checkout.
*/
.uc-cart-checkout-form .form-actions {
text-align: right;
}

View File

@@ -0,0 +1,24 @@
name = Cart
description = REQUIRED. Controls the shopping cart and checkout for an Ubercart e-commerce site.
dependencies[] = uc_order
dependencies[] = uc_product
package = Ubercart - core
core = 7.x
; Test cases
files[] = tests/uc_cart.test
; Views handlers
files[] = views/uc_cart_handler_field_cart_user.inc
; Classes
files[] = uc_cart.controller.inc
configure = admin/store/settings/cart
; Information added by Drupal.org packaging script on 2013-12-17
version = "7.x-3.6"
core = "7.x"
project = "ubercart"
datestamp = "1387304010"

View File

@@ -0,0 +1,57 @@
<?php
/**
* @file
* Entity metadata hooks for uc_cart.module.
*/
/**
* Implements hook_entity_property_info().
*/
function uc_cart_entity_property_info() {
$info = array();
$properties = &$info['uc_cart_item']['properties'];
$properties['cart_item_id'] = array(
'type' => 'integer',
'label' => t('Cart item ID'),
'description' => t('The unique ID of the cart item.'),
'validation callback' => 'entity_metadata_validate_integer_positive',
'schema field' => 'cart_item_id',
);
$properties['cart_id'] = array(
'type' => 'text',
'label' => t('Cart ID'),
'description' => t('The user ID or anonymous hash ID of the cart.'),
'schema field' => 'cart_id',
);
$properties['nid'] = array(
'type' => 'integer',
'label' => t('Node ID'),
'description' => t('The node ID of the cart item.'),
'validation callback' => 'entity_metadata_validate_integer_positive',
'schema field' => 'nid',
);
$properties['qty'] = array(
'type' => 'integer',
'label' => t('Quantity'),
'description' => t('The number of this product in the cart.'),
'setter callback' => 'entity_property_verbatim_set',
'schema field' => 'qty',
);
$properties['changed'] = array(
'type' => 'date',
'label' => t('Changed'),
'description' => t('The time that the product was last changed.'),
'schema field' => 'changed',
);
$properties['node'] = array(
'type' => 'node',
'label' => t('Node'),
'description' => t('The node representing the product.'),
'getter callback' => 'uc_order_product_node_property_get',
);
return $info;
}

View File

@@ -0,0 +1,141 @@
<?php
/**
* @file
* Install, update and uninstall functions for the uc_cart module.
*/
/**
* Implements hook_schema().
*/
function uc_cart_schema() {
$schema = array();
$schema['uc_cart_products'] = array(
'description' => 'Stores products placed in shopping carts.',
'fields' => array(
'cart_item_id' => array(
'description' => 'Unique identifier for cart item.',
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE
),
'cart_id' => array(
'description' => 'A user-specific cart ID. For authenticated users, their {users}.uid. For anonymous users, a token.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '0',
),
'nid' => array(
'description' => 'The {node}.nid of the product.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'qty' => array(
'description' => 'The number of this product in the cart.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'changed' => array(
'description' => 'The Unix timestamp indicating the time the product in the cart was changed.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'data' => array(
'description' => 'A serialized array of extra cart data for the product.',
'type' => 'text',
'serialize' => TRUE,
),
),
'indexes' => array(
'cart_id' => array('cart_id'),
),
'primary key' => array('cart_item_id'),
'foreign keys' => array(
'node' => array(
'table' => 'node',
'columns' => array('nid' => 'nid'),
),
),
);
return $schema;
}
/**
* Implements hook_uninstall().
*/
function uc_cart_uninstall() {
db_delete('variable')
->condition('name', 'uc_cart_%', 'LIKE')
->condition('name', 'uc_pane_%', 'LIKE')
->condition('name', 'uc_cap_%', 'LIKE')
->condition('name', 'uc_checkout_%', 'LIKE')
->condition('name', 'uc_msg_%', 'LIKE')
->condition('name', 'uc_new_customer_%', 'LIKE')
->execute();
variable_del('uc_minimum_subtotal');
variable_del('uc_add_item_redirect');
variable_del('uc_continue_shopping_url');
variable_del('uc_continue_shopping_type');
variable_del('uc_use_next_buttons');
variable_del('uc_collapse_current_pane');
variable_del('uc_checkout_email_customer');
variable_del('uc_checkout_email_admin');
}
/**
* Implements hook_update_last_removed().
*/
function uc_cart_update_last_removed() {
// 7.x-3.0-beta2 and earlier were installed with schema version 0,
// which causes update.php to fail.
return drupal_get_installed_schema_version('uc_cart') == 0 ? 0 : 6201;
}
/**
* Unify cart block title setting.
*/
function uc_cart_update_6202() {
variable_del('uc_cart_block_title');
}
/**
* Remove unused text format variables.
*/
function uc_cart_update_7000() {
variable_del('uc_checkout_instructions_format');
variable_del('uc_checkout_review_instructions_format');
variable_del('uc_msg_order_submit_format');
variable_del('uc_msg_order_logged_in_format');
variable_del('uc_msg_order_existing_user_format');
variable_del('uc_msg_order_new_user_format');
variable_del('uc_msg_continue_shopping_format');
}
/**
* Increase maximum cart item quantity.
*/
function uc_cart_update_7001() {
db_change_field('uc_cart_products', 'qty', 'qty', array(
'description' => 'The number of this product in the cart.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
));
}
/**
* Remove unused variable.
*/
function uc_cart_update_7300() {
variable_del('uc_continue_shopping_text');
}

View File

@@ -0,0 +1,35 @@
/**
* @file
* Adds effects and behaviors to elements on the checkout page.
*/
Drupal.behaviors.ucCart = {
attach: function(context, settings) {
// Add a throbber to the submit order button on the review order form.
jQuery('form#uc-cart-checkout-review-form input#edit-submit:not(.ucSubmitOrderThrobber-processed)', context).addClass('ucSubmitOrderThrobber-processed').click(function() {
jQuery(this).clone().insertAfter(this).attr('disabled', true).after('<span class="ubercart-throbber">&nbsp;&nbsp;&nbsp;&nbsp;</span>').end().hide();
jQuery('#uc-cart-checkout-review-form #edit-back').attr('disabled', true);
});
}
}
/**
* Behaviors for the Next buttons.
*
* When a customer clicks a Next button, expand the next pane, remove the
* button, and don't let it collapse again.
*/
function uc_cart_next_button_click(button, pane_id, current) {
if (current !== 'false') {
jQuery('#' + current + '-pane legend a').click();
}
else {
button.disabled = true;
}
if (jQuery('#' + pane_id + '-pane').attr('class').indexOf('collapsed') > -1 && jQuery('#' + pane_id + '-pane').html() !== null) {
jQuery('#' + pane_id + '-pane legend a').click();
}
return false;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,620 @@
<?php
/**
* @file
* Cart menu items.
*/
/**
* Displays the cart view page.
*
* Show the products in the cart with a form to adjust cart contents or go to
* checkout.
*/
function uc_cart_view() {
// Failsafe so that this function only works when called with no arguments.
// This prevents the accidental wiping of the cart_order session variable.
if (func_num_args() > 0) {
return MENU_NOT_FOUND;
}
// Load the array of shopping cart items.
$items = uc_cart_get_contents();
// Display the empty cart page if there are no items in the cart.
if (empty($items)) {
return array(
'#theme' => 'uc_empty_cart',
);
}
$build = array();
// Load through the cart panes...
foreach (uc_cart_cart_pane_list($items) as $id => $pane) {
// If the pane is enabled...
if ($pane['enabled']) {
// Add its output to the cart view.
$build[$id] = $pane['body'];
}
}
// Add a custom cart breadcrumb if specified.
if (($text = variable_get('uc_cart_breadcrumb_text', '')) !== '') {
$link = l($text, variable_get('uc_cart_breadcrumb_url', '<front>'));
drupal_set_breadcrumb(array($link));
}
return $build;
}
/**
* Confirm that the customer wants to empty their cart.
*/
function uc_cart_empty_confirm($form, &$form_state) {
return confirm_form($form, t('Are you sure you want to empty your shopping cart?'), 'cart');
}
/**
* Submission handler to empty the cart after confirmations.
*/
function uc_cart_empty_confirm_submit($form, &$form_state) {
uc_cart_empty();
$form_state['redirect'] = 'cart';
}
/**
* Displays the cart checkout page built of checkout panes from enabled modules.
*/
function uc_cart_checkout() {
global $user;
$items = uc_cart_get_contents();
if (count($items) == 0 || !variable_get('uc_checkout_enabled', TRUE)) {
drupal_goto('cart');
}
if (($min = variable_get('uc_minimum_subtotal', 0)) > 0) {
$subtotal = 0;
if (is_array($items) && count($items) > 0) {
foreach ($items as $item) {
$data = module_invoke($item->module, 'uc_cart_display', $item);
if (!empty($data)) {
$subtotal += $data['#total'];
}
}
}
if ($subtotal < $min) {
drupal_set_message(t('The minimum order subtotal for checkout is @min.', array('@min' => uc_currency_format($min))), 'error');
drupal_goto('cart');
}
}
// Send anonymous users to login page when anonymous checkout is disabled.
if (!$user->uid && !variable_get('uc_checkout_anonymous', TRUE)) {
drupal_set_message(t('You must login before you can proceed to checkout.'));
if (variable_get('user_register', 1) != 0) {
drupal_set_message(t('If you do not have an account yet, you should <a href="!url">register now</a>.', array('!url' => url('user/register', array('query' => drupal_get_destination())))));
}
drupal_goto('user', array('query' => drupal_get_destination()));
}
// Load an order from the session, if available.
if (isset($_SESSION['cart_order'])) {
$order = uc_order_load($_SESSION['cart_order']);
if ($order) {
// Don't use an existing order if it has changed status or owner, or if
// there has been no activity for 10 minutes (to prevent identity theft).
if (uc_order_status_data($order->order_status, 'state') != 'in_checkout' ||
($user->uid > 0 && $user->uid != $order->uid) ||
$order->modified < REQUEST_TIME - UC_CART_CHECKOUT_TIMEOUT) {
if (uc_order_status_data($order->order_status, 'state') == 'in_checkout' && $order->modified < REQUEST_TIME - UC_CART_CHECKOUT_TIMEOUT) {
// Mark expired orders as abandoned.
uc_order_update_status($order->order_id, 'abandoned');
}
unset($order);
}
}
else {
// Ghost session.
unset($_SESSION['cart_order']);
drupal_set_message(t('Your session has expired or is no longer valid. Please review your order and try again.'));
drupal_goto('cart');
}
}
// Determine whether the form is being submitted or built for the first time.
if (isset($_POST['form_id']) && $_POST['form_id'] == 'uc_cart_checkout_form') {
// If this is a form submission, make sure the cart order is still valid.
if (!isset($order)) {
drupal_set_message(t('Your session has expired or is no longer valid. Please review your order and try again.'));
drupal_goto('cart');
}
elseif (!empty($_SESSION['uc_cart_order_rebuild'])) {
drupal_set_message(t('Your shopping cart contents have changed. Please review your order and try again.'));
drupal_goto('cart');
}
}
else {
// Prepare the cart order.
$rebuild = FALSE;
if (!isset($order)) {
// Create a new order if necessary.
$order = uc_order_new($user->uid);
$_SESSION['cart_order'] = $order->order_id;
$rebuild = TRUE;
}
elseif (!empty($_SESSION['uc_cart_order_rebuild'])) {
// Or, if the cart has changed, then remove old products and line items.
$efq = new EntityFieldQuery();
$result = $efq->entityCondition('entity_type', 'uc_order_product')
->propertyCondition('order_id', $order->order_id)
->execute();
if (!empty($result['uc_order_product'])) {
$product_ids = array_keys($result['uc_order_product']);
uc_order_product_delete_multiple($product_ids);
}
uc_order_delete_line_item($order->order_id, TRUE);
$rebuild = TRUE;
}
if ($rebuild) {
// Copy the cart contents to the cart order.
$order->products = uc_cart_get_contents();
unset($_SESSION['uc_cart_order_rebuild']);
}
elseif (!uc_order_product_revive($order->products)) {
drupal_set_message(t('Some of the products in this order are no longer available.'), 'error');
drupal_goto('cart');
}
}
// Trigger the "Customer starts checkout" hook and event.
module_invoke_all('uc_cart_checkout_start', $order);
rules_invoke_event('uc_cart_checkout_start', $order);
return drupal_get_form('uc_cart_checkout_form', $order);
}
/**
* The checkout form built up from the enabled checkout panes.
*
* @param $order
* The order that is being checked out.
*
* @see uc_cart_checkout_form_process()
* @see uc_cart_checkout_form_validate()
* @see uc_cart_checkout_form_submit()
* @see uc_cart_checkout_review()
* @see theme_uc_cart_checkout_form()
* @ingroup forms
*/
function uc_cart_checkout_form($form, &$form_state, $order) {
if ($processed = isset($form_state['storage']['order'])) {
$order = $form_state['storage']['order'];
}
else {
$form_state['storage']['order'] = $order;
$form_state['storage']['base_path'] = implode('/', array_slice(arg(), 0, -1));
}
$form['#attributes']['class'][] = 'uc-cart-checkout-form';
$form['#attached']['js'][] = drupal_get_path('module', 'uc_cart') . '/uc_cart.js';
$form['#attached']['css'][] = drupal_get_path('module', 'uc_cart') . '/uc_cart.css';
if ($instructions = variable_get('uc_checkout_instructions', '')) {
$form['instructions'] = array(
'#prefix' => '<div id="checkout-instructions">',
'#markup' => filter_xss_admin($instructions),
'#suffix' => '</div>',
);
}
$form['panes'] = array('#tree' => TRUE);
$panes = _uc_checkout_pane_list();
// If the order isn't shippable, remove panes with shippable == TRUE.
if (!uc_order_is_shippable($order) && variable_get('uc_cart_delivery_not_shippable', TRUE)) {
$panes = uc_cart_filter_checkout_panes($panes, array('shippable' => TRUE));
}
// Invoke the 'prepare' op of enabled panes, but only if their 'process' ops
// have not been invoked on this request (i.e. when rebuilding after AJAX).
foreach ($panes as $id => $pane) {
if ($pane['enabled'] && empty($form_state['storage']['panes'][$id]['prepared']) && isset($pane['callback']) && function_exists($pane['callback'])) {
$pane['callback']('prepare', $order, $form, $form_state);
$form_state['storage']['panes'][$id]['prepared'] = TRUE;
$processed = FALSE; // Make sure we save the updated order.
}
}
// Load the line items and save the order. We do this after the 'prepare'
// callbacks of enabled panes have been invoked, because these may have
// altered the order.
if (!$processed) {
$order->line_items = uc_order_load_line_items($order);
uc_order_save($order);
}
foreach ($panes as $id => $pane) {
if ($pane['enabled']) {
$pane['prev'] = _uc_cart_checkout_prev_pane($panes, $id);
$pane['next'] = _uc_cart_checkout_next_pane($panes, $id);
if (!isset($pane['collapsed'])) {
$collapsed = ($pane['prev'] === FALSE || empty($displayed[$pane['prev']])) ? FALSE : TRUE;
}
if (isset($form_state['expanded_panes']) && in_array($id, $form_state['expanded_panes'])) {
$collapsed = FALSE;
}
$return = $pane['callback']('view', $order, $form, $form_state);
// Add the pane if any display data is returned from the callback.
if (is_array($return) && (!empty($return['description']) || !empty($return['contents']))) {
// Create the fieldset for the pane.
$form['panes'][$id] = array(
'#type' => 'fieldset',
'#title' => check_plain($pane['title']),
'#description' => !empty($return['description']) ? $return['description'] : '',
'#collapsible' => $pane['collapsible'] && variable_get('uc_use_next_buttons', FALSE),
'#collapsed' => variable_get('uc_use_next_buttons', FALSE) ? $collapsed : FALSE,
'#id' => $id . '-pane',
'#theme' => isset($return['theme']) ? $return['theme'] : NULL,
);
// Add the contents of the fieldset if any were returned.
if (!empty($return['contents'])) {
$form['panes'][$id] = array_merge($form['panes'][$id], $return['contents']);
}
// Add the 'Next' button if necessary.
if ((!isset($return['next-button']) || $return['next-button'] !== FALSE) && $pane['next'] !== FALSE &&
variable_get('uc_use_next_buttons', FALSE) != FALSE) {
$opt = variable_get('uc_collapse_current_pane', FALSE) ? $id : 'false';
$form['panes'][$id]['next'] = array(
'#type' => 'button',
'#value' => t('Next'),
'#weight' => 20,
'#attributes' => array('onclick' => "return uc_cart_next_button_click(this, '" . $pane['next'] . "', '" . $opt . "');"),
'#prefix' => '<div class="next-button">',
'#suffix' => '</div>',
);
}
// Log that this pane was actually displayed.
$displayed[$id] = TRUE;
}
}
}
unset($form_state['expanded_panes']);
$form['actions'] = array('#type' => 'actions');
$form['actions']['cancel'] = array(
'#type' => 'submit',
'#value' => t('Cancel'),
'#validate' => array(), // Disable validation to prevent a new order
// from being created.
'#limit_validation_errors' => array(),
'#submit' => array('uc_cart_checkout_form_cancel'),
);
$form['actions']['continue'] = array(
'#type' => 'submit',
'#value' => t('Review order'),
);
form_load_include($form_state, 'inc', 'uc_store', 'includes/uc_ajax_attach');
$form['#process'][] = 'uc_ajax_process_form';
unset($_SESSION['uc_checkout'][$order->order_id]);
return $form;
}
/**
* Default theme function for the checkout form.
*
* @param $variables
* An associative array containing:
* - form: A render element representing the form.
*
* @see uc_cart_checkout_form()
* @ingroup themeable
*/
function theme_uc_cart_checkout_form($variables) {
return drupal_render_children($variables['form']);
}
/**
* Form validation for uc_cart_checkout_form().
*
* @see uc_cart_checkout_form()
* @see uc_cart_checkout_form_submit()
*/
function uc_cart_checkout_form_validate($form, &$form_state) {
$order = $form_state['storage']['order'];
// Update the order "modified" time to prevent timeout on ajax requests.
$order->modified = REQUEST_TIME;
// Validate/process the cart panes. A FALSE value results in failed checkout.
$form_state['checkout_valid'] = TRUE;
foreach (element_children($form_state['values']['panes']) as $pane_id) {
$func = _uc_checkout_pane_data($pane_id, 'callback');
if (is_string($func) && function_exists($func)) {
$isvalid = $func('process', $order, $form, $form_state);
if ($isvalid === FALSE) {
$form_state['expanded_panes'][] = $pane_id;
$form_state['checkout_valid'] = FALSE;
}
}
}
// Reload line items and save order.
$order->line_items = uc_order_load_line_items($order);
uc_order_save($order);
}
/**
* Form submission handler for uc_cart_checkout_form().
*
* @see uc_cart_checkout_form()
* @see uc_cart_checkout_form_validate()
*/
function uc_cart_checkout_form_submit($form, &$form_state) {
if ($form_state['checkout_valid'] === FALSE) {
$url = $form_state['storage']['base_path'] . '/checkout';
}
else {
$url = $form_state['storage']['base_path'] . '/checkout/review';
$_SESSION['uc_checkout'][$form_state['storage']['order']->order_id]['do_review'] = TRUE;
}
unset($form_state['checkout_valid']);
$form_state['redirect'] = $url;
}
/**
* Submit handler for "Cancel" button on uc_cart_checkout_form().
*
* @see uc_cart_checkout_form()
*/
function uc_cart_checkout_form_cancel($form, &$form_state) {
$order = $form_state['storage']['order'];
if (isset($_SESSION['cart_order']) && $_SESSION['cart_order'] == $order->order_id) {
uc_order_comment_save($_SESSION['cart_order'], 0, t('Customer canceled this order from the checkout form.'));
unset($_SESSION['cart_order']);
}
unset($_SESSION['uc_checkout'][$order->order_id]);
$form_state['redirect'] = $form_state['storage']['base_path'];
}
/**
* Allows a customer to review their order before finally submitting it.
*
* @see uc_cart_checkout_form()
*/
function uc_cart_checkout_review() {
drupal_add_js(drupal_get_path('module', 'uc_cart') . '/uc_cart.js');
if (empty($_SESSION['cart_order']) || empty($_SESSION['uc_checkout'][$_SESSION['cart_order']]['do_review'])) {
drupal_goto('cart/checkout');
}
$order = uc_order_load($_SESSION['cart_order']);
if ($order === FALSE || uc_order_status_data($order->order_status, 'state') != 'in_checkout') {
unset($_SESSION['uc_checkout'][$order->order_id]['do_review']);
drupal_goto('cart/checkout');
}
elseif (!uc_order_product_revive($order->products)) {
drupal_set_message(t('Some of the products in this order are no longer available.'), 'error');
drupal_goto('cart');
}
$panes = _uc_checkout_pane_list();
// If the cart isn't shippable, bypass panes with shippable == TRUE.
if (!uc_order_is_shippable($order) && variable_get('uc_cart_delivery_not_shippable', TRUE)) {
$panes = uc_cart_filter_checkout_panes($panes, array('shippable' => TRUE));
}
foreach ($panes as $pane) {
if ($pane['enabled']) {
$func = $pane['callback'];
if (function_exists($func)) {
$return = $func('review', $order, NULL);
if (!is_null($return)) {
$data[$pane['title']] = $return;
}
}
}
}
$build = array(
'#theme' => 'uc_cart_checkout_review',
'#panes' => $data,
'#form' => drupal_get_form('uc_cart_checkout_review_form', $order),
);
return $build;
}
/**
* Themes the checkout review order page.
*
* @param $variables
* An associative array containing:
* - form: A render element representing the form, that by default includes
* the 'Back' and 'Submit order' buttons at the bottom of the review page.
* - panes: An associative array for each checkout pane that has information
* to add to the review page, keyed by the pane title:
* - <pane title>: The data returned for that pane or an array of returned
* data.
*
* @return
* A string of HTML for the page contents.
*
* @ingroup themeable
*/
function theme_uc_cart_checkout_review($variables) {
$panes = $variables['panes'];
$form = $variables['form'];
drupal_add_css(drupal_get_path('module', 'uc_cart') . '/uc_cart.css');
$output = '<div id="review-instructions">' . filter_xss_admin(variable_get('uc_checkout_review_instructions', uc_get_message('review_instructions'))) . '</div>';
$output .= '<table class="order-review-table">';
foreach ($panes as $title => $data) {
$output .= '<tr class="pane-title-row">';
$output .= '<td colspan="2">' . $title . '</td>';
$output .= '</tr>';
if (is_array($data)) {
foreach ($data as $row) {
if (is_array($row)) {
if (isset($row['border'])) {
$border = ' class="row-border-' . $row['border'] . '"';
}
else {
$border = '';
}
$output .= '<tr' . $border . '>';
$output .= '<td class="title-col">' . $row['title'] . ':</td>';
$output .= '<td class="data-col">' . $row['data'] . '</td>';
$output .= '</tr>';
}
else {
$output .= '<tr><td colspan="2">' . $row . '</td></tr>';
}
}
}
else {
$output .= '<tr><td colspan="2">' . $data . '</td></tr>';
}
}
$output .= '<tr class="review-button-row">';
$output .= '<td colspan="2">' . drupal_render($form) . '</td>';
$output .= '</tr>';
$output .= '</table>';
return $output;
}
/**
* Gives customers the option to finish checkout or go revise their information.
*
* @see uc_cart_checkout_review_form_back()
* @see uc_cart_checkout_review_form_submit()
* @ingroup forms
*/
function uc_cart_checkout_review_form($form, &$form_state, $order) {
if (!isset($form_state['uc_order'])) {
$form_state['uc_order'] = $order;
$form_state['storage']['base_path'] = implode('/', array_slice(arg(), 0, -2));
}
$form['actions'] = array('#type' => 'actions');
$form['actions']['back'] = array(
'#type' => 'submit',
'#value' => t('Back'),
'#submit' => array('uc_cart_checkout_review_form_back'),
);
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit order'),
);
return $form;
}
/**
* Returns the customer to the checkout page to edit their information.
*
* @see uc_cart_checkout_review_form()
*/
function uc_cart_checkout_review_form_back($form, &$form_state) {
$form_state['redirect'] = $form_state['storage']['base_path'] . '/checkout';
}
/**
* Final checks to make sure the order can be completed.
*
* @see uc_cart_checkout_review_form()
*/
function uc_cart_checkout_review_form_submit($form, &$form_state) {
// Invoke hook_uc_order($op = 'submit') to test to make sure the order can
// be completed... used for auto payment in uc_credit.module.
$order = $form_state['uc_order'];
$error = FALSE;
// Invoke it on a per-module basis instead of all at once.
foreach (module_implements('uc_order') as $module) {
$function = $module . '_uc_order';
if (function_exists($function)) {
// $order must be passed by reference.
$result = $function('submit', $order, NULL);
$msg_type = 'status';
if ($result[0]['pass'] === FALSE) {
$error = TRUE;
$msg_type = 'error';
}
if (!empty($result[0]['message'])) {
drupal_set_message($result[0]['message'], $msg_type);
}
// Stop invoking the hooks if there was an error.
if ($error) {
break;
}
}
}
if ($error) {
$form_state['redirect'] = $form_state['storage']['base_path'] . '/checkout/review';
}
else {
unset($_SESSION['uc_checkout'][$order->order_id]['do_review']);
$_SESSION['uc_checkout'][$order->order_id]['do_complete'] = TRUE;
$form_state['redirect'] = $form_state['storage']['base_path'] . '/checkout/complete';
}
}
/**
* Completes the sale and finishes checkout.
*/
function uc_cart_checkout_complete() {
if (empty($_SESSION['cart_order']) || empty($_SESSION['uc_checkout'][$_SESSION['cart_order']]['do_complete'])) {
drupal_goto('cart');
}
$order = uc_order_load(intval($_SESSION['cart_order']));
if (empty($order)) {
// Display messages to customers and the administrator if the order was lost.
drupal_set_message(t("We're sorry. An error occurred while processing your order that prevents us from completing it at this time. Please contact us and we will resolve the issue as soon as possible."), 'error');
watchdog('uc_cart', 'An empty order made it to checkout! Cart order ID: @cart_order', array('@cart_order' => $_SESSION['cart_order']), WATCHDOG_ERROR);
drupal_goto('cart');
}
$build = uc_cart_complete_sale($order, variable_get('uc_new_customer_login', FALSE));
unset($_SESSION['uc_checkout'][$order->order_id], $_SESSION['cart_order']);
// Add a comment to let sales team know this came in through the site.
uc_order_comment_save($order->order_id, 0, t('Order created through website.'), 'admin');
$page = variable_get('uc_cart_checkout_complete_page', '');
if (!empty($page)) {
drupal_goto($page);
}
return $build;
}

View File

@@ -0,0 +1,36 @@
<?php
/**
* @file
* This file contains the Rules hooks and functions necessary to
* make the cart related entity, conditions, events, and actions work.
*/
/**
* Implements hook_rules_event_info().
*/
function uc_cart_rules_event_info() {
$events['uc_checkout_complete'] = array(
'label' => t('Customer completes checkout'),
'group' => t('Cart'),
'variables' => array(
'order' => array(
'type' => 'uc_order',
'label' => t('Order'),
),
),
);
$events['uc_cart_checkout_start'] = array(
'label' => t('Customer starts checkout'),
'group' => t('Cart'),
'variables' => array(
'order' => array(
'type' => 'uc_order',
'label' => t('Order'),
),
),
);
return $events;
}

View File

@@ -0,0 +1,66 @@
<?php
/**
* @file
* Default Rules configurations.
*/
/**
* Implements hook_default_rules_configuration().
*/
function uc_cart_default_rules_configuration() {
// Setup an example rule for limiting product quantities.
$rule = rules_reaction_rule();
$rule->label = t('Maximum product quantity');
$rule->active = FALSE;
$rule->event('uc_cart_item_presave')
->condition('data_is', array(
'data:select' => 'uc-cart-item:qty',
'op' => '>',
'value' => '10',
))
->action('data_set', array(
'data:select' => 'uc-cart-item:qty',
'value' => '10',
))
->action('drupal_message', array(
'message' => 'You are only allowed to order a maximum of 10 of [uc-cart-item:node:title].',
'type' => 'warning',
));
$configs['uc_cart_maximum_product_qty'] = $rule;
// Setup a default configuration for customer checkout notifications.
$rule = rules_reaction_rule();
$rule->label = t('E-mail customer checkout notification');
$rule->active = TRUE;
$rule->event('uc_checkout_complete')
->action('uc_order_email_invoice', array(
'order:select' => 'order',
'from' => uc_store_email_from(),
'addresses' => '[order:email]',
'subject' => t('Your Order at [store:name]'),
'template' => 'customer',
'view' => 'checkout-mail',
));
$configs['uc_checkout_customer_notification'] = $rule;
// Setup a default predicate for admin checkout notifications.
$rule = rules_reaction_rule();
$rule ->label = t('E-mail admin checkout notification');
$rule->active = TRUE;
$rule->event('uc_checkout_complete')
->action('uc_order_email_invoice', array(
'order:select' => 'order',
'from' => uc_store_email_from(),
'addresses' => uc_store_email(),
'subject' => t('New Order at [store:name]'),
'template' => 'admin',
'view' => 'admin-mail',
));
$configs['uc_checkout_admin_notification'] = $rule;
return $configs;
}

View File

@@ -0,0 +1,295 @@
<?php
/**
* @file
* Theme functions for the uc_cart module.
*/
/**
* Themes the shopping cart block title.
*
* @param $variables
* An associative array containing:
* - title: The text to use for the title of the block.
* - icon_class: Class to use for the cart icon image or FALSE if the icon is
* disabled.
* - collapsible: TRUE or FALSE indicating whether or not the cart block is
* collapsible.
* - collapsed: TRUE or FALSE indicating whether or not the cart block is
* collapsed.
*
* @ingroup themeable
*/
function theme_uc_cart_block_title($variables) {
$title = $variables['title'];
$icon_class = $variables['icon_class'];
$collapsible = $variables['collapsible'];
$collapsed = $variables['collapsed'];
$output = '';
// Add in the cart image if specified.
if ($icon_class) {
$output .= theme('uc_cart_block_title_icon', array('icon_class' => $icon_class));
}
// Add the main title span and text, with or without the arrow based on the
// cart block collapsibility settings.
if ($collapsible) {
$output .= '<span class="cart-block-title-bar" title="' . t('Show/hide shopping cart contents.') . '">' . $title;
if ($collapsed) {
$output .= '<span class="cart-block-arrow arrow-down"></span>';
}
else {
$output .= '<span class="cart-block-arrow"></span>';
}
$output .= '</span>';
}
else {
$output .= '<span class="cart-block-title-bar">' . $title . '</span>';
}
return $output;
}
/**
* Themes the shopping cart icon.
*
* @param $variables
* An associative array containing:
* - icon_class: Class to use for the cart icon image, either cart-full or
* cart-empty.
*
* @ingroup themeable
*/
function theme_uc_cart_block_title_icon($variables) {
$icon_class = $variables['icon_class'];
return l('<span class="' . $icon_class . '" title="' . t('View your shopping cart.') . '"></span>', 'cart', array('html' => TRUE));
}
/**
* Themes the shopping cart block content.
*
* @param $variables
* An associative array containing:
* - help_text: Text to place in the small help text area beneath the cart
* block title or FALSE if disabled.
* - items: An associative array of cart item information containing:
* - qty: Quantity in cart.
* - title: Item title.
* - price: Item price.
* - desc: Item description.
* - item_count: The number of items in the shopping cart.
* - item_text: A textual representation of the number of items in the
* shopping cart.
* - total: The unformatted total of all the products in the shopping cart.
* - summary_links: An array of links used in the cart summary.
* - collapsed: TRUE or FALSE indicating whether or not the cart block is
* collapsed.
*
* @ingroup themeable
*/
function theme_uc_cart_block_content($variables) {
$help_text = $variables['help_text'];
$items = $variables['items'];
$item_count = $variables['item_count'];
$item_text = $variables['item_text'];
$total = $variables['total'];
$summary_links = $variables['summary_links'];
$collapsed = $variables['collapsed'];
$output = '';
// Add the help text if enabled.
if ($help_text) {
$output .= '<span class="cart-help-text">' . $help_text . '</span>';
}
// Add a table of items in the cart or the empty message.
$output .= theme('uc_cart_block_items', array('items' => $items, 'collapsed' => $collapsed));
// Add the summary section beneath the items table.
$output .= theme('uc_cart_block_summary', array('item_count' => $item_count, 'item_text' => $item_text, 'total' => $total, 'summary_links' => $summary_links));
return $output;
}
/**
* Themes the table listing the items in the shopping cart block.
*
* @param $variables
* An associative array containing:
* - items: An associative array of cart item information containing:
* - qty: Quantity in cart.
* - title: Item title.
* - price: Item price.
* - desc: Item description.
* - collapsed: TRUE or FALSE indicating whether or not the cart block is
* collapsed.
*
* @ingroup themeable
*/
function theme_uc_cart_block_items($variables) {
$items = $variables['items'];
$class = $variables['collapsed'] ? 'cart-block-items collapsed' : 'cart-block-items';
// If there are items in the shopping cart...
if ($items) {
$output = '<table class="' . $class . '"><tbody>';
// Loop through each item.
$row_class = 'odd';
foreach ($items as $item) {
// Add the basic row with quantity, title, and price.
$output .= '<tr class="' . $row_class . '"><td class="cart-block-item-qty">' . $item['qty'] . '</td>'
. '<td class="cart-block-item-title">' . $item['title'] . '</td>'
. '<td class="cart-block-item-price">' . theme('uc_price', array('price' => $item['price'])) . '</td></tr>';
// Add a row of description if necessary.
if ($item['desc']) {
$output .= '<tr class="' . $row_class . '"><td colspan="3" class="cart-block-item-desc">' . $item['desc'] . '</td></tr>';
}
// Alternate the class for the rows.
$row_class = ($row_class == 'odd') ? 'even' : 'odd';
}
$output .= '</tbody></table>';
}
else {
// Otherwise display an empty message.
$output = '<p class="' . $class . ' uc-cart-empty">' . t('There are no products in your shopping cart.') . '</p>';
}
return $output;
}
/**
* Themes the summary table at the bottom of the default shopping cart block.
*
* @param $variables
* An associative array containing:
* - item_count: The number of items in the shopping cart.
* - item_text: A textual representation of the number of items in the
* shopping cart.
* - total: The unformatted total of all the products in the shopping cart.
* - summary_links: An array of links used in the summary.
*
* @ingroup themeable
*/
function theme_uc_cart_block_summary($variables) {
$item_count = $variables['item_count'];
$item_text = $variables['item_text'];
$total = $variables['total'];
$summary_links = $variables['summary_links'];
// Build the basic table with the number of items in the cart and total.
$output = '<table class="cart-block-summary"><tbody><tr>'
. '<td class="cart-block-summary-items">' . $item_text . '</td>'
. '<td class="cart-block-summary-total"><label>' . t('Total:')
. '</label> ' . theme('uc_price', array('price' => $total)) . '</td></tr>';
// If there are products in the cart...
if ($item_count > 0) {
// Add a view cart link.
$output .= '<tr class="cart-block-summary-links"><td colspan="2">'
. theme('links', array('links' => $summary_links)) . '</td></tr>';
}
$output .= '</tbody></table>';
return $output;
}
/**
* Themes the uc_cart_view_form().
*
* Outputs a hidden copy of the update cart button first, so pressing Enter
* updates the cart instead of removing an item.
*
* @param $variables
* An associative array containing:
* - form: A render element representing the form.
*
* @see uc_cart_view_form()
* @ingroup themeable
*/
function theme_uc_cart_view_form($variables) {
$form = &$variables['form'];
$output = '<div class="uc-default-submit">';
$output .= drupal_render($form['actions']['update']);
$output .= '</div>';
$form['actions']['update']['#printed'] = FALSE;
$output .= drupal_render_children($form);
return $output;
}
/**
* Themes the cart checkout button(s).
*
* @param $variables
* An associative array containing:
* - buttons: A render element representing the form buttons.
*
* @see uc_cart_view_form()
* @ingroup themeable
*/
function theme_uc_cart_checkout_buttons($variables) {
$output = '';
if ($buttons = element_children($variables['buttons'])) {
// Render the first button.
$button = array_shift($buttons);
$output = drupal_render($variables['buttons'][$button]);
// Render any remaining buttons inside a separate container.
if ($buttons) {
$output .= '<div class="uc-cart-checkout-button-container clearfix">';
// Render the second button.
$output .= '<div class="uc-cart-checkout-button">';
$output .= '<div class="uc-cart-checkout-button-separator">' . t('- or -') . '</div>';
$button = array_shift($buttons);
$output .= drupal_render($variables['buttons'][$button]);
$output .= '</div>';
// Render any remaining buttons.
foreach ($buttons as $button) {
$output .= '<div class="uc-cart-checkout-button">';
$output .= drupal_render($variables['buttons'][$button]);
$output .= '</div>';
}
$output .= '</div>';
}
}
return $output;
}
/**
* Returns the text displayed for an empty shopping cart.
*
* @ingroup themeable
*/
function theme_uc_empty_cart() {
return '<p class="uc-cart-empty">' . t('There are no products in your shopping cart.') . '</p>';
}
/**
* Themes the sale completion page.
*
* @param $variables
* An associative array containing:
* - message: Message containing order number info, account info, and link to
* continue shopping.
*
* @ingroup themeable
*/
function theme_uc_cart_complete_sale($variables) {
return $variables['message'];
}

View File

@@ -0,0 +1,89 @@
<?php
/**
* @file
* Variable module hook implementations.
*/
/**
* Implements hook_variable_group_info().
*/
function uc_cart_variable_group_info() {
$groups['uc_cart_checkout'] = array(
'title' => t('Ubercart checkout settings'),
'access' => 'administer store',
'path' => array('admin/store/settings/checkout'),
);
return $groups;
}
/**
* Implements hook_variable_info().
*/
function uc_cart_variable_info($options) {
$variables['uc_msg_order_submit'] = array(
'type' => 'text',
'title' => t('Completion message header', array(), $options),
'description' => t('Header for message displayed after a user checks out.', array(), $options),
'group' => 'uc_cart_checkout',
'default' => uc_get_message('completion_message'),
);
$variables['uc_msg_order_logged_in'] = array(
'type' => 'text',
'title' => t('Completion message for logged in users', array(), $options),
'description' => t('Message displayed upon checkout for a user who is logged in.', array(), $options),
'group' => 'uc_cart_checkout',
'default' => uc_get_message('completion_logged_in'),
);
$variables['uc_msg_order_existing_user'] = array(
'type' => 'text',
'title' => t('Completion message for existing users', array(), $options),
'description' => t("Message displayed upon checkout for a user who has an account but wasn't logged in.", array(), $options),
'group' => 'uc_cart_checkout',
'default' => uc_get_message('completion_existing_user'),
);
$variables['uc_msg_order_new_user'] = array(
'type' => 'text',
'title' => t('Completion message for new users', array(), $options),
'description' => t("Message displayed upon checkout for a new user whose account was just created. You may use the special tokens !new_username for the username of a newly created account and !new_password for that account's password.", array(), $options),
'group' => 'uc_cart_checkout',
'default' => uc_get_message('completion_new_user'),
);
$variables['uc_msg_order_new_user_logged_in'] = array(
'type' => 'text',
'title' => t('Completion message for new logged in users', array(), $options),
'description' => t('Message displayed upon checkout for a new user whose account was just created and also <em>"Login users when new customer accounts are created at checkout."</em> is set on the <a href="!user_login_setting_ur">checkout settings</a>.', array('!user_login_setting_ur' => 'admin/store/settings/checkout'), $options),
'group' => 'uc_cart_checkout',
'default' => uc_get_message('completion_new_user_logged_in'),
);
$variables['uc_msg_continue_shopping'] = array(
'type' => 'text',
'title' => t('Continue shopping message', array(), $options),
'description' => t('Message displayed upon checkout to direct customers to another part of your site.', array(), $options),
'group' => 'uc_cart_checkout',
'default' => uc_get_message('continue_shopping'),
);
$variables['uc_cart_new_account_details'] = array(
'type' => 'text',
'title' => t('New account details help message', array(), $options),
'description' => t('Enter the help message displayed in the new account details fieldset when shown.', array(), $options),
'group' => 'uc_cart_checkout',
'default' => t('<b>Optional.</b> New customers may supply custom account details.<br />We will create these for you if no values are entered.', array(), $options),
);
$variables['uc_checkout_instructions'] = array(
'type' => 'text',
'title' => t('Checkout instructions', array(), $options),
'description' => t('Provide instructions for customers at the top of the checkout screen.', array(), $options),
'group' => 'uc_cart_checkout',
'default' => '',
);
$variables['uc_checkout_review_instructions'] = array(
'type' => 'text',
'title' => t('Checkout review instructions', array(), $options),
'description' => t('Provide instructions for customers at the top of the checkout review screen.', array(), $options),
'group' => 'uc_cart_checkout',
'default' => t("Your order is almost complete. Please review the details below and click 'Submit order' if all the information is correct. You may use the 'Back' button to make changes to your order if necessary.", array(), $options),
);
return $variables;
}

View File

@@ -0,0 +1,154 @@
/**
* @file
* Styles for the uc_cart module cart block.
*/
.cart-block-icon-full,
.cart-block-icon-empty {
float: left;
height: 16px;
margin: 2px 6px 0 0;
width: 16px;
}
.cart-block-icon-full {
background: transparent url(images/cart-full.png) no-repeat left center;
}
.cart-block-icon-empty {
background: transparent url(images/cart-empty.png) no-repeat left center;
}
/**
* Styles for the cart block title and toggle.
*/
.cart-block-title-bar {
display: inline;
padding-right: 20px;
position: relative;
}
.ucCollapseBlock-processed {
cursor: pointer;
}
.cart-block-arrow {
background: transparent url(images/bullet-arrow-up.gif) no-repeat center center;
height: 5px;
position: absolute;
right: 0;
top: 9px;
width: 10px;
}
.cart-block-title-bar .arrow-down {
background: transparent url(images/bullet-arrow-down.gif) no-repeat center center;
}
.cart-help-text {
font-size: x-small;
position: relative;
top: -5px;
}
/**
* Styles for the cart block contents and summary.
*/
.cart-block-items {
margin: 0;
padding: 2px;
}
.cart-block-items.collapsed {
display: none;
}
.cart-block-items tbody {
border-top: 0;
}
.cart-block-items td {
border: 0;
}
.cart-block-items tr {
vertical-align: top;
}
.cart-block-items tr.odd,
.cart-block-items tr.even {
background-color: inherit;
border: none;
}
.cart-block-item-qty {
white-space: nowrap;
}
.cart-block-item-title {
width: 100%;
}
.cart-block-item-price {
text-align: right;
white-space: nowrap;
}
.cart-block-item-desc ul.product-description {
margin: 0;
padding: 0 0 0.25em 1em;
}
.cart-block-item-desc .product-description li {
font-size: .8em;
margin: 0;
padding-top: 0;
padding-bottom: 0;
}
.cart-block-summary {
margin: 0;
padding: 2px;
}
.cart-block-summary tbody {
border-top: 0;
}
.cart-block-summary tr {
background-color: transparent;
vertical-align: top;
}
.cart-block-summary td {
border: 0;
}
.cart-block-summary-items {
white-space: nowrap;
}
.cart-block-summary-total {
text-align: right;
white-space: nowrap;
width: auto;
}
.cart-block-summary-total label {
display: inline;
font-weight: bold;
}
.cart-block-summary-links td {
text-align: right;
}
.cart-block-summary-links ul.links li {
border-right: solid 1px;
display: inline;
padding: 0 1em 0 .75em;
}
.cart-block-summary-links ul.links li.last {
border-right: none;
padding-right: 0;
}

View File

@@ -0,0 +1,18 @@
/**
* @file
* Adds effects and behaviors to the cart block.
*/
/**
* Sets the behavior to (un)collapse the cart block on a click
*/
Drupal.behaviors.ucCollapseBlock = {
attach: function(context) {
jQuery('.cart-block-title-bar:not(.ucCollapseBlock-processed)', context).addClass('ucCollapseBlock-processed').click(
function() {
var $items = jQuery('.cart-block-items').toggleClass('collapsed');
jQuery('.cart-block-arrow').toggleClass('arrow-down', $items.hasClass('collapsed'));
}
);
}
}

View File

@@ -0,0 +1,552 @@
<?php
/**
* @file
* Callbacks for the default Ubercart checkout panes plus helper functions.
*
* Checkout panes are defined using hook_uc_checkout_pane() and use a callback
* to handle the different processes involved in completing the checkout form.
* The default checkout panes are defined in uc_cart_uc_checkout_pane() in
* uc_cart.module.
*/
/**
* Displays the cart contents for review during checkout.
*/
function uc_checkout_pane_cart($op, $order, $form = NULL, &$form_state = NULL) {
switch ($op) {
case 'view':
$contents['cart_review_table'] = array(
'#theme' => 'uc_cart_review_table',
'#items' => $order->products,
'#weight' => variable_get('uc_pane_cart_field_cart_weight', 2),
);
return array('contents' => $contents, 'next-button' => FALSE);
case 'review':
//$review[] = theme('uc_checkout_pane_cart_review', array('items' => $order->products));
$review[] = theme('uc_cart_review_table', array('items' => $order->products, 'show_subtotal' => FALSE));
return $review;
}
}
/**
* Gets the user's email address for login.
*/
function uc_checkout_pane_customer($op, $order, $form = NULL, &$form_state = NULL) {
global $user;
switch ($op) {
case 'view':
if ($user->uid) {
$email = $user->mail;
$description = t('Order information will be sent to your account e-mail listed below.');// . '<br />'
$contents['primary_email'] = array('#type' => 'hidden', '#value' => $email);
$contents['email_text'] = array(
'#markup' => '<div>' . t('<b>E-mail address:</b> @email (<a href="!url">edit</a>)', array('@email' => $email, '!url' => url('user/' . $user->uid . '/edit', array('query' => drupal_get_destination())))) . '</div>',
);
}
else {
$email = $order->primary_email;
$description = t('Enter a valid email address for this order or <a href="!url">click here</a> to login with an existing account and return to checkout.', array('!url' => url('user/login', array('query' => drupal_get_destination()))));
$contents['primary_email'] = uc_textfield(t('E-mail address'), $email, TRUE, NULL, 64);
}
if (variable_get('uc_cart_email_validation', FALSE) && !$user->uid) {
$contents['primary_email_confirm'] = uc_textfield(t('Confirm e-mail address'), $email, TRUE, NULL, 64);
}
if ($user->uid == 0) {
$contents['new_account'] = array();
if (variable_get('uc_cart_new_account_name', FALSE)) {
$contents['new_account']['name'] = array(
'#type' => 'textfield',
'#title' => t('Username'),
'#default_value' => isset($order->data['new_user']['name']) ? $order->data['new_user']['name'] : '',
'#maxlength' => 60,
'#size' => 32,
);
}
if (variable_get('uc_cart_new_account_password', FALSE)) {
$contents['new_account']['pass'] = array(
'#type' => 'password',
'#title' => t('Password'),
'#maxlength' => 32,
'#size' => 32,
);
$contents['new_account']['pass_confirm'] = array(
'#type' => 'password',
'#title' => t('Confirm password'),
'#description' => t('Passwords must match to proceed.'),
'#maxlength' => 32,
'#size' => 32,
);
}
if (!empty($contents['new_account'])) {
$array = array(
'#type' => 'fieldset',
'#title' => t('New account details'),
'#description' => variable_get('uc_cart_new_account_details', t('<b>Optional.</b> New customers may supply custom account details.<br />We will create these for you if no values are entered.')),
'#collapsible' => FALSE,
);
$contents['new_account'] = array_merge($array, $contents['new_account']);
}
}
return array('description' => $description, 'contents' => $contents);
case 'process':
$pane = $form_state['values']['panes']['customer'];
if (!empty($pane['primary_email']) && !valid_email_address($pane['primary_email'])) {
form_set_error('panes][customer][primary_email', t('You must enter a valid e-mail address.'));
}
$order->primary_email = $pane['primary_email'];
if (variable_get('uc_cart_email_validation', FALSE) && !$user->uid &&
$pane['primary_email'] !== $pane['primary_email_confirm']) {
form_set_error('panes][customer][primary_email_confirm', t('The e-mail address did not match.'));
}
// Invalidate if an account already exists for this e-mail address, and the user is not logged into that account
if (!variable_get('uc_cart_mail_existing', TRUE) && $user->uid == 0 && !empty($pane['primary_email'])) {
if (db_query("SELECT uid FROM {users} WHERE mail LIKE :mail", array(':mail' => $pane['primary_email']))->fetchField() > 0) {
form_set_error('panes][customer][primary_email', t('An account already exists for your e-mail address. You will either need to login with this e-mail address or use a different e-mail address.'));
}
}
// If new users can specify names or passwords then...
if ((variable_get('uc_cart_new_account_name', FALSE) ||
variable_get('uc_cart_new_account_password', FALSE)) &&
$user->uid == 0) {
// Skip if an account already exists for this e-mail address.
if (variable_get('uc_cart_mail_existing', TRUE) && db_query("SELECT uid FROM {users} WHERE mail LIKE :mail", array(':mail' => $pane['primary_email']))->fetchField() > 0) {
drupal_set_message(t('An account already exists for your e-mail address. The new account details you entered will be disregarded.'));
}
else {
// Validate the username.
if (variable_get('uc_cart_new_account_name', FALSE) && !empty($pane['new_account']['name'])) {
$message = user_validate_name($pane['new_account']['name']);
if (!empty($message)) {
form_set_error('panes][customer][new_account][name', $message);
}
elseif (db_query("SELECT uid FROM {users} WHERE name LIKE :name", array(':name' => $pane['new_account']['name']))->fetchField()) {
form_set_error('panes][customer][new_account][name', t('The username %name is already taken. Please enter a different name or leave the field blank for your username to be your e-mail address.', array('%name' => $pane['new_account']['name'])));
}
else {
$order->data['new_user']['name'] = $pane['new_account']['name'];
}
}
// Validate the password.
if (variable_get('uc_cart_new_account_password', FALSE)) {
if (strcmp($pane['new_account']['pass'], $pane['new_account']['pass_confirm'])) {
form_set_error('panes][customer][new_account][pass_confirm', t('The passwords you entered did not match. Please try again.'));
}
if (!empty($pane['new_account']['pass'])) {
require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc');
$order->data['new_user']['hash'] = user_hash_password(trim($pane['new_account']['pass']));
}
}
}
}
if ($user->uid) {
$order->uid = $user->uid;
}
return TRUE;
case 'review':
$review[] = array('title' => t('E-mail'), 'data' => check_plain($order->primary_email));
return $review;
case 'settings':
$form['uc_cart_new_account_details'] = array(
'#type' => 'textarea',
'#title' => t('New account details help message'),
'#description' => t('Enter the help message displayed in the new account details fieldset when shown.'),
'#default_value' => variable_get('uc_cart_new_account_details', t('<b>Optional.</b> New customers may supply custom account details.<br />We will create these for you if no values are entered.')),
);
return $form;
}
}
/**
* Gets the delivery information.
*/
function uc_checkout_pane_delivery($op, $order, $form = NULL, &$form_state = NULL) {
$description = t('Enter your delivery address and information here.');
$copy = t('My delivery information is the same as my billing information.');
return uc_checkout_pane_address('delivery', $op, $order, $form_state, $description, $copy);
}
/**
* Gets the billing information.
*/
function uc_checkout_pane_billing($op, $order, $form = NULL, &$form_state = NULL) {
$description = t('Enter your billing address and information here.');
$copy = t('My billing information is the same as my delivery information.');
return uc_checkout_pane_address('billing', $op, $order, $form_state, $description, $copy);
}
/**
* Generic address pane handler.
*/
function uc_checkout_pane_address($pane, $op, $order, &$form_state, $description, $copy) {
global $user;
// Source pane for "copy address" checkbox.
static $source;
if (!isset($source)) {
$source = $pane;
}
switch ($op) {
case 'view':
if ($source != $pane) {
$contents['copy_address'] = array(
'#type' => 'checkbox',
'#title' => $copy,
'#default_value' => variable_get('uc_cart_default_same_address', FALSE),
'#ajax' => array(
'callback' => 'uc_checkout_pane_address_render',
'wrapper' => $pane . '-address-pane',
'progress' => array(
'type' => 'throbber',
),
),
);
}
if ($user->uid && $addresses = uc_select_addresses($user->uid, $pane)) {
$contents['select_address'] = array(
'#type' => 'select',
'#title' => t('Saved addresses'),
'#options' => $addresses['#options'],
'#ajax' => array(
'callback' => 'uc_checkout_pane_address_render',
'wrapper' => $pane . '-address-pane',
'progress' => array(
'type' => 'throbber',
),
),
'#states' => array(
'invisible' => array(
'input[name="panes[' . $pane . '][copy_address]"]' => array('checked' => TRUE),
),
),
);
}
$contents['address'] = array(
'#type' => 'uc_address',
'#default_value' => $order,
'#key_prefix' => $pane,
'#prefix' => '<div id="' . $pane . '-address-pane">',
'#suffix' => '</div>',
);
if (isset($form_state['values']['panes'][$pane]['copy_address'])) {
$contents['address']['#hidden'] = !empty($form_state['values']['panes'][$pane]['copy_address']);
}
elseif (isset($contents['copy_address'])) {
$contents['address']['#hidden'] = variable_get('uc_cart_default_same_address', FALSE);
}
if (isset($form_state['triggering_element'])) {
$element = &$form_state['triggering_element'];
if ($element['#name'] == "panes[$pane][copy_address]") {
$address = &$form_state['values']['panes'][$source];
foreach ($address as $field => $value) {
if (substr($field, 0, strlen($source)) == $source) {
$field = str_replace($source, $pane, $field);
$form_state['input']['panes'][$pane][$field] = $value;
$order->$field = $value;
}
}
}
if ($element['#name'] == "panes[$pane][select_address]") {
$address = $addresses[$element['#value']];
foreach ($address as $field => $value) {
$form_state['input']['panes'][$pane][$pane . '_' . $field] = $value;
$order->{$pane . '_' . $field} = $value;
}
}
// Forget any previous Ajax submissions, as we send new default values.
unset($form_state['uc_address']);
}
return array('description' => $description, 'contents' => $contents);
case 'process':
$panes = &$form_state['values']['panes'];
foreach ($panes[$pane] as $field => $value) {
if (substr($field, 0, strlen($pane)) == $pane) {
if (!empty($panes[$pane]['copy_address'])) {
$value = $panes[$source][str_replace($pane, $source, $field)];
}
$order->$field = $value;
}
}
return TRUE;
case 'review':
$review[] = array('title' => t('Address'), 'data' => uc_order_address($order, $pane, FALSE));
if (uc_address_field_enabled('phone') && !empty($order->{$pane . '_phone'})) {
$review[] = array('title' => t('Phone'), 'data' => check_plain($order->{$pane . '_phone'}));
}
return $review;
}
}
/**
* Ajax callback to re-render the full address element.
*/
function uc_checkout_pane_address_render($form, &$form_state) {
$element = &$form;
foreach (array_slice($form_state['triggering_element']['#array_parents'], 0, -1) as $field) {
$element = &$element[$field];
}
return $element['address'];
}
/**
* Allows a customer to make comments on the order.
*/
function uc_checkout_pane_comments($op, $order, $form = NULL, &$form_state = NULL) {
switch ($op) {
case 'view':
$description = t('Use this area for special instructions or questions regarding your order.');
if (!empty($order->order_id)) {
$default = db_query("SELECT message FROM {uc_order_comments} WHERE order_id = :id", array(':id' => $order->order_id))->fetchField();
}
else {
$default = NULL;
}
$contents['comments'] = array(
'#type' => 'textarea',
'#title' => t('Order comments'),
'#default_value' => $default,
);
return array('description' => $description, 'contents' => $contents);
case 'process':
db_delete('uc_order_comments')
->condition('order_id', $order->order_id)
->execute();
if (strlen($form_state['values']['panes']['comments']['comments']) > 0) {
uc_order_comment_save($order->order_id, 0, $form_state['values']['panes']['comments']['comments'], 'order', uc_order_state_default('post_checkout'), TRUE);
}
return TRUE;
case 'review':
$review = NULL;
$result = db_query("SELECT message FROM {uc_order_comments} WHERE order_id = :id", array(':id' => $order->order_id));
if ($comment = $result->fetchObject()) {
$review[] = array('title' => t('Comment'), 'data' => check_plain($comment->message));
}
return $review;
}
}
/**
* Finds the collapsible pane displayed above the pane with an ID of $pane_id.
*/
function _uc_cart_checkout_prev_pane($panes, $pane_id = NULL) {
if (is_null($pane_id)) {
return FALSE;
}
$prev = FALSE;
foreach ($panes as $target) {
if ($target['id'] == $pane_id) {
return $prev;
}
if ($target['collapsible'] && $target['enabled']) {
$prev = $target['id'];
}
}
return FALSE;
}
/**
* Finds the pane that displays below the pane with an ID of $pane_id.
*/
function _uc_cart_checkout_next_pane($panes, $pane_id = NULL) {
if (is_null($pane_id)) {
return FALSE;
}
$next = FALSE;
foreach ($panes as $target) {
if ($next) {
if ($target['collapsible'] && $target['enabled']) {
return $target['id'];
}
}
if ($target['id'] == $pane_id) {
$next = TRUE;
}
}
return FALSE;
}
/**
* Builds a list of checkout panes defined in the enabled modules.
*/
function _uc_checkout_pane_list($action = NULL) {
static $panes = array();
if (count($panes) > 0 && $action !== 'rebuild') {
return $panes;
}
foreach (module_invoke_all('uc_checkout_pane') as $id => $pane) {
// Preserve backward compatibility for panes with no key specified.
if (is_numeric($id)) {
$id = $pane['id'];
}
// Set defaults.
$pane += array(
'id' => $id,
'enabled' => TRUE,
'weight' => 0,
'review' => TRUE,
'process' => TRUE,
'collapsible' => TRUE,
);
$pane['enabled'] = variable_get('uc_pane_' . $id . '_enabled', $pane['enabled']);
$pane['weight'] = variable_get('uc_pane_' . $id . '_weight', $pane['weight']);
$panes[$id] = $pane;
}
// Allow other modules to alter the defaults.
drupal_alter('uc_checkout_pane', $panes);
uasort($panes, 'uc_weight_sort');
return $panes;
}
/**
* Returns data from a checkout pane by pane ID and the array key.
*/
function _uc_checkout_pane_data($pane_id, $key) {
$panes = _uc_checkout_pane_list();
return $panes[$pane_id][$key];
}
/**
* Formats the cart contents table on the checkout page.
*
* @param $variables
* An associative array containing:
* - show_subtotal: TRUE or FALSE indicating if you want a subtotal row
* displayed in the table.
* - items: An associative array of cart item information containing:
* - qty: Quantity in cart.
* - title: Item title.
* - price: Item price.
* - desc: Item description.
*
* @return
* The HTML output for the cart review table.
*
* @ingroup themeable
*/
function theme_uc_cart_review_table($variables) {
$items = $variables['items'];
$show_subtotal = $variables['show_subtotal'];
$subtotal = 0;
// Set up table header.
$header = array(
array('data' => theme('uc_qty_label'), 'class' => array('qty')),
array('data' => t('Products'), 'class' => array('products')),
array('data' => t('Price'), 'class' => array('price')),
);
// Set up table rows.
$display_items = uc_order_product_view_multiple($items);
if (!empty($display_items['uc_order_product'])) {
foreach (element_children($display_items['uc_order_product']) as $key) {
$display_item = $display_items['uc_order_product'][$key];
$subtotal += $display_item['total']['#price'];
$rows[] = array(
array('data' => $display_item['qty'], 'class' => array('qty')),
array('data' => $display_item['product'], 'class' => array('products')),
array('data' => $display_item['total'], 'class' => array('price')),
);
}
}
// Add the subtotal as the final row.
if ($show_subtotal) {
$rows[] = array(
'data' => array(
// One cell
array(
'data' => array(
'#theme' => 'uc_price',
'#prefix' => '<span id="subtotal-title">' . t('Subtotal:') . '</span> ',
'#price' => $subtotal,
),
// Cell attributes
'colspan' => 3,
'class' => array('subtotal'),
),
),
// Row attributes
'class' => array('subtotal'),
);
}
return theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('class' => array('cart-review'))));
}
/**
* Themes cart items on the checkout review order page.
*
* @param $variables
* An associative array containing:
* - items: An associative array of cart item information containing:
* - qty: Quantity in cart.
* - title: Item title.
* - price: Item price.
* - desc: Item description.
*
* @return
* A string of HTML for the page contents.
*
* @ingroup themeable
*/
function theme_uc_checkout_pane_cart_review($variables) {
$rows = array();
$items = uc_order_product_view_multiple($variables['items']);
foreach (element_children($items['uc_order_product']) as $key) {
$item = $items['uc_order_product'][$key];
$rows[] = array(
array('data' => $item['qty'], 'class' => array('qty')),
array('data' => $item['product'], 'class' => array('products')),
array('data' => $item['total'], 'class' => array('price')),
);
}
return theme('table', array('rows' => $rows, 'attributes' => array('class' => array('cart-review'))));;
}

View File

@@ -0,0 +1,99 @@
<?php
/**
* @file
* Views hooks.
*/
/**
* Implements hook_views_data().
*/
function uc_cart_views_data() {
// Cart items table.
$data['uc_cart_products']['table']['group'] = t('Cart item');
$data['uc_cart_products']['table']['base'] = array(
'field' => 'cart_item_id',
'title' => t('Cart items'),
'help' => t('Products in customer carts.'),
);
// Pull in node fields directly.
$data['node']['table']['join']['uc_cart_products'] = array(
'left_field' => 'nid',
'field' => 'nid',
);
$data['uc_cart_products']['nid'] = array(
'title' => t('Nid'),
'help' => t('The node ID of a product in the cart.'),
'field' => array(
'handler' => 'views_handler_field_node',
'click sortable' => TRUE,
),
'argument' => array(
'handler' => 'views_handler_argument_node_nid',
'name field' => 'title',
'numeric' => TRUE,
'validate type' => 'nid',
),
'sort' => array(
'handler' => 'views_handler_sort',
),
'filter' => array(
'handler' => 'views_handler_filter_numeric',
),
);
$data['uc_cart_products']['cart_id'] = array(
'title' => t('Cart ID'),
'help' => t('The ID of the cart (user ID for authenticated users, session ID for anonymous users).'),
'field' => array(
'handler' => 'uc_cart_handler_field_cart_user',
'click sortable' => TRUE,
),
'argument' => array(
'handler' => 'views_handler_argument_user_uid',
'name field' => 'name',
'numeric' => TRUE,
'validate type' => 'cart_id',
),
'sort' => array(
'handler' => 'views_handler_sort',
),
'filter' => array(
'handler' => 'views_handler_filter_numeric',
),
);
$data['uc_cart_products']['qty'] = array(
'title' => t('Quantity'),
'help' => t('The quantity to be ordered.'),
'field' => array(
'handler' => 'views_handler_field_numeric',
'click sortable' => TRUE,
),
'sort' => array(
'handler' => 'views_handler_sort',
),
'filter' => array(
'handler' => 'views_handler_filter_numeric',
),
);
$data['uc_cart_products']['changed'] = array(
'title' => t('Last modified'),
'help' => t('The time the cart item was last modified'),
'field' => array(
'handler' => 'views_handler_field_date',
'click sortable' => TRUE,
),
'sort' => array(
'handler' => 'views_handler_sort_date',
),
'filter' => array(
'handler' => 'views_handler_filter_date',
),
);
return $data;
}

View File

@@ -0,0 +1,100 @@
<?php
/**
* @file
* Views field handler.
*/
/**
* Field handler: allows linking to a user from a cart id.
*
* TODO: Should we subclass views_handler_field_user rather than
* views_handler_field?
*/
class uc_cart_handler_field_cart_user extends views_handler_field {
/**
* Overrides init function to provide generic option to link to user.
*/
function init(&$view, &$data) {
parent::init($view, $data);
if (!empty($this->options['link_to_user'])) {
$this->additional_fields['cart_id'] = 'cart_id';
}
}
/**
* Overrides views_handler::option_definition().
*/
function option_definition() {
$options = parent::option_definition();
$options['link_to_user'] = array('default' => TRUE);
$options['overwrite_anonymous'] = array('default' => TRUE);
$options['anonymous_text'] = array('default' => 'Anonymous', 'translatable' => TRUE);
return $options;
}
/**
* Overrides views_handler::options_form().
*
* Provides link to node option.
*/
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
$form['link_to_user'] = array(
'#title' => t('Link this field to its user'),
'#description' => t('This will override any other link you have set.'),
'#type' => 'checkbox',
'#default_value' => $this->options['link_to_user'],
);
$form['overwrite_anonymous'] = array(
'#title' => t('Overwrite the value to display for anonymous users'),
'#type' => 'checkbox',
'#default_value' => !empty($this->options['overwrite_anonymous']),
'#description' => t('If selected, you will see a field to enter the text to use for anonymous users.'),
);
$form['anonymous_text'] = array(
'#title' => t('Text to display for anonymous users'),
'#type' => 'textfield',
'#default_value' => $this->options['anonymous_text'],
'#dependency' => array(
'edit-options-overwrite-anonymous' => array(1),
),
);
}
/**
*
*/
function render_link($data, $values) {
if (!empty($this->options['link_to_user']) &&
user_access('access user profiles') &&
$values->{$this->aliases['cart_id']} &&
$data !== NULL &&
$data !== '') {
$this->options['alter']['make_link'] = TRUE;
$this->options['alter']['path'] = "user/" . $values->{$this->aliases['cart_id']};
}
return $data;
}
/**
* Overrides views_handler_field::render().
*/
function render($values) {
$result = db_query('SELECT name FROM {users} u WHERE u.uid = :uid', array(':uid' => $values->{$this->field_alias}))->fetchField();
if ($result) {
return $this->render_link($result, $values);
}
else {
// If the cart belongs to an unauthenticated user
if (!empty($this->options['overwrite_anonymous'])) {
return check_plain($this->options['anonymous_text']);
}
else {
return $values->{$this->field_alias};
}
}
}
}