Select Git revision
ordernumber.php 21.27 KiB
<?php
/**
* @package VirtueMart 2 OrderNumber plugin for Joomla! 2.5
* @author Reinhold Kainhofer, reinhold@kainhofer.com
* @copyright (C) 2012-2014 - Reinhold Kainhofer
* @license GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
**/
if ( !defined( 'ABSPATH' ) and !defined('_JEXEC') ) {
die( 'Direct Access to ' . basename( __FILE__ ) . ' is not allowed.' ) ;
}
if (!class_exists('vmShopperPlugin'))
require(JPATH_VM_PLUGINS . DS . 'vmshopperplugin.php');
// if (!class_exists( 'VmConfig' ))
// require(JPATH_ROOT.DS.'administrator'.DS.'components'.DS.'com_virtuemart'.DS.'helpers'.DS.'config.php');
// VmConfig::loadConfig();
if (!class_exists('OrdernumberHelperJoomla'))
require_once (dirname(__FILE__) . DS . 'ordernumber_helper_joomla.php');
class plgVmShopperOrdernumber extends vmShopperPlugin {
protected $helper = null;
function __construct(& $subject, $config) {
parent::__construct($subject, $config);
/* Create the database table */
$this->tableFields = array_keys ($this->getTableSQLFields ());
$this->helper = new OrdernumberHelperJoomla();
$this->helper->tableName = $this->_tablename;
$this->helper->registerCallback('setupStoreReplacements', array($this, 'setupStoreReplacements'));
$this->helper->registerCallback('setupOrderReplacements', array($this, 'setupOrderReplacements'));
$this->helper->registerCallback('setupUserReplacements', array($this, 'setupUserReplacements'));
$this->helper->registerCallback('setupShippingReplacements', array($this, 'setupShippingReplacements'));
$this->helper->registerCallback('setupThirdPartyReplacements', array($this, 'setupThirdPartyReplacements'));
}
/**
* Glue functions for the OrdernumberHelper
*/
public function getVmPluginCreateTableSQL () {
return $this->createTableSQL ('VM Shopper plugin: custom order and invoice numbers');
}
function getTableSQLFields () {
$SQLfields = array(
'id' => 'int(11) UNSIGNED NOT NULL AUTO_INCREMENT',
'number_type' => 'varchar(30)',
'number_format' => 'varchar(255)',
'count' => 'int(1)',
);
return $SQLfields;
}
// We don't need this function, but the parent class declares it abstract, so we need to overload
function plgVmOnUpdateOrderBEShopper($_orderID) {}
/* Extract the country information from the given ID */
protected static function getCountryFromID ($country_id) {
$db = JFactory::getDBO();
$query = 'SELECT * FROM `#__virtuemart_countries` WHERE `virtuemart_country_id` = ' . (int)$country_id;
$db->setQuery($query);
return $db->loadObject();
}
public function setupStoreReplacements (&$reps, $details, $nrtype) {
if (isset($details->virtuemart_vendor_id)) $reps["[vendorid]"] = $details->virtuemart_vendor_id;
}
public function setupAddressReplacements(&$reps, $prefix, $details, $nrtype) {
if (isset($details->email)) $reps["[email]"] = $details->email;
if (isset($details->title)) $reps["[title]"] = $details->title;
if (isset($details->first_name)) $reps["[firstname]"] = $details->first_name;
if (isset($details->middle_name)) $reps["[middlename]"] = $details->middle_name;
if (isset($details->last_name)) $reps["[lastname]"] = $details->last_name;
if (isset($details->company)) $reps["[company]"] = $details->company;
if (isset($details->zip)) {
$reps["[zip]"] = $details->zip;
$reps["[postcode]"] = $details->zip;
}
if (isset($details->city)) $reps["[city]"] = $details->city;
if (isset($details->virtuemart_country_id)) {
$reps["[countryid]"] = $details->virtuemart_country_id;
$country = $this->getCountryFromID ($details->virtuemart_country_id);
if ($country) {
$reps["[country]"] = $country->country_name;
$reps["[countrycode2]"] = $country->country_2_code;
$reps["[countrycode3]"] = $country->country_3_code;
}
}
if (isset($details->virtuemart_state_id)) {
$reps["[stateid]"] = $details->virtuemart_state_id;
// TODO: Also extract the state name and abbreviations
}
}
public function setupOrderReplacements (&$reps, $details, $nrtype) {
// Customer numbers are created before any order is submitted, so we don't have any information available.
if ($nrtype=='customer_number')
return;
// Only for Invoice:
if ($nrtype != 'order_number' && isset($details->order_number))
$reps["[ordernumber]"] = $details->order_number;
if (isset($details->virtuemart_order_id))
$reps["[orderid]"] = $details->virtuemart_order_id;
if (isset($details->order_status))
$reps["[orderstatus]"] = $details->order_status;
// TODO: Make the shipping/billing address available for order numbers, too!
$this->setupAddressReplacements($reps, "", $details, $nrtype);
if (isset($details->order_total)) $reps['[ordertotal]'] = $details->order_total;
if (isset($details->order_total)) $reps['[amount]'] = $details->order_total;
if (isset($details->order_subtotal)) $reps['[ordersubtotal]'] = $details->order_total;
if (isset($details->order_tax)) $reps['[ordersubtotal]'] = $details->order_tax;
if (isset($details->order_shipment)) $reps['[ordershipment]'] = $details->order_shipment;
if (isset($details->order_payment)) $reps['[orderpayment]'] = $details->order_payment;
if (isset($details->order_discount)) $reps['[orderdiscount]'] = $details->order_discount;
$articles = 0;
$products = 0;
$skus = array();
$categories = array();
$menufacturers = array();
$vendors = array();
// If we have a virtuemart_order_id already, load that order,
// otherwise assume the order is still in the cart
$items = array();
if (isset($details->virtuemart_order_id)) {
$orderModel = VmModel::getModel('orders');
$productModel = VmModel::getModel('product'); $order = (object)$orderModel->getOrder($details->virtuemart_order_id);
foreach ($order->items as $i) {
$articles += $i->product_quantity;
$products += 1;
$p = $productModel->getProduct($i->virtuemart_product_id);
$skus[$p->product_sku] = 1;
foreach ($p->categories as $c) {
$categories[$c] = 1;
}
foreach ($p->virtuemart_manufacturer_id as $m) {
$manufacturers[$m] = 1;
}
$vendors[$p->virtuemart_vendor_id] = 1;
}
} else {
$cart = VirtueMartCart::getCart();
foreach ($cart->products as $p) {
$articles += $p->quantity;
$products += 1;
$skus[$p->product_sku] = 1;
foreach ($p->categories as $c) {
$categories[$c] = 1;
}
foreach ($p->virtuemart_manufacturer_id as $m) {
$manufacturers[$m] = 1;
}
$vendors[$p->virtuemart_vendor_id] = 1;
}
}
$reps["[articles]"] = $articles;
$reps["[products]"] = $products;
$reps["[skus]"] = array_keys($skus);
$reps["[categories]"] = array_keys($categories);
$reps["[manufacturers]"] = array_keys($menufacturers);
$reps["[vendors]"] = array_keys($vendors);
}
public function setupUserReplacements (&$reps, $details, $nrtype) {
// TODO: Implement shopper group!
$reps["[userid]"] = $details->virtuemart_user_id;
if (isset($details->ip_address)) $reps["[ipaddress]"] = $details->ip_address;
// Customer number:
if (isset($details->username)) $reps["[username]"] = $details->username;
if (isset($details->name)) $reps["[name]"] = $details->name;
if (isset($details->user_is_vendor)) $reps["[user_is_vendor]"] = $details->user_is_vendor;
}
public function setupShippingReplacements(&$reps, $order, $nrtype) {
if (isset($details->virtuemart_paymentmethod_id)) $reps['[paymentmethod]'] = $details->virtuemart_paymentmethod_id;
if (isset($details->virtuemart_shipmentmethod_id)) $reps['[shipmentmethod]'] = $details->virtuemart_shipmentmethod_id;
}
public function setupThirdPartyReplacements (&$reps, $details, $nrtype) {
JPluginHelper::importPlugin('vmshopper');
JDispatcher::getInstance()->trigger('onVmOrdernumberGetVariables',array(&$reps, $nrtype, $details));
}
function assignNumber($order, $type='ordernumber', $default="#") {
if ($this->params->get('customize_'.$type, 0)) {
$fmt = $this->params->get ($type.'_format', $default);
$ctrsettings = array(
"${type}_format" => '',
"${type}_counter" => '',
"${type}_global" => $this->params->get ($type.'_global', 0),
"${type}_padding" => $this->params->get ($type.'_padding', 0),
"${type}_step" => 1,
"${type}_start" => 1,
); $customvars = $this->helper->transposeCustomVariables($this->params->get ('replacements', array()));
$number = $this->helper->createNumber ($fmt, $type, $order, $customvars, $ctrsettings);
return $number;
} else {
return false;
}
}
function plgVmOnUserOrder(&$orderDetails/*,&$data*/) {
$ordernumber = $this->assignNumber($orderDetails, 'order_number', "#");
if ($ordernumber !== false) {
// TODO: Check if ordernr already exists
$orderDetails->order_number = $ordernumber;
}
$orderpwd = $this->assignNumber($orderDetails, 'order_password', "[randomHex8]");
if ($orderpwd !== false) {
$orderDetails->order_pass = $orderpwd;
}
}
function plgVmOnUserInvoice($orderDetails,&$data) {
// Is order number customization enabled?
if ($this->params->get('customize_invoice_number')) {
// check the default configuration
$orderstatusForInvoice = VmConfig::get('inv_os',array('C'));
if(!is_array($orderstatusForInvoice)) $orderstatusForInvoice = array($orderstatusForInvoice); //for backward compatibility 2.0.8e
$pdfInvoice = (int)VmConfig::get('pdf_invoice', 0); // backwards compatible
// For VM<3.0.12, the URL parameter is an int, for VM>=3.0.12, the URL param is a string/cmd
$force_create_invoice = JFactory::getApplication()->input->getCmd('create_invoice', -1);
if (is_numeric($force_create_invoice)) {
// numeric means we have the old behavor pre-3.0.12 => No invoice_pass to check
$invoice_pass = 1;
} else {
$invoice_pass = isset($orderDetails['order_create_invoice_pass']) ? $orderDetails['order_create_invoice_pass'] : '';
}
if ( in_array($orderDetails['order_status'],$orderstatusForInvoice) or $pdfInvoice==1 or $force_create_invoice==$invoice_pass ){
$invoicenr = $this->assignNumber((object)$orderDetails, 'invoice_number', "#");
if ($invoicenr !== false) {
// TODO: Check if ordernr already exists
$data['invoice_number'] = $invoicenr;
return $data;
}
}
}
}
// Customizing the customer numbers requires VM >= 2.0.15b, earlier versions
// left out the & and thus didn't allow changing the user data
function plgVmOnUserStore(&$data) {
if (isset($data['customer_number_bycore']) && $data['customer_number_bycore']==1) {
$customernr = $this->assignNumber((object)$data, 'customer_number', "#");
if ($customernr !== false) {
// TODO: Check if ordernr already exists
$data['customer_number'] = $customernr;
return $data;
}
}
}
/* In versions before VM 2.6.8, the onStoreInstallPluginTable function was protected, so the installer couldn't call it to create the plugin table...
This function simply is a public wrapper to make this function available to the installer on all VM versions: */
public function plgVmOnStoreInstallPluginTable($psType, $name='') {
return $this->onStoreInstallPluginTable($psType, $name);
}
/**
* plgVmOnSelfCallBE ... Called to execute some plugin action in the backend (e.g. set/reset dl counter, show statistics etc.)
*/ function plgVmOnSelfCallBE($type, $name, &$output) {
if ($name != $this->_name || $type != 'vmshopper') return false;
vmDebug('plgVmOnSelfCallBE');
$user = JFactory::getUser();
$authorized = ($user->authorise('core.admin','com_virtuemart') or
$user->authorise('core.manage','com_virtuemart') or
$user->authorise('vm.orders','com_virtuemart'));
$json = array();
$json['authorized'] = $authorized;
if (!$authorized) return FALSE;
$action = vRequest::getCmd('action');
$counter= vRequest::getString('counter');
$nrtype = vRequest::getString('nrtype');
$json['action'] = $action;
$json['success'] = 0; // default: unsuccessfull
switch ($action) {
case "deleteCounter":
$json['success'] = $this->helper->deleteCounter($nrtype, $counter);
break;
case "addCounter":
$value = vRequest::getInt('value',0);
if ($this->helper->counterExists($nrtype, $counter)) {
$json['error'] = JText::sprintf('PLG_ORDERNUMBER_COUNTERLIST_EXISTS', $counter);
$json['success'] = false;
} else {
$json['success'] = $this->helper->addCounter($nrtype, $counter, $value);
// Return the table row for the new counter in the JSON:
$json['row'] = $this->helper->counter_modification_create_row($nrtype, $counter, $value);
}
break;
case "setCounter":
$value = vRequest::getInt('value');
$json['success'] = $this->helper->setCounter($nrtype, $counter, $value);
$json['row'] = $this->helper->counter_modification_create_row($nrtype, $counter, $value);
break;
case "check_update_access":
$order_number = vRequest::getString('order_number');
$order_pass = vRequest::getString('order_pass');
$json = $this->checkUpdateAccess($order_number, $order_pass, $json);
break;
}
// Also return all messages (in HTML format!):
// Since we are in a JSON document, we have to temporarily switch the type to HTML
// to make sure the html renderer is actually used
$document = JFactory::getDocument ();
$previoustype = $document->getType();
$document->setType('html');
$msgrenderer = $document->loadRenderer('message');
$json['messages'] = $msgrenderer->render('Message');
$document->setType($previoustype);
// WORKAROUND for broken (i.e. duplicate) content-disposition headers in Joomla 2.x:
// We request everything in raw and here send the headers for JSON and return
// the raw output in json format
$document =JFactory::getDocument();
$document->setMimeEncoding('application/json');
JResponse::setHeader('Content-Disposition','attachment;filename="ordernumber.json"');
$output = json_encode($json);
}
public function checkUpdateAccess($order_number, $order_pass, $json = array()) {
// First, extract the update server URL from the manifest, then load
// the update XML from the update server, extract the download URL,
// append the order number and password and check whether access is
// possible.
$json['success'] = FALSE; if (isset($this->_xmlFile)) {
$xmlfile = $this->_xmlFile;
} else {
// VM 2 does not set the _xmlFile property, so construct it manually
$xmlfile = JPATH_SITE . '/plugins/' . $this->_type . '/' . $this->_name . '/' . $this->_name . '.xml';
}
$xml = simplexml_load_file($xmlfile);
if (!$xml || !isset($xml->updateservers)) {
JFactory::getApplication()->enqueueMessage(JText::sprintf('OPENTOOLS_XMLMANIFEST_ERROR', $this->_xmlFile), 'error');
return $json;
}
$updateservers = $xml->updateservers;
foreach ($updateservers->children() as $server) {
if ($server->getName()!='server') {
JFactory::getApplication()->enqueueMessage(JText::sprintf('OPENTOOLS_XMLMANIFEST_ERROR', $this->_xmlFile), 'error');
continue;
}
$updateurl = html_entity_decode((string)$server);
$updatescript = simplexml_load_file($updateurl);
if (!$updatescript) {
JFactory::getApplication()->enqueueMessage(JText::sprintf('OPENTOOLS_UPDATESCRIPT_ERROR', $updateurl), 'error');
continue;
}
$urls = $updatescript->xpath('/updates/update/downloads/downloadurl');
while (list( , $node) = each($urls)) {
$downloadurl = (string)($node);
if ($order_number) {
$downloadurl .= (parse_url($downloadurl, PHP_URL_QUERY) ? '&' : '?') . 'order_number=' . urlencode($order_number);
}
if ($order_pass) {
$downloadurl .= (parse_url($downloadurl, PHP_URL_QUERY) ? '&' : '?') . 'order_pass=' . urlencode($order_pass);
}
$downloadurl .= (parse_url($downloadurl, PHP_URL_QUERY) ? '&' : '?') . 'check_access=1';
$headers = get_headers($downloadurl);
list($version, $status_code, $msg) = explode(' ',$headers[0], 3);
// Check the HTTP Status code
switch($status_code) {
case 200:
$json['success'] = TRUE;
JFactory::getApplication()->enqueueMessage($msg, 'message');
$this->setupUpdateCredentials($order_number, $order_pass);
break;
default:
JFactory::getApplication()->enqueueMessage($msg, 'error');
// Clear the credentials...
$this->setupUpdateCredentials("", "");
break;
}
$this->setAndSaveParams(array(
'update_credentials_checked'=>$json['success'],
'order_number' => $order_number,
'order_pass' => $order_pass,
));
}
}
return $json;
}
protected function setAndSaveParams($params) {
$db = JFactory::getDbo();
$query = $db->getQuery(true)
->select('extension_id')
->from('#__extensions')
->where('folder = '.$db->quote($this->_type))
->where('element = '.$db->quote($this->_name))
->where('type =' . $db->quote('plugin'))
->order('ordering');
$plugin = $db->setQuery($query)->loadObject();
if (!$plugin)
return;
$pluginId=$plugin->extension_id;
foreach ($params as $param=>$parvalue) {
$this->params->set($param, $parvalue);
}
$extensions = JTable::getInstance('extension');
$extensions->load($pluginId);
$extensions->bind(array('params' => $this->params->toString()));
// check and store
if (!$extensions->check()) {
$this->setError($extensions->getError());
return false;
}
if (!$extensions->store()) {
$this->setError($extensions->getError());
return false;
}
}
function setupUpdateCredentials($ordernumber, $orderpass) {
$db = JFactory::getDbo();
$query = $db->getQuery(true)
->select('extension_id AS id')
->from('#__extensions')
->where('folder = '.$db->quote($this->_type))
->where('element = '.$db->quote($this->_name))
->where('type =' . $db->quote('plugin'))
->order('ordering');
$plugin = $db->setQuery($query)->loadObject();
if (empty($plugin))
return;
$ordernumber = preg_replace("/[^-A-Za-z0-9_]/", '', $ordernumber);
$orderpass = preg_replace("/[^-A-Za-z0-9_]/", '', $orderpass);
$extra_query = array();
if ($ordernumber!='') {
$extra_query[] = 'order_number='.preg_replace("/[^-A-Za-z0-9_]/", '', $ordernumber);
}
if ($orderpass!='') {
$extra_query[] = 'order_pass='.preg_replace("/[^-A-Za-z0-9_]/", '', $orderpass);
}
// Joomla 2.5 needs the filename to end in .zip:
$extra_query[] = 'filetype=.zip';
$extra_query = implode('&', $extra_query);
// The following code is based on Nicholas K. Dionysopoulos' Joomla Pull request:
// https://github.com/joomla/joomla-cms/pull/2508
// Load the update site record, if it exists
$db = JFactory::getDbo();
$query = $db->getQuery(true)
->select('update_site_id AS id')
->from($db->qn('#__update_sites_extensions'))
->where($db->qn('extension_id').' = '.$db->q($plugin->id));
$db->setQuery($query);
$updateSites = $db->loadObjectList();
foreach ($updateSites as $updateSite) {
// Update the update site record
$query = $db->getQuery(true)
->update($db->qn('#__update_sites'))
->set('extra_query = '.$db->q($extra_query)) ->set('last_check_timestamp = 0')
->where($db->qn('update_site_id').' = '.$db->q($updateSite->id));
$db->setQuery($query);
$db->execute();
// Delete any existing updates (essentially flushes the updates cache for this update site)
$query = $db->getQuery(true)
->delete($db->qn('#__updates'))
->where($db->qn('update_site_id').' = '.$db->q($updateSite->id));
$db->setQuery($query);
$db->execute();
}
}
}