diff --git a/lib/browse/browseutil.inc.php b/lib/browse/browseutil.inc.php index b8bea45..1f6f7e5 100644 --- a/lib/browse/browseutil.inc.php +++ b/lib/browse/browseutil.inc.php @@ -186,7 +186,7 @@ class KTBrowseUtil { $aBreadcrumbs = array(); // skip root. - $folder_path_names = array_slice($oFolder->getPathArray(), 1); + $folder_path_names = $oFolder->getPathArray(); $folder_path_ids = array_slice(explode(',', $oFolder->getParentFolderIds()), 1); $parents = count($folder_path_ids); diff --git a/lib/documentmanagement/documentcore.inc.php b/lib/documentmanagement/documentcore.inc.php index 387215d..885450d 100644 --- a/lib/documentmanagement/documentcore.inc.php +++ b/lib/documentmanagement/documentcore.inc.php @@ -194,7 +194,20 @@ class KTDocumentCore extends KTEntity { } } if (!is_null($lastFolder)) { - $this->sFullPath = $lastFolder->getFullPath(); + $this->sFullPath = 'pending'; + if (!is_null($this->getMetadataVersionId())) + { + $metadata = KTDocumentMetadataVersion::get($this->getMetadataVersionId()); + $name =$metadata->getName(); + if ($lastFolder->getId() == 1) + { + $this->sFullPath = $name; + } + else + { + $this->sFullPath = $lastFolder->getFullPath() . '/' . $name; + } + } $this->sParentFolderIds = $lastFolder->getParentFolderIDs(); } return parent::_fieldValues(); diff --git a/lib/foldermanagement/Folder.inc b/lib/foldermanagement/Folder.inc index 415b159..20c55d0 100644 --- a/lib/foldermanagement/Folder.inc +++ b/lib/foldermanagement/Folder.inc @@ -183,8 +183,21 @@ class Folder extends KTEntity { } function _fieldValues () { - $this->sFullPath = Folder::generateFolderPath($this->iParentID); - $this->sParentFolderIDs = $this->generateFolderIDs($this->iParentID); + if ($this->getId() == 1) + { + $this->sFullPath = null; + $this->sParentFolderIDs = null; + } + else + { + $parent = Folder::get($this->iParentID); + $this->sFullPath = $parent->getFullPath(); + if (!empty($this->sFullPath)) $this->sFullPath .= '/'; + $this->sFullPath .= $this->sName; + $this->sParentFolderIDs = $parent->getParentFolderIDs(); + if (!empty($this->sParentFolderIDs)) $this->sParentFolderIDs .= ','; + $this->sParentFolderIDs .= $this->iParentID; + } return parent::_fieldValues(); return array( 'name' => $this->sName, @@ -331,6 +344,8 @@ class Folder extends KTEntity { */ function getFolderPathNamesAsArray($iFolderID) { global $default; + + $oFolder = Folder::get($iFolderID); $aPathArray = array(); if ($oFolder) { @@ -340,7 +355,8 @@ class Folder extends KTEntity { } else { $aPathArray = array($oFolder->getFullPath()); } - $aPathArray[count($aPathArray)] = $oFolder->getName(); + + //$aPathArray[count($aPathArray)] = $oFolder->getName(); } else { $aPathArray = array($oFolder->getName()); } diff --git a/lib/foldermanagement/compressionArchiveUtil.inc.php b/lib/foldermanagement/compressionArchiveUtil.inc.php index 85796ce..394429b 100644 --- a/lib/foldermanagement/compressionArchiveUtil.inc.php +++ b/lib/foldermanagement/compressionArchiveUtil.inc.php @@ -76,6 +76,30 @@ class ZipFolder { $this->aReplaceValues = array_values($aReplace); } + /** + * Return the full path + * + * @param mixed $oFolderOrDocument May be a Folder or Document + */ + function getFullFolderPath($oFolder) + { + static $sRootFolder = null; + + if (is_null($sRootFolder)) + { + $oRootFolder = Folder::get(1); + $sRootFolder = $oRootFolder->getName(); + } + + $sFullPath = $sRootFolder . '/'; + $sFullPath .= $oFolder->getFullPath(); + + + if (substr($sFullPath,-1) == '/') $sFullPath = substr($sFullPath,0,-1); + return $sFullPath; + } + + /** * Add a document to the zip file */ @@ -84,7 +108,7 @@ class ZipFolder { $oFolder = Folder::get($oDocument->getFolderID()); } - $sDocPath = $oFolder->getFullPath().'/'.$oFolder->getName(); + $sDocPath = $this->getFullFolderPath($oFolder); $sDocName = $oDocument->getFileName(); $sParentFolder = str_replace('<', '', str_replace('', '', sprintf('%s/%s', $this->sTmpPath, $sDocPath)))); @@ -114,7 +138,7 @@ class ZipFolder { * Add a folder to the zip file */ function addFolderToZip($oFolder) { - $sFolderPath = $oFolder->getFullPath().'/'.$oFolder->getName().'/'; + $sFolderPath = $this->getFullFolderPath($oFolder) .'/'; $sParentFolder = str_replace('<', '', str_replace('', '', sprintf('%s/%s', $this->sTmpPath, $sFolderPath)))); $newDir = $this->sTmpPath; $sFullPath = str_replace('<', '', str_replace('', '', $this->_convertEncoding($sFolderPath, true)))); diff --git a/lib/foldermanagement/folderutil.inc.php b/lib/foldermanagement/folderutil.inc.php index 6efbcec..e985d92 100644 --- a/lib/foldermanagement/folderutil.inc.php +++ b/lib/foldermanagement/folderutil.inc.php @@ -57,10 +57,10 @@ class KTFolderUtil { } $oStorage =& KTStorageManagerUtil::getSingleton(); $oFolder =& Folder::createFromArray(array( - 'name' => ($sFolderName), - 'description' => ($sFolderName), - 'parentid' => $oParentFolder->getID(), - 'creatorid' => $oUser->getID(), + 'name' => ($sFolderName), + 'description' => ($sFolderName), + 'parentid' => $oParentFolder->getID(), + 'creatorid' => $oUser->getID(), )); if (PEAR::isError($oFolder)) { return $oFolder; @@ -100,12 +100,26 @@ class KTFolderUtil { $oSubscriptionEvent = new SubscriptionEvent(); $oSubscriptionEvent->AddFolder($oFolder, $oParentFolder); - KTFolderUtil::updateSearchableText($oFolder); - return $oFolder; } function move($oFolder, $oNewParentFolder, $oUser, $sReason=null) { + if ($oFolder->getId() == 1) + { + return PEAR::raiseError(_kt('Cannot move root folder!')); + } + if ($oFolder->getParentID() == $oNewParentFolder->getId()) + { + // moved! done. + return; + } + $sFolderParentIds = $oFolder->getParentFolderIDs(); + $sNewFolderParentIds = $oNewParentFolder->getParentFolderIDs(); + + if (strpos($sNewFolderParentIds, "$sFolderParentIds,") === 0) + { + return PEAR::raiseError(_kt('Cannot move folder into a descendant folder!')); + } if (KTFolderUtil::exists($oNewParentFolder, $oFolder->getName())) { return PEAR::raiseError(_kt('Folder with the same name already exists in the new parent folder')); } @@ -134,51 +148,42 @@ class KTFolderUtil { $bChangePermissionObject = true; } - // First, deal with SQL, as it, at least, is guaranteed to be atomic $table = 'folders'; - if ($oNewParentFolder->getId() == 1) { - $sNewParentFolderPath = $oNewParentFolder->getName(); - $sNewParentFolderIds = ''; - } else { - $sNewParentFolderPath = sprintf("%s/%s", $oNewParentFolder->getFullPath(), $oNewParentFolder->getName()); - $sNewParentFolderIds = sprintf("%s,%s", $oNewParentFolder->getParentFolderIDs(), $oNewParentFolder->getID()); + if ($oNewParentFolder->getId() == 1) + { + $sNewFullPath = $oFolder->getName(); + $sNewParentFolderIds = "1"; } - - $sOldPath = $oFolder->getFullPath(); - - if ($oNewParentFolder->getId() == 1) { - } else { - $sNewParentFolderPath = sprintf("%s/%s", $oNewParentFolder->getFullPath(), $oNewParentFolder->getName()); + else + { + $sNewFullPath = $oNewParentFolder->getFullPath() . '/' . $oFolder->getName(); + $sNewParentFolderIds = $oNewParentFolder->getParentFolderIDs() . ',' . $oNewParentFolder->getID(); } // Update the moved folder first... $sQuery = "UPDATE $table SET full_path = ?, parent_folder_ids = ?, parent_id = ? WHERE id = ?"; $aParams = array( - sprintf("%s", $sNewParentFolderPath), - $sNewParentFolderIds, - $oNewParentFolder->getID(), - $oFolder->getID(), + $sNewFullPath, + $sNewParentFolderIds, + $oNewParentFolder->getID(), + $oFolder->getID(), ); $res = DBUtil::runQuery(array($sQuery, $aParams)); if (PEAR::isError($res)) { return $res; } - if ($oFolder->getId() == 1) { - $sOldFolderPath = $oFolder->getName(); - } else { - $sOldFolderPath = sprintf("%s/%s", $oFolder->getFullPath(), $oFolder->getName()); - } + $sOldFolderPath = $oFolder->getFullPath(); $sQuery = "UPDATE $table SET full_path = CONCAT(?, SUBSTRING(full_path FROM ?)), parent_folder_ids = CONCAT(?, SUBSTRING(parent_folder_ids FROM ?)) WHERE full_path LIKE ?"; $aParams = array( - sprintf("%s", $sNewParentFolderPath), - strlen($oFolder->getFullPath()) + 1, - $sNewParentFolderIds, - strlen($oFolder->getParentFolderIDs()) + 1, - sprintf("%s%%", $sOldFolderPath), + $sNewFullPath, + strlen($oFolder->getFullPath()) + 1, + $sNewParentFolderIds, + strlen($oFolder->getParentFolderIDs()) + 1, + "$sOldFolderPath%" ); $res = DBUtil::runQuery(array($sQuery, $aParams)); if (PEAR::isError($res)) { @@ -187,13 +192,7 @@ class KTFolderUtil { $table = 'documents'; $sQuery = "UPDATE $table SET full_path = CONCAT(?, SUBSTRING(full_path FROM ?)), parent_folder_ids = CONCAT(?, SUBSTRING(parent_folder_ids FROM ?)) WHERE full_path LIKE ?"; - $aParams = array( - sprintf("%s", $sNewParentFolderPath), - strlen($oFolder->getFullPath()) + 1, - $sNewParentFolderIds, - strlen($oFolder->getParentFolderIDs()) + 1, - sprintf("%s%%", $sOldFolderPath), - ); + // use same $aParams as above $res = DBUtil::runQuery(array($sQuery, $aParams)); if (PEAR::isError($res)) { return $res; @@ -219,6 +218,7 @@ class KTFolderUtil { Document::clearAllCaches(); Folder::clearAllCaches(); + $GLOBALS["_OBJECTCACHE"] = array(); if ($bChangePermissionObject) { $aOptions = array( @@ -236,21 +236,20 @@ class KTFolderUtil { // First, deal with SQL, as it, at least, is guaranteed to be atomic $table = "folders"; - $sQuery = "UPDATE $table SET full_path = CONCAT(?, SUBSTRING(full_path FROM ?)) WHERE full_path LIKE ? OR full_path = ?"; - if ($oFolder->getId() == 1) { $sOldPath = $oFolder->getName(); $sNewPath = $sNewName; } else { - $sOldPath = sprintf("%s/%s", $oFolder->getFullPath(), $oFolder->getName()); - $sNewPath = sprintf("%s/%s", $oFolder->getFullPath(), $sNewName); - + $sOldPath = $oFolder->getFullPath(); + $sNewPath = dirname($oFolder->getFullPath()) . '/' . $sNewName; } + + $sQuery = "UPDATE $table SET full_path = CONCAT(?, SUBSTRING(full_path FROM ?)) WHERE full_path LIKE ? OR full_path = ?"; $aParams = array( - sprintf("%s", $sNewPath), - strlen($sOldPath) + 1, - $sOldPath.'/%', - $sOldPath, + "$sNewPath/", + strlen($sOldPath) + 2, + $sOldPath.'/%', + $sOldPath, ); $res = DBUtil::runQuery(array($sQuery, $aParams)); @@ -272,6 +271,7 @@ class KTFolderUtil { } $oFolder->setName($sNewName); + $oFolder->setDescription($sNewName); $res = $oFolder->update(); $oTransaction = KTFolderTransaction::createFromArray(array( @@ -285,8 +285,6 @@ class KTFolderUtil { return $oTransaction; } - KTFolderUtil::updateSearchableText($oFolder); - Document::clearAllCaches(); Folder::clearAllCaches(); @@ -436,7 +434,8 @@ class KTFolderUtil { DBUtil::startTransaction(); - while (!empty($aRemainingFolders) && $copyAll) { + while (!empty($aRemainingFolders) && $copyAll) + { $iFolderId = array_pop($aRemainingFolders); $oFolder = Folder::get($iFolderId); if (PEAR::isError($oFolder) || ($oFolder == false)) { @@ -496,7 +495,7 @@ class KTFolderUtil { $aRow['description'] = $sDestFolderName; $aRow['parent_id'] = $oDestFolder->getId(); $aRow['parent_folder_ids'] = sprintf('%s,%s', $oDestFolder->getParentFolderIDs(), $oDestFolder->getId()); - $aRow['full_path'] = sprintf('%s/%s', $oDestFolder->getFullPath(), $oDestFolder->getName()); + $aRow['full_path'] = $oDestFolder->getFullPath() . '/' . $aRow['name']; $id = DBUtil::autoInsert($sTable, $aRow); if (PEAR::isError($id)) { @@ -530,7 +529,7 @@ class KTFolderUtil { $sPrevParentId = $aRow['parent_id']; $aRow['parent_id'] = $aFolderMap[$aRow['parent_id']]['parent_id']; $aRow['parent_folder_ids'] = sprintf('%s,%s', $aFolderMap[$sPrevParentId]['parent_folder_ids'], $aRow['parent_id']); - $aRow['full_path'] = sprintf('%s/%s', $aFolderMap[$sPrevParentId]['full_path'], $aFolderMap[$sPrevParentId]['name']); + $aRow['full_path'] = sprintf('%s/%s', $aFolderMap[$sPrevParentId]['full_path'], $aRow['name']); $id = DBUtil::autoInsert($sTable, $aRow); if (PEAR::isError($id)) { @@ -573,35 +572,6 @@ class KTFolderUtil { return true; } - - function updateSearchableText($oFolder) { - - // NEW SEARCH - - return; - - // very simple function to rebuild the searchable text for this - // folder. - - // MyISAM table for fulltext index - no transactions. - - // get the folder text - // XXX replace this with a trigger / producer set. - $sSearchableText = $oFolder->getName(); - - // do the update. - $iFolderId = KTUtil::getId($oFolder); - $sTable = KTUtil::getTableName('folder_searchable_text'); - $aDelete = array( - "folder_id" => $iFolderId, - ); - DBUtil::whereDelete($sTable, $aDelete); - $aInsert = array( - "folder_id" => $iFolderId, - "folder_text" => $sSearchableText, - ); - return DBUtil::autoInsert($sTable, $aInsert, array('noid' => true)); - } } ?> diff --git a/lib/upgrades/UpgradeFunctions.inc.php b/lib/upgrades/UpgradeFunctions.inc.php index 7ef1ca3..cb0b904 100644 --- a/lib/upgrades/UpgradeFunctions.inc.php +++ b/lib/upgrades/UpgradeFunctions.inc.php @@ -35,6 +35,8 @@ * */ +//debugger_start_debug(); + require_once(KT_LIB_DIR . '/upgrades/Ini.inc.php'); require_once(KT_DIR . '/plugins/ktcore/scheduler/scheduler.php'); require_once(KT_LIB_DIR . '/database/schema.inc.php'); @@ -57,7 +59,7 @@ class UpgradeFunctions { '3.1.5' => array('upgradeSavedSearches'), '3.1.6.3' => array('cleanupGroupMembership'), '3.5.0' => array('cleanupOldKTAdminVersionNotifier', 'updateConfigFile35', 'registerIndexingTasks'), - '3.5.2' => array('dropForeignKeys','dropPrimaryKeys','dropIndexes','createPrimaryKeys','createForeignKeys','createIndexes'), + '3.5.2' => array('setStorageEngine','dropForeignKeys','dropPrimaryKeys','dropIndexes','createPrimaryKeys','createForeignKeys','createIndexes', 'removeSlashesFromObjects'), ); var $descriptions = array( @@ -80,12 +82,14 @@ class UpgradeFunctions { 'cleanupOldKTAdminVersionNotifier' => 'Cleanup any old files from the old KTAdminVersionNotifier', 'updateConfigFile35' => 'Update the config.ini file for 3.5', 'registerIndexingTasks'=>'Register the required indexing background tasks', + 'setStorageEngine'=>'Recreate db integrity: Set storage engine to InnoDB for transaction safety', 'dropForeignKeys'=>'Recreate db integrity: Drop foreign keys on the database', 'dropPrimaryKeys'=>'Recreate db integrity:Drop primary keys on the database', 'dropIndexes'=>'Recreate db integrity:Drop indexes on the database', 'createPrimaryKeys'=>'Recreate db integrity:Create primary keys on the database', 'createForeignKeys'=>'Recreate db integrity:Create foreign keys on the database', - 'createIndexes'=>'Recreate db integrity:Create indexes on the database' + 'createIndexes'=>'Recreate db integrity:Create indexes on the database', + 'removeSlashesFromObjects'=>'Remove slashes from documents and folders' ); var $phases = array( "setPermissionFolder" => 1, @@ -95,12 +99,13 @@ class UpgradeFunctions { "fixUnits" => 1, 'applyDiscussionUpgrade' => -1, 'fixDocumentRoleAllocation' => -1, - 'dropForeignKeys'=>1, - 'dropPrimaryKeys'=>2, - 'dropIndexes'=>3, - 'createPrimaryKeys'=>4, - 'createForeignKeys'=>5, - 'createIndexes'=>6, + 'setStorageEngine'=>1, + 'dropForeignKeys'=>2, + 'dropPrimaryKeys'=>3, + 'dropIndexes'=>4, + 'createPrimaryKeys'=>5, + 'createForeignKeys'=>6, + 'createIndexes'=>7, ); function dropForeignKeys() @@ -139,6 +144,55 @@ class UpgradeFunctions { $schemautil->createIndexes(); } + // {{{ setStorageEngine + function setStorageEngine() + { + $schemautil = KTSchemaUtil::getSingleton(); + $schemautil->setTablesToInnoDb(); + } + // }}} + + function _removeSlashesFromFolders($folderid, $name, $full_path, $folder_ids) + { + $name = str_replace(array('/','\\'),array('-','-'), $name); + + // get folders + $sql = "select id, name from folders where parent_id=$folderid"; + $ids = DBUtil::getResultArray($sql); + + // set to the latest values + $parent_ids = implode(',', $folder_ids); + if (empty($parent_ids)) $parent_ids = null; + if (empty($full_path)) $full_path = null; + + $full_path = (empty($full_path))?$name:($full_path . '/' . $name); + $folder_ids [] = $folderid; + + $sql = "update folders set name=?,description=?, full_path=?, parent_folder_ids=? where id=?"; + DBUtil::runQuery(array($sql, array($name,$name, $full_path, $parent_ids, $folderid))); + + // update documents + $sql = "update documents set full_path=?, parent_folder_ids=? where folder_id=?"; + DBUtil::runQuery(array($sql, array($full_path, $parent_ids, $folderid))); + + + // recurse subfolders + foreach($ids as $row) + { + $id = $row['id']; + $name = $row['name']; + UpgradeFunctions::_removeSlashesFromFolders($id, $name, $full_path, $folder_ids); + } + } + + function removeSlashesFromObjects() + { + $GLOBALS["_OBJECTCACHE"] = array(); + + UpgradeFunctions::_removeSlashesFromFolders(1, '', array(), array()); + DBUtil::commit(); + } + // {{{ _setPermissionFolder function _setPermissionFolder($iFolderId) { diff --git a/plugins/ktcore/KTWidgets.php b/plugins/ktcore/KTWidgets.php index 3226c47..dbea756 100644 --- a/plugins/ktcore/KTWidgets.php +++ b/plugins/ktcore/KTWidgets.php @@ -1,36 +1,36 @@ . - * + * * 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 + * 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. + * must display the words "Powered by KnowledgeTree" and retain the original + * copyright notice. * Contributor( s): ______________________________________ */ @@ -51,24 +51,24 @@ class KTCoreHiddenWidget extends KTWidget { class KTCoreFileWidget extends KTWidget { var $sNamespace = 'ktcore.widgets.file'; var $sTemplate = 'ktcore/forms/widgets/file'; - + function wrapName($outer) { $this->sName = sprintf('_kt_attempt_unique_%s', $this->sName); // we don't have access via "wrap" when processing, so we can't actually // wrap. just don't use a lot of names } - + function process($data){ $tname = sprintf('_kt_attempt_unique_%s', $this->sName); return array($this->sBasename => $_FILES[$tname]); } - + function getValidators() { if (!$this->bAutoValidate) { return null; } - - $oVF =& KTValidatorFactory::getSingleton(); + + $oVF =& KTValidatorFactory::getSingleton(); return $oVF->get('ktcore.validators.requiredfile', array( 'test' => sprintf('_kt_attempt_unique_%s', $this->sName), 'basename' => $this->sBasename, @@ -85,27 +85,27 @@ class KTCoreTextWidget extends KTWidget { class KTCoreReasonWidget extends KTWidget { var $sNamespace = 'ktcore.widgets.reason'; var $sTemplate = 'ktcore/forms/widgets/text'; - + function configure($aOptions) { $res = parent::configure($aOptions); if (PEAR::isError($res)) { return $res; } - + // FIXME make required *either* per-action property // FIXME or a global pref. $global_required_default = true; $this->bRequired = KTUtil::arrayGet($aOptions, 'required', $global_required_default, false); - + $this->aOptions['cols'] = KTUtil::arrayGet($aOptions, 'cols', 60); - $this->aOptions['rows'] = KTUtil::arrayGet($aOptions, 'rows', 3); + $this->aOptions['rows'] = KTUtil::arrayGet($aOptions, 'rows', 3); } } class KTCoreBooleanWidget extends KTWidget { var $sNamespace = 'ktcore.widgets.boolean'; var $sTemplate = 'ktcore/forms/widgets/boolean'; - + function setDefault($mValue) { $this->value = ($mValue == true); } @@ -114,20 +114,20 @@ class KTCoreBooleanWidget extends KTWidget { class KTCorePasswordWidget extends KTWidget { var $sNamespace = 'ktcore.widgets.password'; var $sTemplate = 'ktcore/forms/widgets/password'; - + var $bConfirm = false; var $sConfirmDescription; - + function configure($aOptions) { $res = parent::configure($aOptions); if (PEAR::isError($res)) { return $res; } - + $this->bConfirm = KTUtil::arrayGet($aOptions, 'confirm', false); $this->sConfirmDescription = KTUtil::arrayGet($aOptions, 'confirm_description'); } - + function process($raw_data) { // since we're essentially a string, pass *that* out as the primary // but we also might want to confirm, and if so we use a private name @@ -135,7 +135,7 @@ class KTCorePasswordWidget extends KTWidget { if ($this->bConfirm) { $res['_password_confirm_' . $this->sBasename] = array( 'base' => $raw_data[$this->sBasename]['base'], - 'confirm' => $raw_data[$this->sBasename]['confirm'], + 'confirm' => $raw_data[$this->sBasename]['confirm'], ); $res[$this->sBasename] = $raw_data[$this->sBasename]['base']; } else { @@ -143,10 +143,10 @@ class KTCorePasswordWidget extends KTWidget { } return $res; } - + function getValidators() { if (!$this->bAutoValidate) { - return null; + return null; } $oVF =& KTValidatorFactory::getSingleton(); @@ -156,7 +156,7 @@ class KTCorePasswordWidget extends KTWidget { 'test' => $this->sOrigname, 'basename' => $this->sBasename )); - + return $val; } } @@ -164,53 +164,53 @@ class KTCorePasswordWidget extends KTWidget { class KTCoreSelectionWidget extends KTWidget { var $sNamespace = 'ktcore.widgets.selection'; - + var $bMulti = false; // multiselection var $USE_SIMPLE = 10; // point at which to switch to a dropdown/multiselect var $bUseSimple; // only use checkboxes, regardless of size - + var $aVocab; - + var $sEmptyMessage; - + var $_valuesearch; - + function configure($aOptions) { $res = parent::configure($aOptions); if (PEAR::isError($res)) { return $res; } - + $this->bUseSimple = KTUtil::arrayGet($aOptions, 'simple_select', null, false); $this->bMulti = KTUtil::arrayGet($aOptions, 'multi', false); - + $this->aVocab = (array) KTUtil::arrayGet($aOptions, 'vocab'); - $this->sEmptyMessage = KTUtil::arrayGet($aOptions, 'empty_message', + $this->sEmptyMessage = KTUtil::arrayGet($aOptions, 'empty_message', _kt('No options available for this field.')); } - + function getWidget() { // very simple, general purpose passthrough. Chances are this is sufficient, // just override the template being used. - $bHasErrors = false; + $bHasErrors = false; if (count($this->aErrors) != 0) { $bHasErrors = true; } - // at this last moment we pick the template to use + // at this last moment we pick the template to use $total = count($this->aVocab); if ($this->bUseSimple === true) { - $this->sTemplate = 'ktcore/forms/widgets/simple_selection'; + $this->sTemplate = 'ktcore/forms/widgets/simple_selection'; } else if ($this->bUseSimple === false) { - $this->sTemplate = 'ktcore/forms/widgets/selection'; + $this->sTemplate = 'ktcore/forms/widgets/selection'; } else if (is_null($this->bUseSimple) && ($total <= $this->USE_SIMPLE)) { $this->sTemplate = 'ktcore/forms/widgets/simple_selection'; } else { $this->sTemplate = 'ktcore/forms/widgets/selection'; } - - $oTemplating =& KTTemplating::getSingleton(); + + $oTemplating =& KTTemplating::getSingleton(); $oTemplate = $oTemplating->loadTemplate($this->sTemplate); - // have to do this here, and not in "configure" since it breaks + // have to do this here, and not in "configure" since it breaks // entity-select. $unselected = KTUtil::arrayGet($this->aOptions, 'unselected_label'); if (!empty($unselected)) { @@ -222,7 +222,7 @@ class KTCoreSelectionWidget extends KTWidget { foreach ($this->aVocab as $k => $v) { $vocab[$k] = $v; } - + $this->aVocab = $vocab; // make sure its the selected one if there's no value specified. @@ -239,7 +239,7 @@ class KTCoreSelectionWidget extends KTWidget { $this->_valuesearch[$v] = true; } } - + $aTemplateData = array( 'context' => $this, 'name' => $this->sName, @@ -252,7 +252,7 @@ class KTCoreSelectionWidget extends KTWidget { ); return $oTemplate->render($aTemplateData); } - + function selected($lookup) { if ($this->bMulti) { return $this->_valuesearch[$lookup]; @@ -260,38 +260,38 @@ class KTCoreSelectionWidget extends KTWidget { return ($this->value == $lookup); } } - + function process($raw_data) { return array($this->sBasename => $raw_data[$this->sBasename]); - } + } } // this happens so often, its worth creating a util function for it class KTCoreEntitySelectionWidget extends KTCoreSelectionWidget { var $sNamespace = 'ktcore.widgets.entityselection'; - + var $sIdMethod; var $sLabelMethod; - + function configure($aOptions) { $res = parent::configure($aOptions); - if (PEAR::isError($res)) { + if (PEAR::isError($res)) { return $res; } - + // the selection widget's configure method has already setup almost - // all the vars we need. we have one utility here, where you pass + // all the vars we need. we have one utility here, where you pass // in a list of existing entities that match the query, and we work // from there. - + $this->sIdMethod = KTUtil::arrayGet($aOptions, 'id_method', 'getId'); $this->sLabelMethod = KTUtil::arrayGet($aOptions, 'label_method'); if (empty($this->sLabelMethod)) { return PEAR::raiseError(_kt('No label method specified.')); } $existing_entities = (array) KTUtil::arrayGet($aOptions, 'existing_entities'); - - // now we construct the "value" array from this set + + // now we construct the "value" array from this set // BUT ONLY IF WE DON'T HAVE A "VALUE" array. if (empty($this->value)) { $this->value = array(); @@ -299,7 +299,7 @@ class KTCoreEntitySelectionWidget extends KTCoreSelectionWidget { $this->value[] = call_user_func(array(&$oEntity, $this->sIdMethod)); } } - + // we next walk the "vocab" array, constructing a new one based on the // functions passed in so far. $new_vocab = array(); @@ -316,7 +316,7 @@ class KTCoreEntitySelectionWidget extends KTCoreSelectionWidget { class KTDescriptorSelectionWidget extends KTWidget { var $sNamespace = 'ktcore.widgets.descriptorselection'; var $sTemplate = 'ktcore/forms/widgets/descriptor'; - + var $aJavascript = array('resources/js/jsonlookup.js'); function configure($aOptions) { @@ -324,24 +324,24 @@ class KTDescriptorSelectionWidget extends KTWidget { if (PEAR::isError($res)) { return $res; } - - + + } - + function getWidget() { - $oTemplating =& KTTemplating::getSingleton(); - $oTemplate = $oTemplating->loadTemplate($this->sTemplate); + $oTemplating =& KTTemplating::getSingleton(); + $oTemplate = $oTemplating->loadTemplate($this->sTemplate); $src_location = $this->aOptions['src']; $sJS = sprintf('addLoadEvent(initJSONLookup("%s", "%s"));', $this->sBasename, $src_location); // its bad, but that's life. - $oPage =& $GLOBALS['main']; - $oPage->requireJSStandalone($sJS); - + $oPage =& $GLOBALS['main']; + $oPage->requireJSStandalone($sJS); + $this->aOptions['multi'] = true; - + $aTemplateData = array( 'context' => $this, 'label' => $this->sLabel, @@ -357,7 +357,7 @@ class KTDescriptorSelectionWidget extends KTWidget { 'short_name' => $this->sBasename, 'options' => $this->aOptions, ); - return $oTemplate->render($aTemplateData); + return $oTemplate->render($aTemplateData); } } @@ -365,7 +365,7 @@ class KTCoreTreeMetadataWidget extends KTWidget { var $sNamespace = 'ktcore.widgets.treemetadata'; var $iFieldId; var $aCSS = array('resources/css/kt-treewidget.css'); - + function configure($aOptions) { $res = parent::configure($aOptions); if (PEAR::isError($res)) { @@ -377,19 +377,19 @@ class KTCoreTreeMetadataWidget extends KTWidget { return PEAR::raiseError(_kt('Tree metadata fields must be associated with a particular type.')); } } - + function getWidget() { // very simple, general purpose passthrough. Chances are this is sufficient, // just override the template being used. - $bHasErrors = false; - + $bHasErrors = false; + require_once(KT_LIB_DIR . '/documentmanagement/MDTree.inc'); - + $fieldTree = new MDTree(); $fieldTree->buildForField($this->iFieldId); $fieldTree->setActiveItem($this->value); - return $fieldTree->_evilTreeRenderer($fieldTree, $this->sName); - } + return $fieldTree->_evilTreeRenderer($fieldTree, $this->sName); + } } // wrap a set of fields into a core, basic one. @@ -406,16 +406,16 @@ class KTCoreFieldsetWidget extends KTWidget { function configure($aOptions) { // do NOT use parent. $this->sLabel = KTUtil::arrayGet($aOptions, 'label'); - $this->sDescription = KTUtil::arrayGet($aOptions, 'description'); + $this->sDescription = KTUtil::arrayGet($aOptions, 'description'); $this->sName = KTUtil::arrayGet($aOptions, 'name'); - $this->sBasename = $this->sName; - + $this->sBasename = $this->sName; + $aWidgets = (array) KTUtil::arrayGet($aOptions, 'widgets'); // very similar to the one in forms.inc.php if (is_null($this->_oWF)) { $this->_oWF =& KTWidgetFactory::getSingleton(); } - + $this->_widgets = array(); // we don't want to expose the factory stuff to the user - its an // arbitrary distinction to the user. Good point from NBM ;) @@ -428,24 +428,24 @@ class KTCoreFieldsetWidget extends KTWidget { } else { $namespaceOrObject = $aInfo[0]; $config = (array) $aInfo[1]; - + $this->_widgets[] = $this->_oWF->get($namespaceOrObject, $config); } - } + } } function render() { - $oTemplating =& KTTemplating::getSingleton(); + $oTemplating =& KTTemplating::getSingleton(); $oTemplate = $oTemplating->loadTemplate('ktcore/forms/widgets/fieldset'); - + $aTemplateData = array( 'context' => $this, 'label' => $this->sLabel, 'description' => $this->sDescription, 'widgets' => $this->renderWidgets(), ); - return $oTemplate->render($aTemplateData); + return $oTemplate->render($aTemplateData); } function renderWidgets() { @@ -458,11 +458,11 @@ class KTCoreFieldsetWidget extends KTWidget { $rendered[] = $v->render(); } } - + return implode(' ', $rendered); } - function getDefault() { + function getDefault() { // we need to do a little more admin here // to obtain the default // annoyingly @@ -475,7 +475,7 @@ class KTCoreFieldsetWidget extends KTWidget { } return $d; } - + function setDefault($aValue) { $d = (array) $aValue; foreach ($this->_widgets as $k => $w) { @@ -483,8 +483,8 @@ class KTCoreFieldsetWidget extends KTWidget { $oWidget->setDefault(KTUtil::arrayGet($d, $oWidget->getBasename(), $oWidget->getDefault())); } } - - function wrapName($sOuter) { + + function wrapName($sOuter) { $this->sName = sprintf('%s[%s]', $sOuter, $this->sBasename); // now, chain to our children foreach ($this->_widgets as $k => $v) { @@ -495,12 +495,12 @@ class KTCoreFieldsetWidget extends KTWidget { $oWidget->wrapName($this->sName); } } - + function setErrors($aErrors = null) { if (is_array($aErrors)) { $this->aErrors = $aErrors; } - + foreach ($this->_widgets as $k => $w) { $oWidget =& $this->_widgets[$k]; $oWidget->setErrors(KTUtil::arrayGet($aErrors, $oWidget->getBasename())); @@ -511,10 +511,10 @@ class KTCoreFieldsetWidget extends KTWidget { function getValidators() { // we use a fieldsetValidator here. $extra_validators = array(); - + foreach ($this->_widgets as $oWidget) { $res = $oWidget->getValidators(); - + if (!is_null($res)) { if (is_array($res)) { $extra_validators = kt_array_merge($extra_validators, $res); @@ -523,23 +523,23 @@ class KTCoreFieldsetWidget extends KTWidget { } } } - - $oVF =& KTValidatorFactory::getSingleton(); + + $oVF =& KTValidatorFactory::getSingleton(); return array($oVF->get('ktcore.validators.fieldset', array( - 'test' => $this->sBasename, + 'test' => $this->sBasename, 'validators' => &$extra_validators, ))); } - + function process($raw_data) { $d = (array) KTUtil::arrayGet($raw_data, $this->sBasename); $o = array(); - + // we now need to recombine the process foreach ($this->_widgets as $oWidget) { $o =& kt_array_merge($o, $oWidget->process($d)); } - + return array($this->sBasename => $o); } @@ -547,49 +547,49 @@ class KTCoreFieldsetWidget extends KTWidget { class KTCoreTransparentFieldsetWidget extends KTCoreFieldsetWidget { var $sNamespace = 'ktcore.widgets.transparentfieldset'; - + function render() { - $oTemplating =& KTTemplating::getSingleton(); + $oTemplating =& KTTemplating::getSingleton(); $oTemplate = $oTemplating->loadTemplate('ktcore/forms/widgets/transparent_fieldset'); - + $aTemplateData = array( 'widgets' => $this->renderWidgets(), ); - return $oTemplate->render($aTemplateData); - } + return $oTemplate->render($aTemplateData); + } } class KTExtraConditionalFieldsetWidget extends KTCoreFieldsetWidget { var $sNamespace = 'ktextra.conditionalmetadata.fieldset'; - + function render() { - $oTemplating =& KTTemplating::getSingleton(); + $oTemplating =& KTTemplating::getSingleton(); $oTemplate = $oTemplating->loadTemplate('ktcore/forms/widgets/conditionalfieldset'); - + $aTemplateData = array( 'context' => $this, 'label' => $this->sLabel, - 'description' => $this->sDescription, + 'description' => $this->sDescription, ); - return $oTemplate->render($aTemplateData); - } + return $oTemplate->render($aTemplateData); + } } class KTCoreCollectionWidget extends KTWidget { var $sNamespace = 'ktcore.widgets.collection'; var $sTemplate = 'ktcore/forms/widgets/collectionframe'; - + var $oCollection; var $sCode; function configure($aOptions) { $aOptions['broken_name'] = KTUtil::arrayGet($aOptions, 'broken_name', true, false); - + $res = parent::configure($aOptions); - if (PEAR::isError($res)) { + if (PEAR::isError($res)) { return $res; } @@ -600,19 +600,19 @@ class KTCoreCollectionWidget extends KTWidget { if(empty($this->iFolderId)) return PEAR::raiseError(_kt('No initial folder specified specified.')); $this->aBCUrlParams = KTUtil::arrayGet($aOptions, 'bcurl_params', array()); - + $this->aCols = array(); foreach($this->oCollection->columns as $oCol) { $this->aCols[] = $oCol->namespace; - } + } $this->sCode = KTUtil::randomString(); $this->sCollection = serialize($this->oCollection); $_SESSION['collection_widgets'][$this->sCode] = serialize($this); $this->requireJSResource('resources/js/collectionframe.js'); - - + + } function getTargetURL() { @@ -620,13 +620,13 @@ class KTCoreCollectionWidget extends KTWidget { $oPlugin =& $oPluginRegistry->getPlugin('ktcore.plugin'); $sPath = $oPlugin->getPagePath('collection'); $oKTConfig =& KTConfig::getSingleton(); - + $sName = $this->sName; if (KTUtil::arrayGet($this->aOptions, 'broken_name', false)) { $this->sName = 'fFolderId'; } - - $sPath = KTUtil::addQueryString($sPath, array('code'=>$this->sCode, + + $sPath = KTUtil::addQueryString($sPath, array('code'=>$this->sCode, 'fFolderId'=>$this->iFolderId, 'varname' => $sName)); @@ -636,8 +636,8 @@ class KTCoreCollectionWidget extends KTWidget { function getCollection() { $oCR =& KTColumnRegistry::getSingleton(); //print '
';
-        foreach($this->aCols as $ns) { 
-        	
+        foreach($this->aCols as $ns) {
+
             $oCR->getColumn($ns);
         }
         $this->oCollection = unserialize($this->sCollection);
@@ -671,25 +671,25 @@ class KTCoreFolderCollectionWidget extends KTCoreCollectionWidget {
         $collection->setQueryObject($qObj);
 
         $aO = $collection->getEnvironOptions();
-        $collection->setOptions($aO); 
-    
+        $collection->setOptions($aO);
+
         $aOptions['collection'] = $collection;
-        $aOptions['broken_name'] = $false;        
-    
+        $aOptions['broken_name'] = $false;
+
         return parent::configure($aOptions);
     }
-    
+
     function getDefault() { return $this->value; }
-    function setDefault($mValue) { 
+    function setDefault($mValue) {
         if ($mValue != $this->value) {
             $this->oCollection->setQueryObject(new FolderBrowseQuery($mValue));
             $this->value = $mValue;
             $this->aOptions['folder_id'] = $this->value;
             $this->iFolderId = $this->value;
             $this->sCollection = serialize($this->oCollection);
-            $_SESSION['collection_widgets'][$this->sCode] = serialize($this);         
+            $_SESSION['collection_widgets'][$this->sCode] = serialize($this);
         }
-    }    
+    }
 }
 
 class KTCoreCollectionPage extends KTStandardDispatcher {
@@ -699,30 +699,33 @@ class KTCoreCollectionPage extends KTStandardDispatcher {
         $folder_path_names = $oFolder->getPathArray();
         $folder_path_ids = explode(',', $oFolder->getParentFolderIds());
         $folder_path_ids[] = $oFolder->getId();
-        if ($folder_path_ids[0] == 0) {
-            array_shift($folder_path_ids);
-            array_shift($folder_path_names);
+
+        if (!empty($folder_path_ids) && empty($folder_path_ids[0]))
+        {
+			array_shift($folder_path_ids);
         }
 
-        
+        $oRoot = Folder::get(1);
+        $folder_path_names = array_merge(array($oRoot->getName()), $folder_path_names);
+
 
         foreach (range(0, count($folder_path_ids) - 1) as $index) {
             $id = $folder_path_ids[$index];
-        
+
             $aParams = kt_array_merge($aURLParams, array('fFolderId'=>$id, 'code'=>$sCode, 'varname'=>$sName));
             $url = KTUtil::addQueryString($_SERVER['PHP_SELF'], $aParams);
             $aBreadcrumbs[] = array('url' => $url, 'name' => $folder_path_names[$index]);
         }
-        
+
         return $aBreadcrumbs;
-    }        
-        
+    }
+
 
 
     function do_main() {
 
         $sCode = KTUtil::arrayGet($_REQUEST, 'code');
-        $sName = KTUtil::arrayGet($_REQUEST, 'varname','fFolderId');        
+        $sName = KTUtil::arrayGet($_REQUEST, 'varname','fFolderId');
         $oWidget = unserialize($_SESSION['collection_widgets'][$sCode]);
 
         $oCollection = $oWidget->getCollection();
@@ -735,17 +738,17 @@ class KTCoreCollectionPage extends KTStandardDispatcher {
 
         $aOptions = array('ignorepermissions' => KTBrowseUtil::inAdminMode($this->oUser, $oFolder));
         $oCollection->_queryObj->folder_id = $oFolder->getId();
-                
+
         $aOptions = $oCollection->getEnvironOptions();
         $aOptions['return_url'] = KTUtil::addQueryString($_SERVER['PHP_SELF'], array('code'=>$sCode, 'varname' => $sName, 'fFolderId' => $oFolder->getId()));
-    
+
         $oCollection->setOptions($aOptions);
-    
+
         // add the collection code to the title column QS params
-    
-        foreach($oWidget->aCols as $ns) { 
+
+        foreach($oWidget->aCols as $ns) {
             $aColOpts = $oCollection->getColumnOptions($ns);
-            $aColOpts['qs_params'] = kt_array_merge(KTUtil::arrayGet($aColOpts, 'qs_params', array()), 
+            $aColOpts['qs_params'] = kt_array_merge(KTUtil::arrayGet($aColOpts, 'qs_params', array()),
                                                     array('code' => $sCode, 'varname' => $sName));
             $oCollection->setColumnOptions($ns, $aColOpts);
         }
@@ -753,7 +756,7 @@ class KTCoreCollectionPage extends KTStandardDispatcher {
         // make the breadcrumbs
         $aBreadcrumbs = $this->_generate_breadcrumbs($oFolder, $sCode, $oWidget->aBCUrlParams, $sName);
 
-        print KTTemplating::renderTemplate('ktcore/forms/widgets/collection', 
+        print KTTemplating::renderTemplate('ktcore/forms/widgets/collection',
             array(
                 'collection'=> $oCollection,
                 'folder' => $oFolder,
@@ -780,33 +783,33 @@ class KTCoreConditionalSelectionWidget extends KTCoreSelectionWidget {
 
     var $bIsMaster;
     var $bMappings;
-    
+
     function _getFieldIdForMetadataId($iMetadata) {
 	$sTable = 'metadata_lookup';
 	$sQuery = "SELECT document_field_id FROM " . $sTable . " WHERE id = ?";
 	$aParams = array($iMetadata);
-	
+
 	$res = DBUtil::getOneResultKey(array($sQuery, $aParams), 'document_field_id');
 	if (PEAR::isError($res)) {
 	    return false;
 	}
 	return $res;
     }
-	    
+
 
     function configure($aOptions) {
         $res = parent::configure($aOptions);
-        if (PEAR::isError($res)) { 
+        if (PEAR::isError($res)) {
             return $res;
         }
-        
+
         $this->sIdMethod = KTUtil::arrayGet($aOptions, 'id_method', 'getId');
         $this->sLabelMethod = KTUtil::arrayGet($aOptions, 'label_method');
         if (empty($this->sLabelMethod)) {
             return PEAR::raiseError(_kt('No label method specified.'));
         }
         $existing_entities = (array) KTUtil::arrayGet($aOptions, 'existing_entities');
-        
+
         if (empty($this->value)) {
             $this->value = array();
             foreach ($existing_entities as $oEntity) {
@@ -829,7 +832,7 @@ class KTCoreConditionalSelectionWidget extends KTCoreSelectionWidget {
 
 	    foreach($oFieldset->getFields() as $oField) {
 		$c = array();
-	      
+
 		foreach($oField->getEnabledValues() as $oMetadata) {
 		    $a = array();
 		    // print '
';
@@ -838,7 +841,7 @@ class KTCoreConditionalSelectionWidget extends KTCoreSelectionWidget {
 		    if($nvals) {
 			foreach($nvals as $i=>$aVals) {
 			    $a = array_merge($a, $aVals);
-			    
+
 			    foreach($aVals as $id) {
 			      $field = $this->_getFieldIdForMetadataId($id);
 			      // print 'id ' . $id . ' is in field ' . $field . "
"; @@ -853,14 +856,14 @@ class KTCoreConditionalSelectionWidget extends KTCoreSelectionWidget { } $aConnections[$oField->getId()] = $c; } - + //exit(0); $oJSON = new Services_JSON; $this->sLookupsJSON = $oJSON->encode($aLookups); $this->sConnectionsJSON = $oJSON->encode($aConnections); } - + $new_vocab = array(); foreach ($this->aVocab as $oEntity) { @@ -872,12 +875,12 @@ class KTCoreConditionalSelectionWidget extends KTCoreSelectionWidget { } function getWidget() { - $bHasErrors = false; + $bHasErrors = false; if (count($this->aErrors) != 0) { $bHasErrors = true; } - $this->sTemplate = 'ktcore/forms/widgets/conditional_selection'; - - $oTemplating =& KTTemplating::getSingleton(); + $this->sTemplate = 'ktcore/forms/widgets/conditional_selection'; + + $oTemplating =& KTTemplating::getSingleton(); $oTemplate = $oTemplating->loadTemplate($this->sTemplate); $unselected = KTUtil::arrayGet($this->aOptions, 'unselected_label'); @@ -900,7 +903,7 @@ class KTCoreConditionalSelectionWidget extends KTCoreSelectionWidget { $this->_valuesearch[$v] = true; } } - + $aTemplateData = array( 'context' => $this, 'name' => $this->sName, @@ -918,7 +921,7 @@ class KTCoreConditionalSelectionWidget extends KTCoreSelectionWidget { ); return $oTemplate->render($aTemplateData); } - + } diff --git a/search2/indexing/indexerCore.inc.php b/search2/indexing/indexerCore.inc.php index f458902..4f4b672 100755 --- a/search2/indexing/indexerCore.inc.php +++ b/search2/indexing/indexerCore.inc.php @@ -179,8 +179,7 @@ class QueryResultItem } else { - $this->fullpath = $result['full_path'] . '/' . $result['name']; - if (substr($this->fullpath,0,1) == '/') $this->fullpath = substr($this->fullpath,1); + $this->fullpath = $result['full_path']; } $this->mimeType = $result['mimetype']; diff --git a/search2/search/fields/FullPathField.inc.php b/search2/search/fields/FullPathField.inc.php new file mode 100644 index 0000000..2b855e9 --- /dev/null +++ b/search2/search/fields/FullPathField.inc.php @@ -0,0 +1,60 @@ +. + * + * 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): ______________________________________ + * + */ + +class FullPathField extends DBFieldExpr +{ + public $general_op = ExprOp::CONTAINS; + + public function __construct() + { + parent::__construct('full_path', 'documents', _kt('Full Path')); + $this->setAlias('Full Path'); + } + + public function getInputRequirements() + { + return array('value'=>array('type'=>FieldInputType::TEXT)); + } + + public function is_valid() + { + return DefaultOpCollection::validateParent($this, DefaultOpCollection::$contains); + } +} + +?> \ No newline at end of file