News:

You may pay someone to create your store, or you visit our seminar and become a professional yourself with the silver certification

Main Menu

Disabling custom fields have bugs, show []d checkbox and show d fields in BE

Started by Kuubs, May 24, 2024, 08:35:00 AM

Previous topic - Next topic

Kuubs

When you remove a custom field, it also removes them from database and thus from the already made orders itself. This makes it so custom fields where the customer paid for is not in the order itself anymore which is not good for order management.

This bug is already a long time here, and I think it's time to smash this one. I think I have the solution at hand.

- Enable the []d checkbox that is also on child products to disable custom fields for the parent
- Make sure to keep including them in the admin backend to not let virtuemart completely remove the custom field

You can set the disabler from the custom field to 1 in the database, this will disable the field but not remove them. BUT, the custom fields also won't display in the backend, which is a huge problem because if you save the parent product (which doens't display the disabled field) it will remove the custom field from the dataabase. My fixes would be to enable the []d checkbox, but also keep showing all the fields in the backend, even if it's disabled.

@Milbo

Milbo

yes, that sounds like a plan. But I have no fast answer how todo it.
Should I fix your bug, please support the VirtueMart project and become a member
______________________________________
Extensions approved by the core team: http://extensions.virtuemart.net/

Kuubs

Quote from: Milbo on May 24, 2024, 08:55:02 AMyes, that sounds like a plan. But I have no fast answer how todo it.
Ah that's a shame. I will try to find if I can somehow fix it myself by checking all the files, I hoped you could point me to the files itself.. I will just chekc the view files first, then the controller files and then the model files. Thanks for the reply.

EDIT:

So I checked the model first that removes the disabled custom fields from the product. I changed some code that does this:


public function storeProductCustomfields($table, $datas, $id) {

vRequest::vmCheckToken('Invalid token in storeProductCustomfields');
//Sanitize id
$id = (int)$id;

//Table whitelist
$tableWhiteList = array('product','category','manufacturer');
if(!in_array($table,$tableWhiteList)) return false;

// Get old IDS
$db = JFactory::getDBO();
$db->setQuery( 'SELECT * FROM `#__virtuemart_'.$table.'_customfields` as `PC` WHERE `PC`.virtuemart_'.$table.'_id ='.$id );

$oldCustomfields = $db->loadAssocList('virtuemart_customfield_id');
$old_customfield_ids = array_keys($oldCustomfields);

if (!empty( $datas['field'])) {

foreach($datas['field'] as $key => $fields){

if(!empty($datas['field'][$key]['virtuemart_product_id']) and (int)$datas['field'][$key]['virtuemart_product_id']!=$id){
//vmdebug('The field is from the parent',$fields);
$fields['override'] = !empty($fields['override'])?(int)$fields['override']:0;
$fields['disabler'] = !empty($fields['disabler'])?(int)$fields['disabler']:0;

if($fields['override']!=0 or $fields['disabler']!=0){
if($fields['override']!=0){
$fields['override'] = $fields['virtuemart_customfield_id'];
}
if($fields['disabler']!=0){
$fields['disabler'] = $fields['virtuemart_customfield_id'];
}

if(!empty($fields['virtuemart_customfield_id']) and empty($oldCustomfields[$fields['virtuemart_customfield_id']]['virtuemart_customfield_id'])){
//vmdebug('It is set now as override, store it as clone, therefore set the virtuemart_customfield_id = 0');
$fields['virtuemart_customfield_id'] = 0;
}
}
else {
//vmdebug('there is no override/disabler set',$fields,$oldCustomfields[$fields['virtuemart_customfield_id']]);
//we do not store customfields inherited by the parent, therefore
$key = array_search($fields['virtuemart_customfield_id'], $old_customfield_ids );
if ($key !== false ){
unset( $old_customfield_ids[ $key ] );
}
continue;
}
}
else {
//vmdebug('The field is from the current product',$fields);
// if(empty($fields['override']) and empty($fields['disabler']) and !empty($fields['virtuemart_customfield_id']) and (!empty($oldCustomfields[$fields['virtuemart_customfield_id']]['disabler']) or !empty($oldCustomfields[$fields['virtuemart_customfield_id']]['override']) )){
// //vmdebug('Remove customfield override/disabler',$fields['virtuemart_customfield_id']);
// $old_customfield_ids[] = $fields['virtuemart_customfield_id'];
// }

//DONT REMOVE DISABLED FROM THE PRODUCT < CHANGES KUUBS
if(empty($fields['override']) and !empty($fields['virtuemart_customfield_id']) or !empty($oldCustomfields[$fields['virtuemart_customfield_id']]['override'])){
//vmdebug('Remove customfield override/disabler',$fields['virtuemart_customfield_id']);
$old_customfield_ids[] = $fields['virtuemart_customfield_id'];
}

}

if(!empty($fields['field_type']) and $fields['field_type']=='C' and !isset($datas['clone']) ){

$cM = VmModel::getModel('custom');
$c = $cM->getCustom($fields['virtuemart_custom_id'],'');

if(!empty($fields['set_matrix'])){

$productModel = VmModel::getModel ('product');
$avail = $productModel->getProductChildIds($id);

foreach($fields['selectoptions'] as $kv => $selectoptions){
if(!empty($selectoptions['values'])){
$values[$kv] = preg_split('/\r\n|\r|\n/', $selectoptions['values'],5);
}
}

vmdebug('my values',$values,$avail);

$parentCombo = $fields['options'][$id];
$myMatrix = array();
$size = 1;
//$parentMatrix = array();
//Yes, also this may get better written, but I am just happy that it works this way.
foreach ($values as $variantKey => $optArray) {
$size = $size * sizeof($optArray);
//$level++;
foreach ($optArray as $option) {
//vmdebug('myMatrix $k=>$option',$option);
if($variantKey==0){
$myMatrix[$option] = null;
//$parentMatrix[$parentCombo[0]] = null;
} else if($variantKey == 1){

$myMatrix = self::writeValuesToKeysAddValueArray($myMatrix,$option);

//$parentMatrix[$parentCombo[0]][$parentCombo[1]]= null;
} else if($variantKey == 2){
foreach($myMatrix as $k1 => &$option1){
$option1 = self::writeValuesToKeysAddValueArray($option1,$option);
}
//$parentMatrix[$parentCombo[0]][$parentCombo[1]][$parentCombo[2]]= null;
} else if($variantKey == 3){
foreach($myMatrix as $k1 => &$option1){
foreach($option1 as $k2 => &$option2){
$option2 = self::writeValuesToKeysAddValueArray($option2,$option);
}
}
//$parentMatrix[$parentCombo[0]][$parentCombo[1]][$parentCombo[2]][$parentCombo[3]]= null;
} else if($variantKey == 4){
foreach($myMatrix as $k1 => &$option1){
foreach($option1 as $k2 => &$option2){
foreach($option2 as $k3 => &$option3){
$option3 = self::writeValuesToKeysAddValueArray($option3,$option);
}
}
}
//$parentMatrix[$parentCombo[0]][$parentCombo[1]][$parentCombo[2]][$parentCombo[3]][$parentCombo[4]]= null;
}
}
}
vmdebug('myMatrix 3',$size,$myMatrix);

vmdebug('myMatrix orig $fields',$fields['options']);
$parentComboSerialized = serialize($parentCombo);
//reset($avail);
for((int)$i=0;$i<$size;$i++){
if(empty($avail)){
$childId = $productModel->createChild($id);
} else {
$childId = $avail[$i];
unset($avail[$i]);
}
vmdebug('$childId after unset'.$i,$childId,$avail);
$combo = self::writeCombos($myMatrix);

if(serialize($combo) == $parentComboSerialized){
$combo = self::writeCombos($myMatrix);
//vmdebug('myMatrix $combo equals $parentCombo',$combo, $parentCombo);
$size--;
} else {
//vmdebug('myMatrix $combo NOT equal',serialize($combo), $parentComboSerialized);
}
$fields['options'][$childId] = $combo;
}
vmdebug('myMatrix $fields',$fields['options'],$avail);

}
//The idea was here to store the images directly. Maybe just the ids.
/*if(!empty($c->withImage)){
$mediaM = VmModel::getModel('media');
$tablePM = $mediaM->getTable('product_medias');
foreach($fields['options'] as $prodId => $lvalue){
$images = $tablePM->load($prodId);
if(isset($images[0])){
$media = $mediaM->createMediaByIds($images[0]);
$fields['images'][$prodId] = $media[0]->getFileUrlThumb();
}

}
}*/

//Set tags on extra customfield
if(!empty($c->sCustomId)){

$sCustId = $c->sCustomId;
$labels = array();
foreach($fields['selectoptions'] as $k => $option){
if (is_object($option)) {
$option = Joomla\Utilities\ArrayHelper::fromObject($option);
}
if($option['voption'] == 'clabels' and !empty($option['clabel'])){
$labels[$k] = $option['clabel'];
}
}

foreach($fields['options'] as $prodId => $lvalue){

if($prodId == $id) continue;
$db->setQuery( 'SELECT `virtuemart_customfield_id` FROM `#__virtuemart_'.$table.'_customfields` as `PC` WHERE `PC`.virtuemart_'.$table.'_id ="'.$prodId.'" AND `virtuemart_custom_id`="'.(int)$sCustId.'" '  );
$strIds = $db->loadColumn();
$i=0;
foreach($lvalue as $k=>$value) {

if(!empty($labels[$k])) {
$ts = array();
$ts['field_type'] = 'S';
$ts['virtuemart_product_id'] = (int)$prodId;
$ts['virtuemart_custom_id'] = (int)$sCustId;
if(isset($strIds[$i])){
$ts['virtuemart_customfield_id'] = (int)$strIds[$i];
unset( $strIds[$i++] );
}
$ts['customfield_value'] = $value;

$tableCustomfields = $this->getTable($table.'_customfields');
$tableCustomfields->bindChecknStore($ts);
}
}

if(count($strIds)>0){
// delete old unused Customfields
$db->setQuery( 'DELETE FROM `#__virtuemart_'.$table.'_customfields` WHERE `virtuemart_customfield_id` in ("'.implode('","', $strIds ).'") ');
$db->execute();
}
}
}
//vmdebug('Executing',$id,$fields);
}

if (!empty($datas['customfield_params'][$key]) and !isset($datas['clone']) ) {
if (array_key_exists( $key,$datas['customfield_params'])) {
$fields = array_merge ((array)$fields, (array)$datas['customfield_params'][$key]);
}
}
$fields['virtuemart_'.$table.'_id'] = $id;

if(!empty($fields['field_type']) and ( $fields['field_type']=='RC' or $fields['field_type']=='R' ) and !isset($datas['clone']) ){
if(is_array($fields['customfield_value'])){
$fields['customfield_value'] = implode(',',$fields['customfield_value']);
}
}

$this->storeProductCustomfield('product', $fields);

$key = array_search($fields['virtuemart_customfield_id'], $old_customfield_ids );
if ($key !== false ) unset( $old_customfield_ids[ $key ] );

}
} else {
//vmdebug('storeProductCustomfields nothing to store');
}

vDispatcher::importVMPlugins('vmcustom');

//vmdebug('Delete $old_customfield_ids',$old_customfield_ids);

if ( count($old_customfield_ids) ) {
// call the plugins to delete their records
foreach ($old_customfield_ids as $old_customfield_id) {
vDispatcher::trigger('plgVmOnCustomfieldRemove', array($oldCustomfields[$old_customfield_id]));
}
// delete old unused Customfields
$db->setQuery( 'DELETE FROM `#__virtuemart_'.$table.'_customfields` WHERE `virtuemart_customfield_id` in ("'.implode('","', $old_customfield_ids ).'") ');
$db->execute();
//vmdebug('Deleted $old_customfield_ids',$old_customfield_ids);
}



if (isset($datas['customfield_params']) and is_array($datas['customfield_params'])) {
foreach ($datas['field'] as $key => $pfield ) {
if($pfield['field_type']=="E" and !empty($pfield['custom_element'])){
vDispatcher::directTrigger( 'vmcustom',$pfield['custom_element'], 'plgVmOnStoreProduct', array($datas, $datas['customfield_params'][$key], $old_customfield_ids, $key ));
}
}
}

}


I removed the part where it adds the product custom field to the old_custom_fields so it doesn't remove them in the query below. I tested it and it seems that it works correctly. But i'm not sure if this is correct. This doesn't show the []d checkbox, but at least it doesn't remove it from the product when you save the product with the disabled checkbox activated (still needs to use direct database to change the disabler value)

@Milbo, do you see anything wrong with this that will break other parts of Virtuemart?