Skip to content
Snippets Groups Projects
Select Git revision
  • 1b44f0f15e4441e2bd77f1f7f198751d2f68e0b1
  • master default
  • V1.0
3 results

Makefile

Blame
  • ordernumber.php 27.56 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
    **/
    
    defined('_JEXEC') or     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();
    
    class plgVmShopperOrdernumber extends vmShopperPlugin {
    
        function __construct(& $subject, $config) {
            parent::__construct($subject, $config);
            /* Create the database table */
            $this->tableFields = array_keys ($this->getTableSQLFields ());
        }
    
        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) {}
        
       function _getCounter($nrtype, $format, $default=0) {
            $db = JFactory::getDBO();
            
            /* prevent sql injection attacks by escaping the user-entered format! Empty for global counter... */
            /* For global counting, simply read the empty number_format entries! */
            $q = 'SELECT `count` FROM `'.$this->_tablename.'` WHERE `number_type`='.$db->quote($nrtype).' AND `number_format`='.$db->quote($format);
            $db->setQuery($q);
            $existing = $db->loadResult();
            $count = $existing?$existing:$default;
            return $count;
        }
        
        function _counterExists($nrtype, $format) {
            $db = JFactory::getDBO();
            $q = 'SELECT `count` FROM `'.$this->_tablename.'` WHERE `number_type`='.$db->quote($nrtype).' AND `number_format`='.$db->quote($format);
            $db->setQuery($q);
            return ($db->loadResult() != null);
        }
        
        // Insert new counter value into the db
        function _addCounter($nrtype, $format, $value) {
            $db = JFactory::getDBO();
            $q = 'INSERT INTO `'.$this->_tablename.'` (`count`, `number_type`, `number_format`) VALUES ('.(int)$value.','.$db->quote($nrtype).', '.$db->quote($format).')';
            $db->setQuery( $q );
            $db->query();
            return $db->getAffectedRows();
        }
    
        // Insert new counter value into the db or update existing one
        function _setCounter($nrtype, $format, $value) {
            $db = JFactory::getDBO();
            $q = 'UPDATE `'.$this->_tablename.'` SET `count`= "'.(int)$value.'" WHERE `number_type`='.$db->quote($nrtype).' AND `number_format`='.$db->quote($format);
            $db->setQuery( $q );
            $db->query();
            if ($db->getAffectedRows()<1) {
                return $this->_addCounter($nrtype, $format, $value);
            } else {
                return $db->getAffectedRows();
            }
        }
    
        // Insert new counter value into the db or update existing one
        function _deleteCounter($nrtype, $format) {
            $db = JFactory::getDBO();
            $format = $db->escape ($format);
            $q = 'DELETE FROM `'.$this->_tablename.'` WHERE `number_type`='.$db->quote($nrtype).' AND `number_format`='.$db->quote($format);
            $db->setQuery( $q );
            $db->query();
            return $db->getAffectedRows();
        }
    
        /* Return a random "string" of the given length taken from the given alphabet */
        static function randomString($alphabet, $len) {
            $alen = strlen($alphabet);
            $r = "";
            for ($n=0; $n<$len; $n++) {
                $r .= $alphabet[mt_rand(0, $alen-1)];
            }
            return $r;
        }
    
        function replaceRandom ($match) {
            /* the regexp matches (random)(Type)(Len) as match, Type and Len is optional */
            $len = ($match[3]?$match[3]:1);
            // Fallback: If no Type is given, use Digit
            $alphabet = "0123456789";
            // Select the correct alphabet depending on Type
            switch (strtolower($match[2])) {
                case "digit": $alphabet = "0123456789"; break;
                case "hex": $alphabet = "0123456789abcdef"; break;
                case "letter": $alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; break;
                case "uletter": $alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; break;
                case "lletter": $alphabet = "abcdefghijklmnopqrstuvwxyz"; break;
                case "alphanum": $alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; break;
            }
            return self::randomString ($alphabet, $len);
        }
        
        /* Extract the country information from the given ID */
        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();
        }
    
    
    
        protected function setupDateTimeReplacements (&$reps, $details, $nrtype) {
        	$utime = microtime(true);
        	$reps["[year]"] = date ("Y", $utime);
        	$reps["[year2]"] = date ("y", $utime);
        	$reps["[month]"] = date("m", $utime);
        	$reps["[day]"] = date("d", $utime);
        	$reps["[hour]"] = date("H", $utime);
        	$reps["[hour12]"] = date("h", $utime);
        	$reps["[ampm]"] = date("a", $utime);
        	$reps["[minute]"] = date("i", $utime);
        	$reps["[second]"] = date("s", $utime);
        	$milliseconds = (int)(1000*($utime - (int)$utime));
        	$millisecondsstring = sprintf('%03d', $milliseconds);
        	$reps["[decisecond]"] = $millisecondsstring[0];
        	$reps["[centisecond]"] = substr($millisecondsstring, 0, 2);
        	$reps["[millisecond]"] = $millisecondsstring;
        }
    
        protected function setupStoreReplacements (&$reps, $details, $nrtype) {
            if (isset($details->virtuemart_vendor_id)) 
                $reps["[vendorid]"] = $details->virtuemart_vendor_id;
        }
        
        protected 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
            }
        }
        
        protected 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);
    		
        }
       
    
        protected 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;
        }
        
        protected 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;
        }
        
        protected function setupThirdPartyReplacements (&$reps, $details, $nrtype) {
            JPluginHelper::importPlugin('vmshopper');
            JDispatcher::getInstance()->trigger('onVmOrdernumberGetVariables',array(&$reps, $nrtype, $details));
        }
        
        protected function setupReplacements($nrtype, $details) {
            $reps = array();
            $this->setupDateTimeReplacements($reps, $details, $nrtype);
            $this->setupStoreReplacements($reps, $details, $nrtype);
            $this->setupOrderReplacements($reps, $details, $nrtype);
            $this->setupUserReplacements($reps, $details, $nrtype);
            $this->setupShippingReplacements($reps, $details, $nrtype);
            $this->setupThirdPartyReplacements($reps, $details, $nrtype);
            return $reps;
        }
    
        protected function setupCustomVariables ($nrtype, $order, $reps, $customvars) {
            foreach ($customvars as $c) {
                $conditionvar = strtolower($c['conditionvar']);
                $op = $c['conditionop'];
                
                $found = false;
                $match = false;
                $compareval = null;
                
                if (isset($reps[$conditionvar])) {
                    $found = true;
                    $compareval = $reps[$conditionvar];
                } elseif (isset($reps['['.$conditionvar.']'])) {
                    $found = true;
                    $compareval = $reps['['.$conditionvar.']'];
                }/* elseif ($order && $compareval = $order->getData($conditionvar)) {
                    // TODO: Handle order property
                    $found = true;
                }*/ else {
                    // TODO: Handly other possible properties!
                    // TODO: Print out warning that variable could not be found.
                }
                if ($found) {
                    $condval = $c['conditionval'];
                    switch ($op) {
                        case 'nocondition':
                                $match = true; break;
                        case 'equals': 
                                $match = ($compareval == $condval); break;
                        case 'contains':
                                if (is_array($compareval)) {
                                    $match = in_array($condval, $compareval);
                                } else {
                                    $match = strpos ($compareval, $condval);
                                }
                                break;
                        case 'smaller':
                                $match = ($compareval<$condval); break;
                        case 'smallerequal':
                                $match = ($compareval<=$condval); break;
                        case 'larger':
                                $match = ($compareval>$condval); break;
                        case 'largerequal':
                                $match = ($compareval>=$condval); break;
                        case 'startswith':
                                $match = (substr("$compareval", 0, strlen("$condval")) === "$condval"); break;
                        case 'endswith':
                                $match = (substr("$compareval", -strlen("$condval")) === "$condval"); break;
                    }
                } elseif (empty($conditionvar)) {
                    $match = true;
                }
                if ($match) {
                    $varname = '['.strtolower($c['newvar']).']';
                    $reps[$varname] = $c['newval'];
                }
            }
            return $reps;
        }
    
        // Allow the user to override the format like any other custom variable:
        protected function setupNumberFormatString($fmt, $type, $order, $reps) {
            if (isset($reps['['.$type.'_format]'])) {
                return $reps['['.$type.'_format]'];
            } else {
                return $fmt;
            }
        }
        
        protected function doReplacements ($fmt, $reps) {
            // First, replace all random...[n] fields. This needs to be done with a regexp and a callback:
            $fmt = preg_replace_callback ('/\[(random)(.*?)([0-9]*?)\]/', array($this, 'replaceRandom'), $fmt);
            // Only use string-valued variables for replacement (array-valued variables can be used in custom variable definitions!)
            $reps = array_filter($reps, function($v) { return !is_array($v);} );
            return str_ireplace (array_keys($reps), array_values($reps), $fmt);
        }
        
        protected function extractCounterSettings ($fmt, $type, $ctrsettings) {
    		// First, extract all counter settings, i.e. all strings of the form [#####:startval/increment] or [####/increment:startval]
    		$regexp = '%\[(#+)(/([0-9]+))?(:([0-9]+))?(/([0-9]+))?\]%';
    		
    		if (preg_match($regexp, $fmt, $counters)) {
    			// $counters is an array of the form:
    			// Array (
    			// 		[0] => [#####:100/3]
    			// 		[1] => #####
    			// 		[2] => 
    			// 		[3] => 
    			// 		[4] => :100
    			// 		[5] => 100
    			// 		[6] => /3
    			// 		[7] => 3
    			// )
    			$ctrsettings["${type}_padding"] = strlen($counters[1]);
    			if (!empty($counters[2])) {
    				// $counters[2] contains the whole "/n" part, while $counters[3] contains just the step itself
    				$ctrsettings["${type}_step"] = $counters[3]; 
    			}
    			if (!empty($counters[4])) {
    				// $counters[4] contains the whole ":n" part, while $counters[5] contains just the start value itself
    				$ctrsettings["${type}_start"] = $counters[5]; 
    			}
    			
    			if (!empty($counters[6])) {
    				// $counters[6] contains the whole ":n" part, while $counters[7] contains just the start value itself
    				$ctrsettings["${type}_step"] = $counters[7]; 
    			}
    			
    			$fmt = preg_replace($regexp, "#", $fmt);
    		}
    		// Split at a | to get the number format and a possibly different counter increment format
    		// If a separate counter format is given after the |, use it, otherwise reuse the number format itself as counter format
    		$parts = explode ("|", $fmt);
    		$ctrsettings["${type}_format"] = $parts[0];
    		$ctrsettings["${type}_counter"] = ($ctrsettings["${type}_global"]==1)?"":$parts[(count($parts)>1)?1:0];
    		
    		return $ctrsettings;
    	}
    
        /* replace the variables in the given format. $type indicates the type of number. */
        function createNumber ($fmt, $type, $order, $customvars, $ctrsettings) {
            $reps   = $this->setupReplacements ($type, $order);
            $reps   = $this->setupCustomVariables ($type, $order, $reps, $customvars);
            $format = $this->setupNumberFormatString($fmt, $type, $order, $reps);
            $format = $this->doReplacements($format, $reps);
            $ctrsettings = $this->extractCounterSettings ($format, $type, $ctrsettings);
    
    // JFactory::getApplication()->enqueueMessage("<pre>Replacements for $type:".print_r($reps,1)."</pre>", 'error');
            // Increment the counter only if the format contains a placeholder for it!
            if (strpos($ctrsettings["${type}_format"], "#") !== false) {
                $countername = $ctrsettings["${type}_counter"];
                // Look up the current counter
                $count = $this->_getCounter($type, $countername, $ctrsettings["${type}_start"] - $ctrsettings["${type}_step"]) + $ctrsettings["${type}_step"];
                $this->_setCounter($type, $countername, $count);
                // return the format with the counter inserted
                $number = str_replace ("#", sprintf('%0' . $ctrsettings["${type}_padding"] . 's', $count), $ctrsettings["${type}_format"]);
            } else {
                $number = $ctrsettings["${type}_format"];
            }
            return $number;
        }
        
        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,
                );
                $cvar = $this->params->get ('replacements', array());
                // Even though the replacements are created and stored as an array, they are retrieved as a stdClass object:
                if (is_object($cvar)) $cvar = (array)$cvar;
                if (!is_array($cvar))
                    $cvar = array();
                // The customvars are stored in transposed form (for technical reasons, since there is no trigger 
                // called when the corresponding form field from the plugin param is saved)
                $customvars = array();
            
                if (!empty($cvar)) {
                    $keys = array_keys($cvar);
                    foreach (array_keys($cvar[$keys[0]]) as $i) {
                        $entry = array();
                        foreach ($keys as $k) {
                            $entry[$k] = $cvar[$k][$i];
                        }
                        $customvars[] = $entry;
                    }
                }
            
    
                $number = $this->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'] : 'DO_NOT_CREATE_INVOICE';
                }
    
                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;
                }
            }
        }
     
    
        /**
         * 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->_deleteCounter($nrtype, $counter);
                    break;
                case "addCounter":
                    $value = vRequest::getInt('value',0);
                    if ($this->_counterExists($nrtype, $counter)) {
                        $json['error'] = JText::sprintf('PLG_ORDERNUMBER_COUNTERLIST_EXISTS', $counter);
                        $json['success'] = false;
                    } else {
                        $json['success'] = $this->_addCounter($nrtype, $counter, $value);
                        // Return the table row for the new counter in the JSON:
                        $pluginpath = '/plugins/vmshopper/ordernumber/ordernumber/';
                        $displayfmt = ($counter=="") ? JText::_('PLG_ORDERNUMBER_COUNTERLIST_GLOBAL') : $counter;
                        $html=array();
                        $html[] = "<tr class='counter_row counter_type_$nrtype'>";
                        $html[] = "  <td class='counter_format'>" . (string)$displayfmt . "</td>";
                        $html[] = "  <td class='counter_value'>" . (string)$value . "</td>";
                        $html[] = "  <td class='counter_buttons'><img src='" .JURI::root(true).$pluginpath . "assets/images/icon-16-edit.png' class='vmordernumber-counter-editbtn vmordernumber-btn' onClick='ajaxEditCounter(this, ".json_encode($nrtype).", ".json_encode($counter).", $value)' /><img src='" . JURI::root(true).$pluginpath . "assets/images/icon-16-delete.png' class='vmordernumber-counter-deletebtn vmordernumber-btn' onClick='ajaxDeleteCounter(this, ".json_encode($nrtype).", ".json_encode($counter).", $value)' /></td>";
                        $html[] = "</tr>";
                        $json['newrow'] = implode("\n", $html);
                    }
                    break;
                case "setCounter":
                    $value = vRequest::getInt('value');
                    $json['success'] = $this->_setCounter($nrtype, $counter, $value);
                    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);
        }
        
        
        /* 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);
        }
    
    }