<?php
/**
 * $Id: metadatautil.inc.php 7482 2007-10-23 09:54:35Z kevin_fourie $
 *
 * Utility functions for retrieving information from and managing
 * fieldsets with conditions.
 *
 * KnowledgeTree Open Source Edition
 * Document Management Made Simple
 * Copyright (C) 2004 - 2007 The Jam Warehouse Software (Pty) Limited
 * 
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License version 3 as published by the
 * Free Software Foundation.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * You can contact The Jam Warehouse Software (Pty) Limited, Unit 1, Tramber Place,
 * Blake Street, Observatory, 7925 South Africa. or email info@knowledgetree.com.
 * 
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU General Public License version 3.
 * 
 * In accordance with Section 7(b) of the GNU General Public License version 3,
 * these Appropriate Legal Notices must retain the display of the "Powered by
 * KnowledgeTree" logo and retain the original copyright notice. If the display of the 
 * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
 * must display the words "Powered by KnowledgeTree" and retain the original 
 * copyright notice. 
 * Contributor( s): ______________________________________
 */

require_once(KT_LIB_DIR . "/ktentity.inc");
require_once(KT_LIB_DIR . '/documentmanagement/MetaData.inc');
require_once(KT_LIB_DIR . '/documentmanagement/DocumentField.inc');
require_once(KT_LIB_DIR . '/metadata/valueinstance.inc.php');
require_once(KT_LIB_DIR . '/metadata/fieldset.inc.php');
require_once(KT_LIB_DIR . '/metadata/fieldbehaviour.inc.php');

class KTMetadataUtil {

    // {{{ _getNextForBehaviour
    /**
     * Recursively traverses a fieldset from the behaviour assigned to
     * the master field and thereafter subsequent fields uncovering the
     * fields that should be filled in given the columns/fields that
     * have already be filled in, and providing the values for them
     * based on the behaviours specified by the existing values in their
     * parent fields (in combination with _their_ parent fields).
     */
    function _getNextForBehaviour($oBehaviour, $aCurrentSelections) {
        /*
         * GENERAL GAME PLAN
         *
         * For this behaviour, get the fields that this behaviour
         * affects.  Also get the values that this behaviour prescribes
         * for those fields.
         *
         * Then, for each of the next fields, check if they are already
         * filled in.
         *
         * If not, leave that field in the set of values that need to be
         * filled in, and move on to the next field for this behaviour.
         *
         * If it is filled in, remove the field from the set of values
         * to be filled in.  But add the set of fields and values that
         * the choice of value in this field prescribe (using a
         * recursive call to this function).
         */
        
        $oBehaviour =& KTUtil::getObject('KTFieldBehaviour', $oBehaviour);
        
        $GLOBALS['default']->log->debug('KTMetadataUtil::_getNextForBehaviour, behaviour is ' . $oBehaviour->getId());

        $aValues = KTMetadataUtil::getNextValuesForBehaviour($oBehaviour);
        
        $iFieldId = $oBehaviour->getFieldId();
        $aNextFields = KTMetadataUtil::getChildFieldIds($iFieldId);
                
        $GLOBALS['default']->log->debug('KTMetadataUtil::_getNextForBehaviour, next fields for ' . $iFieldId . ' are: ' . print_r($aNextFields, true)); 

        foreach ($aNextFields as $iThisFieldId) {
            if (!in_array($iThisFieldId, array_keys($aCurrentSelections))) {
                $GLOBALS['default']->log->debug('KTMetadataUtil::_getNextForBehaviour, field ' . $iThisFieldId . ' is not selected');

            } else {
                $GLOBALS['default']->log->debug('KTMetadataUtil::_getNextForBehaviour, field ' . $iThisFieldId . ' is selected');
                unset($aValues[$iThisFieldId]);
                
                // here we need a Lookup field.
                if (!is_a($aCurrentSelections[$iThisFieldId], 'MetaData')) { 
                    $oL = MetaData::getByValueAndDocumentField($aCurrentSelections[$iThisFieldId], $iThisFieldId);
                } else { $oL = $aCurrentSelections[$iThisFieldId]; }
                
                $oInstance = KTValueInstance::getByLookupAndParentBehaviour($oL, $oBehaviour);
                if (is_null($oInstance)) {
                    // somehow an invalid value hit us here.
                    continue;
                }
                
                $GLOBALS['default']->log->debug('KTMetadataUtil::_getNextForBehaviour, instance is ' . print_r($oInstance, true));
                $nextBehaviour = $oInstance->getBehaviourId(); // may be NULL
                if (!is_null($nextBehaviour)) {
                    $oChildBehaviour =& KTFieldBehaviour::get($nextBehaviour);
                    $GLOBALS['default']->log->debug('KTMetadataUtil::_getNextForBehaviour, field ' . $iThisFieldId . ' is not selected');
                
                    $aMyValues = KTMetadataUtil::_getNextForBehaviour($oChildBehaviour, $aCurrentSelections);
                    $GLOBALS['default']->log->debug('KTMetadataUtil::_getNextForBehaviour, values are ' . print_r($aMyValues, true));
                    foreach ($aMyValues as $k => $v) {
                        $aValues[$k] = $v;
                    }
                }
            }
        }

        $GLOBALS['default']->log->debug('KTMetadataUtil::_getNextForBehaviour, final values are ' . print_r($aValues, true));
        
        // now we need to clean this up: remove no-value entries.
        $temp = $aValues;
        foreach ($temp as $k => $v) {
            if (empty($v)) {
                unset($aValues[$k]);
            }
        }
        return $aValues;
    }
    // }}}
    
    // {{{ getNext
    /**
     * Given a set of selected values (aCurrentSelections) for a given
     * field set (iFieldSet), returns an array (possibly empty) with the
     * keys set to newly uncovered fields, and the contents an array of
     * the value instances that are available to choose in those fields.
     *
     * Return value:
     *
     * array(
     *      array('field' => DocumentField, 'values' => array(Metadata, Metadata)),
     *      ...
     * )
     *
     */
    function getNext($oFieldset, $aCurrentSelections) {
        /*
         * GENERAL GAME PLAN
         *
         * Firstly, if there are no current selections, return the
         * master field and all of its values.
         *
         * If there are selections, get the behaviour for the selected
         * value of the master field, and call _getNextForBehaviour on
         * it, passing in the current selections.  This will return an
         * array keyed on field id with values of an array of lookup ids
         * for that field.
         *
         * Convert these to objects and the return format.
         */
        $oFieldset =& KTUtil::getObject('KTFieldset', $oFieldset);
        $GLOBALS['default']->log->debug('KTMetadataUtil::getNext, selections are: ' . print_r($aCurrentSelections, true));

        if (empty($aCurrentSelections)) {
            
            $oField =& DocumentField::get($oFieldset->getMasterFieldId());
            if (PEAR::isError($oField)) {
                return array();
            }
            // FIXME we now need:  the VALUES of MasterField that are assigned to a behaviour. (ONLY)
            return array($oField->getId() => array('field' => $oField, 'values' => MetaData::getEnabledByDocumentField($oField)));
        }

        $oMasterField =& DocumentField::get($oFieldset->getMasterFieldId());
        if (PEAR::isError($oMasterField)) {
            return array();
        }
        $aSelectedFields = array_keys($aCurrentSelections);
        
        $field = $oMasterField->getId();
        $val = $aCurrentSelections[$field];
        $lookup = MetaData::getByValueAndDocumentField($val, $field);

        //var_dump($lookup); exit(0);
        $oValueInstance = KTValueInstance::getByLookupSingle($lookup);
        if (PEAR::isError($oValueInstance) || is_null($oValueInstance) || ($oValueInstance === false)) { return true; } // throw an error
        $aValues = KTMetadataUtil::_getNextForBehaviour($oValueInstance->getBehaviourId(), $aCurrentSelections);
        $GLOBALS['default']->log->debug('KTMetadataUtil::getNext, values are ' . print_r($aValues, true));
        $aReturn = array();
        foreach ($aValues as $iFieldId => $aValueIds) {
            $aTheseValues = array();
            foreach ($aValueIds as $iLookupId) {
                $aTheseValues[$iLookupId] = MetaData::get($iLookupId);
            }
            $aReturn[$iFieldId] = array(
                'field' => DocumentField::get($iFieldId),
                'values' => $aTheseValues,
            );
        }
        return $aReturn;
    }
    // }}}

    // {{{ getMasterField
    /**
     * A conditional fieldset has a single field which is not affected
     * by other values.  This is the master field.  This function gets
     * the master field for the fieldset provided.
     */
    function getMasterField($oFieldset) {
        $oFieldset =& KTUtil::getObject('KTFieldset', $oFieldset);
        if ($oFieldset->getMasterField()) {
            return DocumentField::get($oFieldset->getMasterField());
        }
    }
    // }}}

    // {{{ removeSetsFromDocumentType
    /**
     * Removes a non-generic fieldset from a given document type.
     *
     * (Generic fieldsets are made available to and are required for all
     * (subsequent) documents.  Non-generic fieldsets are made available
     * to and are required for all (subsequent) documents that have a
     * particular document type.)
     */
    function removeSetsFromDocumentType($oDocumentType, $aFieldsets) {
        if (is_object($oDocumentType)) {
            $iDocumentTypeId = $oDocumentType->getId();
        } else {
            $iDocumentTypeId = $oDocumentType;
        }
        if (!is_array($aFieldsets)) {
            $aFieldsets = array($aFieldsets);
        }
        if (empty($aFieldsets)) {
            return true;
        }
        $aIds = array();
        foreach ($aFieldsets as $oFieldset) {
            if (is_object($oFieldset)) {
                $iFieldsetId = $oFieldset->getId();
            } else {
                $iFieldsetId = $oFieldset;
            }
            $aIds[] = $iFieldsetId;
        }
        // Converts to (?, ?, ?) for query
        $sParam = DBUtil::paramArray($aIds);
        $aWhere = KTUtil::whereToString(array(
            array('document_type_id = ?', array($iDocumentTypeId)),
            array("fieldset_id IN ($sParam)", $aIds),
        ));
        $sTable = KTUtil::getTableName('document_type_fieldsets');
        $aQuery = array(
            "DELETE FROM $sTable WHERE {$aWhere[0]}",
            $aWhere[1],
        );
        return DBUtil::runQuery($aQuery);
    }
    // }}}

    // {{{ addSetsToDocumentType
    /**
     * Adds a non-generic fieldset to a given document type.
     *
     * (Generic fieldsets are made available to and are required for all
     * (subsequent) documents.  Non-generic fieldsets are made available
     * to and are required for all (subsequent) documents that have a
     * particular document type.)
     */
    function addSetsToDocumentType($oDocumentType, $aFieldsets) {
        if (is_object($oDocumentType)) {
            $iDocumentTypeId = $oDocumentType->getId();
        } else {
            $iDocumentTypeId = $oDocumentType;
        }
        if (!is_array($aFieldsets)) {
            $aFieldsets = array($aFieldsets);
        }
        $aIds = array();
        foreach ($aFieldsets as $oFieldset) {
            if (is_object($oFieldset)) {
                $iFieldsetId = $oFieldset->getId();
            } else {
                $iFieldsetId = $oFieldset;
            }
            $aIds[] = $iFieldsetId;
        }

        $sTable = KTUtil::getTableName('document_type_fieldsets');
        foreach ($aIds as $iId) {
            $res = DBUtil::autoInsert($sTable, array(
                'document_type_id' => $iDocumentTypeId,
                'fieldset_id' => $iId,
            ));
            if (PEAR::isError($res)) {
                return $res;
            }
        }
        return true;
    }
    // }}}

    // {{{ addFieldOrder
    /**
     * Informs the system that the parent field's values in affects the
     * child field's values in a conditional fieldset.
     */
    function addFieldOrder($oParentField, $oChildField, $oFieldset) {
        $iParentFieldId = KTUtil::getId($oParentField);
        $iChildFieldId = KTUtil::getId($oChildField);
        $iFieldsetId = KTUtil::getId($oFieldset);

        $aOptions = array('noid' => true);
        $sTable = KTUtil::getTableName('field_orders');
        $aValues = array(
            'parent_field_id' => $iParentFieldId,
            'child_field_id' => $iChildFieldId,
            'fieldset_id' => $iFieldsetId,
        );
        return DBUtil::autoInsert($sTable, $aValues, $aOptions);
    }
    // }}}

    // {{{ removeFieldOrdering
    /**
     * Removes all field ordering for the given fieldset.
     */
    function removeFieldOrdering($oFieldset) {
        $iFieldsetId = KTUtil::getId($oFieldset);
        $sTable = KTUtil::getTableName('field_orders');
        $aQuery = array(
            "DELETE FROM $sTable WHERE fieldset_id = ?",
            array($iFieldsetId),
        );
        return DBUtil::runQuery($aQuery);
    }
    // }}}

    // {{{ getParentFieldId
    /**
     * In a conditional fieldset, a field's values is affected by a
     * single parent field's values in an ordered fashion (unless it is
     * the root/master field).  This function gets the field id for the
     * field that this field is affected by.
     */
    function getParentFieldId($oField) {
        $sTable = KTUtil::getTableName('field_orders');
        $aQuery = array("SELECT parent_field_id FROM $sTable WHERE child_field_id = ?",
            array($oField->getId()),
        );
        return DBUtil::getOneResultKey($aQuery, 'parent_field_id');
    }
    // }}}

    // {{{ getChildFieldIds
    /**
     * In a conditional fieldset, a field's values affect other fields'
     * values in an ordered fashion.  This function gets the field ids
     * for the fields that this field affects.
     */
    function getChildFieldIds($oField) {
        $iFieldId = KTUtil::getId($oField);
        $sTable = KTUtil::getTableName('field_orders');
        $aQuery = array("SELECT child_field_id FROM $sTable WHERE parent_field_id = ?",
            array($iFieldId),
        );
        return DBUtil::getResultArrayKey($aQuery, 'child_field_id');
    }
    // }}}

    // {{{ getOrCreateValueInstanceForLookup
    /**
     * Used as a helper function in simple conditional fieldset
     * administration, this function either returns the existing value
     * instance for a lookup, or creates a new value instance and
     * returns that.
     */
    function &getOrCreateValueInstanceForLookup(&$oLookup) {
        $oLookup =& KTUtil::getObject('MetaData', $oLookup);
        $oValueInstance =& KTValueInstance::getByLookupSingle($oLookup);
        if (PEAR::isError($oValueInstance)) {
            return $oValueInstance;
        }
        // If we got a value instance, return it.
        if (!is_null($oValueInstance)) {
            return $oValueInstance;
        }
        // Else create one and return it.
        return KTValueInstance::createFromArray(array(
            'fieldid' => $oLookup->getDocFieldId(),
            'fieldvalueid' => $oLookup->getId(),
        ));
    }
    // }}}

    // {{{ getNextValuesForLookup
    /**
     * Used as a helper for simple conditional fieldset administration,
     * this function returns an array of lookup ids (Metadata->id) for
     * each of the columns/fields that this lookup's column affects.
     *
     * Return value:
     *
     * Associative array keyed by field_id, value is an array of lookup
     * ids.
     *
     * array(
     *      1 => array(1, 2, 3, 4),
     *      ...
     * );
     */
    function getNextValuesForLookup($oLookup) {
        /* 
         * GENERAL GAME PLAN
         *
         * Get the instance attached to the lookup, and and call
         * getNextValuesForBehaviour on its behaviour.
         *
         * If there's no instance or behaviour, return an empty array
         * for each field that the lookup's field affects.
         */
        
        $oLookup =& KTUtil::getObject('MetaData', $oLookup);
        $oInstance =& KTValueInstance::getByLookupSingle($oLookup);
        if (PEAR::isError($oInstance)) {
            $GLOBALS['default']->log->error('KTMetadataUtil::getNextValuesForLookup, got dud instance id, returned: ' . print_r($oInstance, true));
            return $oInstance;
        }
        if (!is_null($oInstance) && $oInstance->getBehaviourId()) {
            // if we have an instance, and we have a behaviour, return
            // the actual values for that behaviour.
            $oBehaviour =& KTFieldBehaviour::get($oInstance->getBehaviourId());
            if (PEAR::isError($oBehaviour)) {
                $GLOBALS['default']->log->error('KTMetadataUtil::getNextValuesForLookup, got dud behaviour id, returned: ' . print_r($oBehaviour, true));
                return $res;
            }
            return KTMetadataUtil::getNextValuesForBehaviour($oBehaviour);
        }
        // No instance or no behaviour, so send an empty array for each
        // field that we affect.
        $aChildFieldIds = KTMetadataUtil::getChildFieldIds($oLookup->getDocFieldId());
        if (PEAR::isError($aChildFieldIds)) {
            $GLOBALS['default']->log->error('KTMetadataUtil::getNextValuesForLookup, getChildFieldIds returned: ' . print_r($aChildFieldIds, true));
            return $res;
        }
        foreach ($aChildFieldIds as $iFieldId) {
            $aValues[$iFieldId] = array();
        }
        return $aValues;
    }
    // }}}

    // {{{ getNextValuesForBehaviour
    /**
     * Given a behaviour, return an array of lookup ids (Metadata->id)
     * that are available for each of the columns/fields that this
     * behaviour's column affects.
     *
     * Return value:
     *
     * Associative array keyed by field_id, value is an array of lookup
     * ids.
     *
     * array(
     *      1 => array(1, 2, 3, 4),
     *      ...
     * );
     */
    function getNextValuesForBehaviour($oBehaviour) {
        $oBehaviour =& KTUtil::getObject('KTFieldBehaviour', $oBehaviour);
        $aValues = array();
        $sTable = KTUtil::getTableName('field_behaviour_options'); 
        $aChildFieldIds = KTMetadataUtil::getChildFieldIds($oBehaviour->getFieldId());
        foreach ($aChildFieldIds as $iFieldId) {
            $aValues[$iFieldId] = array();
        }
        $aQuery = array(
            "SELECT field_id, instance_id FROM $sTable WHERE behaviour_id = ?",
            array($oBehaviour->getId()),
        );
        $aRows = DBUtil::getResultArray($aQuery);
        if (PEAR::isError($aRows)) {
            return $aRows;
        }
        foreach ($aRows as $aRow) {
            $oInstance =& KTValueInstance::get($aRow['instance_id']);
            // need to wean out the disabled values.
            // now get the metadata value.
            $oMetadata = MetaData::get($oInstance->getFieldValueId());
            if (PEAR::isError($oMetadata)) { 
                continue; // invalid link.  bugger.
            }
            if ($oMetadata->getDisabled()) {
                continue; // disabled.
            }
            $aValues[$aRow['field_id']][] = $oInstance->getFieldValueId();
        }
        return $aValues;
    }
    // }}}

    // {{{
    function validateCompleteness($oFieldset) {
        $res = KTMetadataUtil::checkConditionalFieldsetCompleteness($oFieldset);
        // errors, false, or null are all false.        
        if ($res === true) {
            return true;
        } 
        
        return false;
    }
    
    
    // }}}

    // {{{ checkConditionalFieldsetCompleteness
    /**
     * Checks whether a conditional fieldset has the necessary
     * relationships set up to be usable - this means that for each
     * field, no matter how it is reached, there is at least one option
     * available to choose.
     */
    function checkConditionalFieldsetCompleteness($oFieldset) {
        $oFieldset =& KTUtil::getObject('KTFieldset', $oFieldset);

        if ($oFieldset->getIsConditional() == false) {
            // If we're not conditional, we are fine.
            return true;
        }

        /*
         * First, ensure at least one master field item has a behaviour
         * assigned to it.  That allows at least one item in the master
         * field to be chosen.
         */

        $iMasterFieldId = $oFieldset->getMasterFieldId();
        $sTable = KTUtil::getTableName('field_value_instances');
        $sLookupTable = KTUtil::getTableName('metadata_lookup');
        $aQuery = array(
            "SELECT COUNT(FVI.id) AS cnt FROM $sTable AS FVI LEFT JOIN $sLookupTable AS ML ON (FVI.field_value_id = ML.id) WHERE FVI.field_id = ? AND ML.disabled = 0",
            array($iMasterFieldId),
        );
        $iCount = DBUtil::getOneResultKey($aQuery, 'cnt');
        if (PEAR::isError($iCount)) {
            return $iCount;
        }
        $GLOBALS['default']->log->debug("Number of value instances for master field: $iCount");
        if ($iCount == 0) {
            $GLOBALS['default']->log->debug("Number of value instances for master field is zero, failing");
            return PEAR::raiseError(_kt("Master field has no values which are assigned to behaviours."));
        }
        $GLOBALS['default']->log->debug("Number of value instances for master field is positive, continuing");

        // fix for KTS-1023
        // check that each master-field value has a valueinstance assigned.
        $sTable = KTUtil::getTableName('metadata_lookup');
        $aQuery = array(
            "SELECT COUNT(id) AS cnt FROM $sTable WHERE document_field_id = ? AND disabled = 0 ",
            array($iMasterFieldId),
        );
        $iValCount = DBUtil::getOneResultKey($aQuery, 'cnt');
        
        // assumes that there cannot be more than 1 value instance for each master-field-value.
        if ($iValCount != $iCount) {
            return PEAR::raiseError(sprintf(_kt('%d values for the Master Field are not assigned to behaviours.'), ($iValCount - $iCount)));
        }

        /*
         * Plan: For each behaviour that is assigned on the system,
         * ensure that it allows at least one value instance in each of
         * the fields that it needs to affect.
         */

        $sTable = KTUtil::getTableName('field_value_instances');
        $sFieldTable = KTUtil::getTableName('document_fields');
        $aQuery = array(
            "SELECT DISTINCT FV.behaviour_id AS behaviour_id FROM $sTable AS FV INNER JOIN $sFieldTable AS F ON FV.field_id = F.id WHERE F.parent_fieldset = ? AND FV.behaviour_id IS NOT NULL",
            array($oFieldset->getId()),
        );
        $aBehaviourIds = DBUtil::getResultArrayKey($aQuery, 'behaviour_id');
        if (PEAR::isError($aBehaviourIds)) {
            return $aBehaviourIds;
        }

        foreach ($aBehaviourIds as $iBehaviourId) {
            $GLOBALS['default']->log->debug("Checking behaviour id: " . $iBehaviourId);
            $oBehaviour =& KTFieldBehaviour::get($iBehaviourId);
            $sBehaviourName = $oBehaviour->getName();
            $sBehaviourHumanName = $oBehaviour->getHumanName();
            $iParentFieldId = $oBehaviour->getFieldId();
            $GLOBALS['default']->log->debug("   field is " .  $iParentFieldId);
            $aNextFields = KTMetadataUtil::getChildFieldIds($iParentFieldId);
            $oParentField =& DocumentField::get($iParentFieldId);
            $sParentFieldName = $oParentField->getName();
            $GLOBALS['default']->log->debug("   next fields must include " . print_r($aNextFields, true));
            $sTable = KTUtil::getTableName('field_behaviour_options');
            $aQuery = array(
                "SELECT DISTINCT field_id FROM $sTable WHERE behaviour_id = ?",
                array($iBehaviourId),
            );
            $aFields = DBUtil::getResultArrayKey($aQuery, 'field_id');
            $GLOBALS['default']->log->debug("   actual fields are " . print_r($aNextFields, true));
            /*
            foreach ($aNextFields as $iFieldId) {
                if (!in_array($iFieldId, $aFields)) {
                    $GLOBALS['default']->log->debug("   field $iFieldId is not included, failing");
                    $oChildField =& DocumentField::get($iFieldId);
                    $sChildFieldName = $oChildField->getName();
                    return PEAR::raiseError("Child field $sChildFieldName of parent field $sParentFieldName has no selectable values in behaviour $sBehaviourHumanName ($sBehaviourName)");

            }
            */            
        }
        $GLOBALS['default']->log->debug("Got through: passed!");
        return true;
    }
    // }}}
    
    // {{{ synchroniseMetadata
    /**
     * This function takes a list of metadata values and synchronises
     * those values into the values that already exist for the field by
     * adding new values and disabling values that aren't in the new
     * list.
     *
     * XXX: Scalability: This function 
     */
    function synchroniseMetadata($oField, $aNewMetadata) {
        $iFieldId = KTUtil::getId($oField);

        $aCurrentAllValues = Metadata::getValuesByDocumentField($iFieldId);
        $aCurrentEnabledValues = Metadata::getEnabledValuesByDocumentField($iFieldId);
        $aCurrentDisabledValues = Metadata::getDisabledValuesByDocumentField($iFieldId);

        $aToBeAddedValues = array_diff($aNewMetadata, $aCurrentAllValues);
        $aToBeDisabledValues = array_diff($aCurrentEnabledValues, $aNewMetadata);
        $aToBeEnabledValues = array_intersect($aCurrentDisabledValues, $aNewMetadata);

        foreach ($aToBeAddedValues as $sValue) {
            $oMetadata =& Metadata::createFromArray(array(
                'name' => $sValue,
                'docfieldid' => $iFieldId,
            ));
        }

        foreach ($aToBeDisabledValues as $sValue) {
            $oMetadata =& Metadata::getByValueAndDocumentField($sValue, $iFieldId);
            if (PEAR::isError($oMetadata)) {
                var_dump($aToBeDisabledValues);
                var_dump($sValue);
                var_dump($iFieldId);
                var_dump($oMetadata);
                exit(0);
            }
            if (!$oMetadata->getIsStuck()) {
                $oMetadata->updateFromArray(array(
                    'disabled' => true,
                ));
            }
        }

        foreach ($aToBeEnabledValues as $sValue) {
            $oMetadata =& Metadata::getByValueAndDocumentField($sValue, $iFieldId);
            if (!$oMetadata->getIsStuck()) {
                $oMetadata->updateFromArray(array(
                    'disabled' => false,
                ));
            }
        }
    }
    // }}}

    // {{{ fieldsetsForDocument 
    function fieldsetsForDocument($oDocument, $iTypeOverride = null) {
        global $default;
        $oDocument = KTUtil::getObject('Document', $oDocument);
        $iMetadataVersionId = $oDocument->getMetadataVersionId();
        $iDocumentTypeId = $oDocument->getDocumentTypeId();
        if (!is_null($iTypeOverride)) {
            $iDocumentTypeId = $iTypeOverride;
        }

        $sQuery = "SELECT DISTINCT F.id AS fieldset_id " .
            "FROM $default->document_metadata_version_table AS DM INNER JOIN document_fields_link AS DFL ON DM.id = DFL.metadata_version_id " .
            "INNER JOIN $default->document_fields_table AS DF ON DF.ID = DFL.document_field_id " .
            "INNER JOIN $default->fieldsets_table AS F ON F.id = DF.parent_fieldset " .
            "WHERE DM.id = ?" .
            "AND F.disabled = false";
        $aParam = array($iMetadataVersionId);
        $aDocumentFieldsetIds = DBUtil::getResultArrayKey(array($sQuery, $aParam), 'fieldset_id');

        $aGenericFieldsetIds = KTFieldset::getGenericFieldsets(array('ids' => true));
        $aSpecificFieldsetIds = KTFieldset::getForDocumentType($iDocumentTypeId, array('ids' => true));

        $aFieldsetIds = kt_array_merge($aDocumentFieldsetIds, $aGenericFieldsetIds, $aSpecificFieldsetIds);
        $aFieldsetIds = array_unique($aFieldsetIds);
        sort($aFieldsetIds);

        $aRet = array();
        foreach ($aFieldsetIds as $iID) {
            $aRet[] = call_user_func(array('KTFieldset', 'get'), $iID);
        }
        return $aRet;
    }
    // }}}
}

?>
