From 22227d012844f1603687fa8ef837157f2e6598eb Mon Sep 17 00:00:00 2001 From: Reinhold Kainhofer <reinhold@kainhofer.com> Date: Fri, 7 Nov 2014 18:09:41 +0100 Subject: [PATCH] V1.4: Fix changing VM shoppergroups for good (need to use VmTable rather than the model, and reset the cache...) --- Makefile | 2 +- buyer_assign_group.php | 79 +++++++++++++----- buyer_assign_group.xml | 2 +- ..._opentools_vm2_buyer_assign_group_v1.4.zip | Bin 0 -> 9543 bytes 4 files changed, 62 insertions(+), 21 deletions(-) create mode 100644 releases/plg_opentools_vm2_buyer_assign_group_v1.4.zip diff --git a/Makefile b/Makefile index e2a6910..df3d015 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ BASE=buyer_assign_group PLUGINTYPE=vmcustom ZIPBASE=opentools_vm2 -VERSION=1.3 +VERSION=1.4 PLUGINFILES=$(BASE).php $(BASE).script.php $(BASE).xml index.html diff --git a/buyer_assign_group.php b/buyer_assign_group.php index 2fd5456..6a75389 100644 --- a/buyer_assign_group.php +++ b/buyer_assign_group.php @@ -30,7 +30,6 @@ class plgVmCustomBuyer_Assign_Group extends vmCustomPlugin { } else { $this->setConfigParameterable ('customfield_params', $varsToPush); } -// $this->onStoreInstallPluginTable($this->_psType); } /** @@ -46,7 +45,6 @@ class plgVmCustomBuyer_Assign_Group extends vmCustomPlugin { } else { $paramName = 'customfield_params'; } - $html .= '<table class="admintable">'; $html .= VmHTML::row (array('JHTML', '_'),'VMCUSTOM_BUYER_GROUP_JOOMLA', 'access.usergroup', $paramName.'['.$row.'][joomla_groups][]', $field->joomla_groups, ' multiple data-placeholder=" "', false); $html .= VmHTML::row (array('JHTML', '_'),'VMCUSTOM_BUYER_GROUP_JOOMLA_REMOVE', 'access.usergroup', $paramName.'['.$row.'][joomla_groups_remove][]', $field->joomla_groups_remove, ' multiple data-placeholder=" "', true); @@ -111,10 +109,49 @@ class plgVmCustomBuyer_Assign_Group extends vmCustomPlugin { return $SQLfields; } + /* This function does NOT load the whole user, it just loads enough to have the Joomla + groups and the VM shopper groups, i.e. it loads all data from the vmusers and the + vmuser_shoppergroups table, and the Joomla user information, but nothing else */ + function loadUserGroupData($uid) { + if (empty($uid)) return null; + + $db = JFactory::getDBO(); + + $userModel = VmModel::getModel('user'); + $user = $userModel->getTable('vmusers'); + $user->load((int)$uid); + $user->JUser = JUser::getInstance($uid); + + // Add the virtuemart_shoppergroup_ids + $xrefTable = $userModel->getTable('vmuser_shoppergroups'); + $user->shopper_groups = $xrefTable->load($uid); + if(empty($user->shopper_groups)) $user->shopper_groups = array(); + + $site = JFactory::getApplication ()->isSite (); + if ($site) { + $shoppergroupmodel = VmModel::getModel('ShopperGroup'); + $shoppergroupmodel->appendShopperGroups($user->shopper_groups,$user->JUser,$site); + } + $user->shopper_groups = (array)$user->shopper_groups; + + return $user; + } + + /** This functions makes sure the user model clears the cache of the given user, because the + user data was modified directly in the database. In VM2 there is only the last user + cached, so make sure to change the user id by first setting it to 0 and then to the real + uid. Otherwise the cache would not be erased if the uid was the last requested. */ + function clearUserCache($uid) { + $userModel = VmModel::getModel('user'); + $userModel->_data = null; + $userModel->setId(0); // <- Make sure the cache is really, really erased in VM2! + $userModel->setId($uid); + } + function setShopperGroups($uid, $groups) { + $this->clearUserCache($uid); $noError = true; $userModel = VmModel::getModel('user'); - $userModel->setId($uid); $shoppergroupmodel = VmModel::getModel('ShopperGroup'); $defaultgroup = $shoppergroupmodel->getDefault(0); @@ -133,6 +170,7 @@ class plgVmCustomBuyer_Assign_Group extends vmCustomPlugin { vmError('Set shoppergroup '.$error); $noError = false; } + $user_shoppergroups_table->emptyCache(); return $noError; } @@ -181,9 +219,7 @@ class plgVmCustomBuyer_Assign_Group extends vmCustomPlugin { foreach ($orderEntries as $e) { $uid = $e['virtuemart_user_id']; if (!isset($users[$uid])) { - $userModel->setId($uid); - $users[$uid] = $userModel->getUser(); - $users[$uid]->shopper_groups = (array)$users[$uid]->shopper_groups; + $users[$uid] = $this->loadUserGroupData($uid); } $cid = $e['virtuemart_custom_id']; if (!isset($customs[$cid])) { @@ -203,8 +239,8 @@ class plgVmCustomBuyer_Assign_Group extends vmCustomPlugin { $purchased = in_array($order_status, $pstates[$cid]) && !in_array($old_order_status, $pstates[$cid]); $unpurchased = !in_array($order_status, $pstates[$cid]) && in_array($old_order_status, $pstates[$cid]); + $modified = false; if ($purchased) { - $modified = false; if ($e['group_type']==0) { // Joomla User Group if ($e['group_add']==1 && !in_array($e['group_id'], $users[$uid]->JUser->groups)) { // Add to Joomla user group $modified = JUserHelper::addUserToGroup ($uid, $e['group_id']); @@ -220,13 +256,10 @@ class plgVmCustomBuyer_Assign_Group extends vmCustomPlugin { } if ($modified) { $this->setModifiedFlag ($e, $modified); + // clear the user model cache and update update the $users array so the next step in the loop has the correct groups if the user is added to another group + $this->clearUserCache($uid); + $users[$uid] = $this->loadUserGroupData($uid); } - // Update the $users array so the next step in the loop has the correct groups if the user is added to another group - $userModel->_data = null; - $userModel->setId(0); // <- This makes sure to invalidate the cache - $userModel->setId($uid); - $users[$uid] = $userModel->getUser(); - $users[$uid]->shopper_groups = (array)$users[$uid]->shopper_groups; } elseif ($unpurchased && $e['modified']) { // Undo the addition/removal when a product purchase is cancelled if ($e['group_type']==0) { // Joomla User Group @@ -246,11 +279,8 @@ class plgVmCustomBuyer_Assign_Group extends vmCustomPlugin { $this->setModifiedFlag ($e, 0); } // Reload the user data, because shopper groups are NOT automatically updated in the data structure in memory! - $userModel->_data = null; - $userModel->setId(0); // <- This makes sure to invalidate the cache - $userModel->setId($uid); - $users[$uid] = $userModel->getUser(); - $users[$uid]->shopper_groups = (array)$users[$uid]->shopper_groups; + $this->clearUserCache($uid); + $users[$uid] = $this->loadUserGroupData($uid); } catch (Exception $ex) { JFactory::getApplication()->enqueueMessage("ERROR: <pre>".$ex->getMessage()."</pre>", 'info'); @@ -261,7 +291,7 @@ class plgVmCustomBuyer_Assign_Group extends vmCustomPlugin { // VM2.x compat: function plgVmOnUpdateOrder($data, $old_order_status) { - return $this->plgVmOnUpdateOrderShipment($data, $old_order_status); + return $this->updateOrderStatus($data->virtuemart_order_id, $data->order_status, $old_order_status); } // VM3.x function plgVmOnUpdateOrderShipment($data, $old_order_status) { @@ -325,6 +355,17 @@ class plgVmCustomBuyer_Assign_Group extends vmCustomPlugin { $this->updateOrderStatus($order_id, $order['details']['BT']->order_status, ''); } } + +// function plgVmOnSelfCallBE($type, $name, &$output) { +// if ($name != $this->_name || $type != 'vmcustom') return false; +// vmDebug('plgVmOnSelfCallBE'); +// +// $db = JFactory::getDBO(); +// $db->setQuery("SELECT * FROM `".$this->_tablename.";"); +// $val = $db->loadAssocList(); +// +// JFactory::getApplication()->enqueueMessage("<pre>plgVmOnSelfCallBE, type=$type, name=$name, full database contents:".print_r($val,1)."</pre>", 'error'); +// } } // No closing tag \ No newline at end of file diff --git a/buyer_assign_group.xml b/buyer_assign_group.xml index c3f0b0d..6a58807 100644 --- a/buyer_assign_group.xml +++ b/buyer_assign_group.xml @@ -6,7 +6,7 @@ <authorUrl>http://www.open-tools.net/</authorUrl> <copyright>Copyright (C) 2013-2014 Reinhold Kainhofer. All rights reserved.</copyright> <license>http://www.gnu.org/licenses/gpl.html GNU/GPL v3+</license> - <version>1.3</version> + <version>1.4</version> <description>VMCUSTOM_BUYER_GROUP_DESC</description> <files> <filename plugin="buyer_assign_group">buyer_assign_group.php</filename> diff --git a/releases/plg_opentools_vm2_buyer_assign_group_v1.4.zip b/releases/plg_opentools_vm2_buyer_assign_group_v1.4.zip new file mode 100644 index 0000000000000000000000000000000000000000..946926df1e6be9bfb72f3b581ef465dedb88315d GIT binary patch literal 9543 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 literal 0 HcmV?d00001 -- GitLab