diff --git a/fields/vmordernumbercounters.php b/fields/vmordernumbercounters.php index bf1a24d3754c5d0a1742dbd1eff50fcae2db40a2..48972225c58ae6f941f3d4128208fd3d922a9d93 100644 --- a/fields/vmordernumbercounters.php +++ b/fields/vmordernumbercounters.php @@ -2,7 +2,7 @@ defined('_JEXEC') or die(); /** * - * @package VirtueMart + * @package VirtueMart * @subpackage Plugins - Fields * @author Reinhold Kainhofer, Open Tools * @link http://www.open-tools.net @@ -15,53 +15,62 @@ defined('_JEXEC') or die(); */ defined('DS') or define('DS', DIRECTORY_SEPARATOR); +if (!class_exists( 'VmConfig' )) + require(JPATH_ROOT.DS.'administrator'.DS.'components'.DS.'com_virtuemart'.DS.'helpers'.DS.'config.php'); +VmConfig::loadConfig(); class JFormFieldVmOrdernumberCounters extends JFormField { var $_name = 'vmOrdernumberCounters'; protected $countertype; - public function __get($name) - { - switch ($name) - { - case 'countertype': - return $this->$name; - } + public function __get($name) + { + switch ($name) + { + case 'countertype': + return $this->$name; + } - return parent::__get($name); - } + return parent::__get($name); + } - public function __set($name, $value) - { - switch ($name) - { - case 'countertype': - $this->$name = (string) $value; - break; + public function __set($name, $value) + { + switch ($name) + { + case 'countertype': + $this->$name = (string) $value; + break; - default: - parent::__set($name, $value); - } - } + default: + parent::__set($name, $value); + } + } - public function setup(SimpleXMLElement $element, $value, $group = null) - { - $return = parent::setup($element, $value, $group); + public function setup(SimpleXMLElement $element, $value, $group = null) + { + $return = parent::setup($element, $value, $group); - if ($return) { - $this->countertype = (string) $this->element['countertype']; - } + if ($return) { + $this->countertype = (string) $this->element['countertype']; + } - return $return; - } - + return $return; + } + + protected function makeJSTranslationsAvailable() { + JText::script('PLG_ORDERNUMBER_COUNTER_QUERY_NAME'); + } protected function getInput() { + $pluginpath = '/plugins/vmshopper/ordernumber/ordernumber/'; + $doc = JFactory::getDocument()->addStyleSheet(JURI::root(true) . $pluginpath . 'assets/css/ordernumber.css'); + $doc->addScript(JURI::root(true).$pluginpath . 'assets/js/ordernumber.js'); + // Look up the current counters $db = JFactory::getDBO(); - $db->setQuery('SELECT `number_format`, `count` FROM `#__virtuemart_shopper_plg_ordernumber` WHERE `number_type`='.(string)$this->countertype . ' ORDER BY `number_format`;' ); + $db->setQuery('SELECT `number_format`, `count` FROM `#__virtuemart_shopper_plg_ordernumber` WHERE `number_type`='.$db->quote($this->countertype) . ' ORDER BY `number_format`;' ); $counters = $db->loadObjectList(); -// JFactory::getApplication()->enqueueMessage("<pre>All counters: ".print_r($counters, 1)."</pre>", 'error'); // Joomla 2.x uses <li> for the params and float:left on the controls, so we need to add that too $version = new JVersion(); $float = ""; @@ -69,15 +78,29 @@ class JFormFieldVmOrdernumberCounters extends JFormField { $float = "float: left; "; } - $ret = "<table border=\"1\" style=\"border: 1px solid dark-gray; border-collapse: collapse; display: inline-table; $float\"><tr><th>".JText::_('PLG_ORDERNUMBER_COUNTERLIST_HEADER_COUNTER')."</th><th>".JText::_('PLG_ORDERNUMBER_COUNTERLIST_HEADER_VALUE'). "</th></tr>"; - $ret .= "<colgroup><col ><col style=\"text-align: center\" ></colgroup>"; + $html=array(); + $html[] = "<table class=\"vmordernumber-countertable table-striped \" display: inline-table; $float\">"; + $html[] = " <tr>"; + $html[] = " <th class='counter_format'>".JText::_('PLG_ORDERNUMBER_COUNTERLIST_HEADER_COUNTER')."</th>"; + $html[] = " <th class='counter_value'>".JText::_('PLG_ORDERNUMBER_COUNTERLIST_HEADER_VALUE'). "</th>"; + $html[] = " <th class='counter_buttons'></th>"; + $html[] = " </tr>"; + $html[] = " <colgroup><col class='counter_type'><col style=\"text-align: center\" ><col ></colgroup>"; foreach ($counters as $c) { - if ($c->number_format=="") { - $c->number_format = JText::_ ('PLG_ORDERNUMBER_COUNTERLIST_GLOBAL'); + $displayfmt = $c->number_format; + if ($displayfmt=="") { + $displayfmt = JText::_ ('PLG_ORDERNUMBER_COUNTERLIST_GLOBAL'); } - $ret .= "<tr><td>" . (string)$c->number_format . "</td><td>" . (string)$c->count . "</td></tr>"; + $html[] = " <tr class='counter_row counter_type_$this->countertype'>"; + $html[] = " <td class='counter_format'>" . (string)$displayfmt . "</td>"; + $html[] = " <td class='counter_value'>" . (string)$c->count . "</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, $this->countertype, ".json_encode($c->number_format).", $c->count)' /><img src='" . JURI::root(true).$pluginpath . "assets/images/icon-16-delete.png' class='vmordernumber-counter-deletebtn vmordernumber-btn' onClick='ajaxDeleteCounter(this, $this->countertype, ".json_encode($c->number_format).", $c->count)' /></td>"; + $html[] = " </tr>"; } - $ret .= "</table>"; - return $ret; + $html[] = " <tr class='addcounter_row'>"; + $html[] = " <td colspan=3 class='counter_add'><div class='vmordernumber-counter-addbtn vmordernumber-btn' onClick='ajaxAddCounter(this, $this->countertype)'><img src='" . JURI::root(true).$pluginpath . "assets/images/icon-16-new.png' class='vmordernumber-counter-addbtn' />" . JText::_('PLG_ORDERNUMBER_COUNTERLIST_ADD') . "</div></td>"; + $html[] = " </tr>"; + $html[] = "</table>"; + return implode("\n", $html); } } \ No newline at end of file diff --git a/language/en-GB/en-GB.plg_vmshopper_ordernumber.ini b/language/en-GB/en-GB.plg_vmshopper_ordernumber.ini index fc4bdf76edd99e4e71dd0a607582c23b30ebeb17..37a3f73f2ed3162673f3c136b3de142ac13db753 100644 --- a/language/en-GB/en-GB.plg_vmshopper_ordernumber.ini +++ b/language/en-GB/en-GB.plg_vmshopper_ordernumber.ini @@ -48,4 +48,6 @@ PLG_ORDERNUMBER_CUSTOMERNR_PADDING_DESC="Select the minimum number of digits tha PLG_ORDERNUMBER_ORDERNR_ALLCOUNTERS="All counter values" PLG_ORDERNUMBER_COUNTERLIST_GLOBAL="<i>Global counter</i>" PLG_ORDERNUMBER_COUNTERLIST_HEADER_COUNTER="Counter format/name" -PLG_ORDERNUMBER_COUNTERLIST_HEADER_VALUE="Counter value" \ No newline at end of file +PLG_ORDERNUMBER_COUNTERLIST_HEADER_VALUE="Counter value" +PLG_ORDERNUMBER_COUNTERLIST_ADD="Add new counter" +PLG_ORDERNUMBER_COUNTERLIST_EXISTS="Counter '%s' already exists." diff --git a/language/en-GB/en-GB.plg_vmshopper_ordernumber.sys.ini b/language/en-GB/en-GB.plg_vmshopper_ordernumber.sys.ini index fc4bdf76edd99e4e71dd0a607582c23b30ebeb17..37a3f73f2ed3162673f3c136b3de142ac13db753 100644 --- a/language/en-GB/en-GB.plg_vmshopper_ordernumber.sys.ini +++ b/language/en-GB/en-GB.plg_vmshopper_ordernumber.sys.ini @@ -48,4 +48,6 @@ PLG_ORDERNUMBER_CUSTOMERNR_PADDING_DESC="Select the minimum number of digits tha PLG_ORDERNUMBER_ORDERNR_ALLCOUNTERS="All counter values" PLG_ORDERNUMBER_COUNTERLIST_GLOBAL="<i>Global counter</i>" PLG_ORDERNUMBER_COUNTERLIST_HEADER_COUNTER="Counter format/name" -PLG_ORDERNUMBER_COUNTERLIST_HEADER_VALUE="Counter value" \ No newline at end of file +PLG_ORDERNUMBER_COUNTERLIST_HEADER_VALUE="Counter value" +PLG_ORDERNUMBER_COUNTERLIST_ADD="Add new counter" +PLG_ORDERNUMBER_COUNTERLIST_EXISTS="Counter '%s' already exists." diff --git a/ordernumber.php b/ordernumber.php index 61b04ebf0054e6299b6e2e6c98878a165e18f7c9..d4226928851d0e0c52a0d37987609c3c23f4943a 100644 --- a/ordernumber.php +++ b/ordernumber.php @@ -7,7 +7,11 @@ **/ 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('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 { @@ -34,6 +38,56 @@ class plgVmShopperOrdernumber extends vmShopperPlugin { // 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) { + $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`='.(int)$nrtype.' AND `number_format`='.$db->quote($format); + $db->setQuery($q); + $existing = $db->loadResult(); + $count = $existing?$existing:0; + return $count; + } + + function _counterExists($nrtype, $format) { + $db = JFactory::getDBO(); + $q = 'SELECT `count` FROM `'.$this->_tablename.'` WHERE `number_type`='.(int)$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.','.(int)$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`='.(int)$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`='.(int)$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); @@ -64,7 +118,6 @@ class plgVmShopperOrdernumber extends vmShopperPlugin { /* 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(); @@ -141,26 +194,12 @@ class plgVmShopperOrdernumber extends vmShopperPlugin { // If a separate counter format is given after the |, use it, otherwise reuse the number format itself as counter format $parts = explode ("|", $nr); $format = $parts[0]; - $counterfmt = $parts[(count($parts)>1)?1:0]; - + + $counterfmt = ($global==1)?"":$parts[(count($parts)>1)?1:0]; + // Look up the current counter - $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! */ - $counterfmt = $global?"":$db->getEscaped ($counterfmt); - $q1 = 'SELECT `count` FROM `'.$this->_tablename.'` WHERE `number_type`='.(int)$nrtype.' AND `number_format`="'.$counterfmt.'"'; - $db->setQuery($q1); - $existing = $db->loadResult(); - $count = $existing?($existing+1):1; - - // Insert new counter value into the db - if ($existing) { - $q = 'UPDATE `'.$this->_tablename.'` SET `count`= "'.$count.'" WHERE `number_type`='.(int)$nrtype.' AND `number_format`="'.$counterfmt.'"'; - } else { - $q = 'INSERT INTO `'.$this->_tablename.'` (`count`, `number_type`, `number_format`) VALUES ('.(int)$count.','.(int)$nrtype.', "'.$counterfmt.'")'; - } - $db->setQuery( $q ); - $db->query(); + $count = $this->_getCounter($nrtype, $counterfmt) + 1; + $this->_setCounter($nrtype, $counterfmt, $count); // return the format with the counter inserted return str_replace ("#", sprintf('%0' . $padding . 's', $count), $format); @@ -225,22 +264,70 @@ class plgVmShopperOrdernumber extends vmShopperPlugin { } - /** - * 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'); - - $db = JFactory::getDBO(); - $nullDate = $db->getNullDate(); - }*/ + /** + * 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')); + $output = array(); + $output['authorized'] = $authorized; + if (!$authorized) return FALSE; + $action = vRequest::getCmd('action'); + $counter= vRequest::getString('counter'); + $nrtype = vRequest::getInt('nrtype'); + $output['action'] = $action; + $output['success'] = 0; // default: unsuccessfull + switch ($action) { + case "deleteCounter": + $output['success'] = $this->_deleteCounter($nrtype, $counter); + break; + case "addCounter": + $value = vRequest::getInt('value',0); + if ($this->_counterExists($nrtype, $counter)) { + $output['error'] = JText::sprintf('PLG_ORDERNUMBER_COUNTERLIST_EXISTS', $counter); + $output['success'] = false; + } else { + $output['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, $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, $nrtype, ".json_encode($counter).", $value)' /></td>"; + $html[] = "</tr>"; + $output['newrow'] = implode("\n", $html); + } + break; + case "setCounter": + $value = vRequest::getInt('value'); + $output['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'); + $output['messages'] = $msgrenderer->render('Message'); + $document->setType($previoustype); + } + /* 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); - } + public function plgVmOnStoreInstallPluginTable($psType, $name='') { + return $this->onStoreInstallPluginTable($psType, $name); + } } diff --git a/ordernumber/assets/css/ordernumber.css b/ordernumber/assets/css/ordernumber.css new file mode 100644 index 0000000000000000000000000000000000000000..ca337903ddd492f9cf67d6c1d718dec03581f207 --- /dev/null +++ b/ordernumber/assets/css/ordernumber.css @@ -0,0 +1,18 @@ +td.counter_value { + text-align: center; +} + +table.vmordernumber-countertable { + border: 1px solid #888888; +} + +table.vmordernumber-countertable.table-striped tbody > tr:nth-child(odd) > th { + background: #E0E0E0; +} +.vmordernumber-btn { + cursor: pointer; +} + +col.counter_type, th.counter_type, td.counter_type { + display:none; +} \ No newline at end of file diff --git a/ordernumber/assets/images/icon-16-delete.png b/ordernumber/assets/images/icon-16-delete.png new file mode 100644 index 0000000000000000000000000000000000000000..1573413858f535fc32cb0428d3275f639a5f74cc Binary files /dev/null and b/ordernumber/assets/images/icon-16-delete.png differ diff --git a/ordernumber/assets/images/icon-16-edit.png b/ordernumber/assets/images/icon-16-edit.png new file mode 100644 index 0000000000000000000000000000000000000000..b1bcf04bb12fe3186243c8becb73bb0b24795117 Binary files /dev/null and b/ordernumber/assets/images/icon-16-edit.png differ diff --git a/ordernumber/assets/images/icon-16-new.png b/ordernumber/assets/images/icon-16-new.png new file mode 100644 index 0000000000000000000000000000000000000000..fcd6a5a695d6efadc571362896612e17440e9cc1 Binary files /dev/null and b/ordernumber/assets/images/icon-16-new.png differ diff --git a/ordernumber/assets/js/ordernumber.js b/ordernumber/assets/js/ordernumber.js new file mode 100644 index 0000000000000000000000000000000000000000..bee01cade3b3f334cfe8d14e967706237303e569 --- /dev/null +++ b/ordernumber/assets/js/ordernumber.js @@ -0,0 +1,108 @@ +var updateMessages = function(messages, area) { + jQuery( "#system-message-container #system-message div."+area+"-message").remove(); + // Extract the messages from the returned string, add the ordernumber-message class (so the next ajax call + // can remove them again) and then move the messages to the original message container. + // Things are complicated by the fact that no #system-message element exists if no messages were printed so far + var newmessages = jQuery( messages ).find("div.alert").addClass(area+"-message"); + if (!jQuery( "#system-message-container #system-message").length && newmessages.length) { + jQuery( "#system-message-container" ).append( "<div id='system-message'></div>" ); + } + newmessages.appendTo( "#system-message-container #system-message"); +} +var getCounterData = function (btn) { + var row=jQuery(btn).parents("tr.counter_row"); + return { row: row }; +} +var handleJSONResponse = function (json, counter) { + updateMessages(json['messages'], "ordernumber"); + if (!json.authorized) { + alert("You are not authorized to modify order number counters."); + } else if (json.error) { + alert(json.error); + } else { + // TODO: Which other error checks can we do? + } +} +var ajaxEditCounter = function (btn, nrtype, ctr, value) { + var counter = getCounterData(btn); + counter.type=nrtype; + counter.counter=ctr; + counter.value=value; + var value = NaN; + var msgprefix = ""; + while (isNaN(value) && (value != null)) { + value = prompt (msgprefix+"Please enter the new value for the counter '" + counter.counter + "' (current value: " + counter.value + "):", counter.value); + if (value != null) + value = parseInt(value); + if (isNaN(value)) + msgprefix = "You entered an invalid value for the counter.\n\n"; + } + if (value != null) { + jQuery.ajax({ + type: "POST", + cache: false, + dataType: "text", // Read text, but interpret as JSON later in the done method (prevents a warning!) + url: "index.php?option=com_virtuemart&view=plugin&type=vmshopper&name=ordernumber&action=setCounter&format=json", + data: { nrtype: counter.type, counter: counter.counter, value: value }, + success: function( data ) { + var json = data ? jQuery.parseJSON(data) : null; + handleJSONResponse(json, counter); + if (json.success>0) { + jQuery(counter.row).children(".counter_value").text(value); + } else { + alert ("Failed modifying counter "+counter.counter); + } + }, + error: function() { alert ("ERROR: Failed modifying counter "+counter.counter); } + }); + } +} +var ajaxDeleteCounter = function (btn, nrtype, ctr, value) { + var counter = getCounterData(btn); + counter.type=nrtype; + counter.counter=ctr; + counter.value=value; + var proceed = confirm ("Really delete counter '"+counter.counter+"' with value '"+counter.value+"'?"); + if (proceed == true) { + jQuery.ajax({ + type: "POST", + cache: false, + dataType: "text", // Read text, but interpret as JSON later in the done method (prevents a warning!) + url: "index.php?option=com_virtuemart&view=plugin&type=vmshopper&name=ordernumber&action=deleteCounter&format=json", + data: { nrtype: counter.type, counter: counter.counter }, + success: function( data ) { + var json = data ? jQuery.parseJSON(data) : null; + handleJSONResponse(json, counter); + if (json.success>0) { + jQuery(counter.row).fadeOut(1500, function() { $(this).remove(); }); + } else { + alert ("Failed modifying counter "+counter.counter); + } + }, + error: function() { alert ("ERROR: Failed modifying counter "+counter.counter); } + }); + } +} +var ajaxAddCounter = function (btn, nrtype) { + var row = jQuery(btn).parents("tr.addcounter_row"); + var countername = prompt ("Please enter the format/name of the new counter:"); + if (countername != null) { + jQuery.ajax({ + type: "POST", + cache: false, + dataType: "text", // Read text, but interpret as JSON later in the done method (prevents a warning!) + url: "index.php?option=com_virtuemart&view=plugin&type=vmshopper&name=ordernumber&action=addCounter&format=json", + data: { nrtype: nrtype, counter: countername }, + success: function( data ) { + var json = data ? jQuery.parseJSON(data) : null; + handleJSONResponse(json, { type: nrtype, counter: countername, value: 0, row: row }); + if (json.success>0) { + if (json.newrow) { + jQuery(row).before(jQuery(json.newrow)); + } + } + }, + error: function() { alert ("ERROR: Failed adding counter "+countername); } + }); + } +}