Commit 81251662294d9b6e0fef1c39035e92cbe8545417

Authored by bshuttle
1 parent 36868ecb

Checkin of the tree-like metadata, basics of fieldsets and basics of the conditional metadata tree.


git-svn-id: https://kt-dms.svn.sourceforge.net/svnroot/kt-dms/trunk@3636 c91229c3-7414-0410-bfa2-8a42b809f60b
config/siteMap.inc
... ... @@ -138,6 +138,8 @@ $default->siteMap->addPage("docLinkManagement", "/presentation/lookAndFeel/knowl
138 138 $default->siteMap->addPage("manageHelp", "/presentation/lookAndFeel/knowledgeTree/administration/help/manageHelp.php", "Administration", SysAdmin, _("Help Administration"), true, 17);
139 139  
140 140 $default->siteMap->addPage("managePermissions", "/presentation/lookAndFeel/knowledgeTree/administration/permissions/managePermissions.php", "Administration", SysAdmin, _("Permissions Administration"), true, 18);
  141 +$default->siteMap->addPage("manageFieldsets", "/presentation/lookAndFeel/knowledgeTree/administration/fieldsetmanagement/manageFieldsets.php", "Administration", SysAdmin, _("Fieldsets Administration"), true, 19);
  142 +$default->siteMap->addPage("manageLookupTrees", "/presentation/lookAndFeel/knowledgeTree/administration/docfieldmanagement/manageLookupTrees.php", "Administration", SysAdmin, _("Lookup Tree Administration"), true, 20);
141 143  
142 144 $default->siteMap->addSectionColour("Administration", "th", "056DCE");
143 145 $default->siteMap->addSectionColour("Administration", "td", "6699FF");
... ...
config/tableMappings.inc
... ... @@ -28,6 +28,8 @@ $default->sessions_table = "active_sessions";
28 28 $default->data_types_table ="data_types";
29 29 // document type fields
30 30 $default->document_fields_table = "document_fields";
  31 +// document type fields
  32 +$default->document_fieldset_table = "document_fieldsets";
31 33 // links document
32 34 $default->document_fields_link_table = "document_fields_link";
33 35 // document subscriptions
... ... @@ -38,6 +40,8 @@ $default->transaction_types_table = "document_transaction_types_lookup";
38 40 $default->document_transactions_table = "document_transactions";
39 41 // links document types to document type fields
40 42 $default->document_type_fields_table = "document_type_fields_link";
  43 +// links document types to document type fields
  44 +$default->document_type_fieldsets_table = "document_type_fieldsets_link";
41 45 // document type information
42 46 $default->document_types_table = "document_types_lookup";
43 47 // stores documents
... ... @@ -122,4 +126,8 @@ $default->permission_descriptor_groups_table = "permission_descriptor_groups";
122 126 $default->permission_lookups_table = "permission_lookups";
123 127 $default->permission_lookup_assignments_table = "permission_lookup_assignments";
124 128 $default->groups_groups_table = "groups_groups_link";
  129 +$default->metadata_treenode_table = "metadata_lookup_tree";
  130 +$default->metadata_condition_table = "metadata_lookup_condition";
  131 +$default->md_condition_table = "metadata_lookup_condition";
  132 +$default->md_condition_chain_table = "metadata_lookup_condition_chain";
125 133 ?>
... ...
graphics/minus.png 0 → 100644

185 Bytes

graphics/plus.png 0 → 100644

191 Bytes

lib/documentmanagement/DocumentField.inc
... ... @@ -28,125 +28,39 @@ require_once("$default->fileSystemRoot/lib/documentmanagement/DocumentType.inc")
28 28 class DocumentField extends KTEntity {
29 29  
30 30 /** primary key value */
31   - var $iId;
32   - /** document field name */
  31 + var $iId = -1;
33 32 var $sName;
34   - /** document field data type */
35 33 var $sDataType;
36   - /** is_generic */
37 34 var $bIsGeneric;
38   - /**has lookup*/
39 35 var $bHasLookup;
40   -
41   - /**
42   - * Default constructor
43   - *
44   - * @param Name of document field
45   - * @param Document field data type
46   - *
47   - */
48   - function DocumentField($sNewName, $sNewDataType, $bNewIsGeneric, $bNewHasLookup) {
49   - //object not created yet
  36 + var $iParentFieldset;
  37 + var $bHasLookupTree;
  38 +
  39 + // FIXME deprecate constructor use. Use DocumentField::createFromArray instead.
  40 + function DocumentField($sNewName = null, $sNewDataType = null, $bNewIsGeneric = null, $bNewHasLookup = null, $iParentFieldset = null, $bHasLookupTree = null) {
  41 +
50 42 $this->iId = -1;
51 43 $this->sName = $sNewName;
52 44 $this->sDataType = $sNewDataType;
53 45 $this->bIsGeneric = $bNewIsGeneric;
54 46 $this->bHasLookup = $bNewHasLookup;
55   -
56   - }
57   -
58   - /**
59   - * Get the document field's primary key value
60   - *
61   - * @return int document field's primary key value
62   - *
63   - */
64   - function getID() {
65   - return $this->iId;
  47 + $this->iParentFieldset = $iParentFieldset;
  48 + $this->bHasLookupTree = $bHasLookupTree;
66 49 }
67 50  
68   - /**
69   - * Get the document field's name
70   - *
71   - * @return String document field's name
72   - *
73   - */
74   - function getName() {
75   - return $this->sName;
76   - }
77   -
78   - /**
79   - * Set the document field's name
80   - *
81   - * @param Document field's new name
82   - *
83   - */
84   - function setName($sNewValue) {
85   - $this->sName = $sNewValue;
86   - }
87   -
88   - /**
89   - * Get the document field's data type
90   - *
91   - * @return String document field's data type
92   - *
93   - */
94   - function getDataType() {
95   - return $this->sDataType;
96   - }
97   -
98   - /**
99   - * Set the document field's data type
100   - *
101   - * @param Document field's new data type
102   - *
103   - */
104   - function setDataType($sNewValue) {
105   - $this->sDataType = $sNewValue;
106   - }
107   -
108   -
109   - /**
110   - * Get the document field's generic or not
111   - *
112   - * @return String document field's data type
113   - *
114   - */
115   - function getIsGeneric() {
116   - return $this->bIsGeneric;
117   - }
118   -
119   - /**
120   - * Set the document field's genericness
121   - *
122   - * @param Document field's new data type
123   - *
124   - */
125   - function setIsGeneric($sNewValue) {
126   - $this->bIsGeneric = $sNewValue;
127   - }
128   -
129   -
130   -
131   - /**
132   - * Get the document field's lookup or not
133   - *
134   - * @return String document field's data type
135   - *
136   - */
137   - function getHasLookup() {
138   - return $this->bHasLookup;
139   - }
140   -
141   - /**
142   - * Set the document field's lookup
143   - *
144   - * @param Document field's new data type
145   - *
146   - */
147   - function setHasLookup($sNewValue) {
148   - $this->bHasLookup = $sNewValue;
149   - }
  51 + function getID() { return $this->iId; }
  52 + function getName() { return $this->sName; }
  53 + function setName($sNewValue) { $this->sName = $sNewValue; }
  54 + function getDataType() { return $this->sDataType; }
  55 + function setDataType($sNewValue) { $this->sDataType = $sNewValue; }
  56 + function getIsGeneric() { return $this->bIsGeneric; }
  57 + function setIsGeneric($sNewValue) { $this->bIsGeneric = $sNewValue; }
  58 + function getHasLookup() { return $this->bHasLookup; }
  59 + function setHasLookup($iNewValue) { $this->bHasLookup = $iNewValue; }
  60 + function getParentFieldset() { return $this->iParentFieldset; }
  61 + function setParentFieldset($iNewValue) { $this->iParentFieldset = $iNewValue; }
  62 + function getHasLookupTree() { return $this->bHasLookupTree; }
  63 + function setHasLookupTree($iNewValue) { $this->bHasLookupTree = KTUtil::anyToBool($iNewValue); }
150 64  
151 65 function _fieldValues () {
152 66 return array(
... ... @@ -154,6 +68,8 @@ class DocumentField extends KTEntity {
154 68 'data_type' => $this->sDataType,
155 69 'is_generic' => KTUtil::anyToBool($this->bIsGeneric),
156 70 'has_lookup' => KTUtil::anyToBool($this->bHasLookup),
  71 + 'parent_fieldset' => $this->iParentFieldset,
  72 + 'has_lookuptree' => KTUtil::anyToBool($this->bHasLookupTree),
157 73 );
158 74 }
159 75  
... ... @@ -161,7 +77,16 @@ class DocumentField extends KTEntity {
161 77 global $default;
162 78 return $default->document_fields_table;
163 79 }
  80 +
  81 + function &getList($sWhereClause = null) {
  82 + return KTEntityUtil::getList2('DocumentField', $sWhereClause);
  83 + }
  84 +
  85 + function &createFromArray($aOptions) {
  86 + return KTEntityUtil::createFromArray('DocumentField', $aOptions);
  87 + }
164 88  
  89 + // FIXME remove &get and rather use the autogenerated version.
165 90 /**
166 91 * Static function.
167 92 * Given a document_fields primary key it will create a
... ... @@ -178,6 +103,8 @@ class DocumentField extends KTEntity {
178 103 if ($sql->next_record()) {
179 104 $oDocumentField = & new DocumentField($sql->f("name"), $sql->f("data_type"), (bool)$sql->f("is_generic"), (bool)$sql->f("has_lookup"));
180 105 $oDocumentField->iId = $sql->f("id");
  106 + $oDocumentField->iParentFieldset = $sql->f("parent_fieldset");
  107 + $oDocumentField->bHasLookupTree = (bool)$sql->f("has_lookuptree");
181 108 return $oDocumentField;
182 109 }
183 110 $_SESSION["errorMessage"] = $lang_err_object_not_exist . "id = " . $iDocumentID . " table = $default->document_fields_table";
... ... @@ -187,16 +114,6 @@ class DocumentField extends KTEntity {
187 114 return false;
188 115 }
189 116  
190   - /**
191   - * Static- Get a list of document fieldss;
192   - *
193   - * @param String Where clause (not required)
194   - *
195   - * @return Array array of DocumentField objects, false otherwise
196   - */
197   - function getList($sWhereClause = null) {
198   - return KTEntityUtil::getList(DocumentField::_table(), 'DocumentField', $sWhereClause);
199   - }
200 117  
201 118 /**
202 119 * Returns the DocumentTypes mapped to this document field
... ...
lib/documentmanagement/DocumentFieldSet.inc 0 → 100644
  1 +<?php
  2 +
  3 +require_once("$default->fileSystemRoot/lib/documentmanagement/DocumentType.inc");
  4 +require_once(KT_LIB_DIR . "/ktentity.inc");
  5 +
  6 +/**
  7 + * class DocumentFieldSet
  8 + *
  9 + * Represents the basic grouping of fields into a fieldset.
  10 + */
  11 +class DocumentFieldSet extends KTEntity {
  12 +
  13 + /** primary key value */
  14 + var $iId = -1;
  15 + /** document fieldset name */
  16 + var $sName;
  17 + /** document fieldset namespace */
  18 + var $sName;
  19 + /** document fieldset mandatory flag*/
  20 + var $bMandatory;
  21 + var $bIsConditional;
  22 + var $iMasterField;
  23 +
  24 + var $_bUsePearError = true;
  25 +
  26 + function getId() { return $this->iId; }
  27 + function getName() { return $this->sName; }
  28 +
  29 + function setName($sNewValue) { $this->sName = $sNewValue; }
  30 + function getNamespace() { return $this->sNamespace; }
  31 + function setNamespace($sNewcreValue) { $this->sNamespace = $sNewValue; }
  32 + function getMandatory() { return $this->bMandatory; }
  33 + function setMandatory($bNewValue) { $this->bMandatory = $bNewValue; }
  34 + function getIsConditional () { return $this->bIsConditional; }
  35 + function setIsConditional ($bNewValue) { $this->bIsConditional = $bNewValue; }
  36 + function getMasterField () { return $this->bIsConditional; }
  37 + function setMasterField ($iNewValue) { $this->bIsConditional = $iNewValue; }
  38 +
  39 + var $_aFieldToSelect = array(
  40 + "iId" => "id",
  41 + "sName" => "name",
  42 + "sNamespace" => "namespace",
  43 + "bMandatory" => "mandatory",
  44 + "bIsConditional" => "is_conditional",
  45 + "iMasterField" => "master_field",
  46 + );
  47 +
  48 + // returns TRUE if all children are lookup enabled, false otherwise.
  49 + function canBeMadeConditional() {
  50 + if ($this->getIsConditional()) {
  51 + return false;
  52 + }
  53 +
  54 + // DEBUG
  55 + return false;
  56 + }
  57 +
  58 + function _fieldValues () {
  59 + return array(
  60 + 'name' => $this->sName,
  61 + 'namespace' => $this->sNamespace,
  62 + 'mandatory' => $this->bMandatory,
  63 + 'is_conditional' => KTUtil::anyToBool($this->bMandatory),
  64 + 'master_field' => $this->iMasterField,
  65 + );
  66 + }
  67 +
  68 + function _table () {
  69 + global $default;
  70 + return $default->document_fieldset_table;
  71 + }
  72 +
  73 + // Static function
  74 + function &get($iId) { return KTEntityUtil::get('DocumentFieldSet', $iId); }
  75 + function &getList($sWhereClause = null) { return KTEntityUtil::getList2('DocumentFieldSet', $sWhereClause); }
  76 + function &createFromArray($aOptions) { return KTEntityUtil::createFromArray('DocumentFieldSet', $aOptions); }
  77 +}
  78 +
  79 +?>
... ...
lib/documentmanagement/MDCondition.inc 0 → 100644
  1 +<?php
  2 +
  3 +require_once(KT_LIB_DIR . "/ktentity.inc");
  4 +
  5 +require_once(KT_LIB_DIR . "/documentmanagement/DocumentField.inc");
  6 +require_once(KT_LIB_DIR . "/documentmanagement/MetaData.inc");
  7 +
  8 +class MDConditionNode extends KTEntity {
  9 + /** boilerplate DB code. */
  10 + /** primary key */
  11 + var $iId = -1;
  12 + var $iFieldId;
  13 + var $sName;
  14 + var $iParentNode;
  15 +
  16 + var $_aFieldToSelect = array(
  17 + "iId" => "id",
  18 + "iFieldId" => "document_field_id",
  19 + "iLookupId" => "metadata_lookup_id",
  20 + "sName" => "name", // this can be null - if it isn't we are looking at a rooted subtree
  21 + );
  22 +
  23 + var $_bUsePearError = true;
  24 +
  25 + function getID() { return $this->iId; }
  26 + function setID($iId) { $this->iId = $iId; }
  27 + function getFieldId() { return $this->iFieldId; }
  28 + function setFieldId($iFieldId) { $this->iFieldId = $iFieldId; }
  29 + function getLookupId() { return $this->iLookupId; }
  30 + function setLookupId($iLookupId) { $this->$iLookupId = $iLookupId; }
  31 +
  32 + function _table () {
  33 + global $default;
  34 + return $default->metadata_condition_table;
  35 + }
  36 +
  37 + // Static Functions (dull)
  38 + function &get($iId) { return KTEntityUtil::get("MDConditionNode", $iId); }
  39 + function &createFromArray($aOptions) { return KTEntityUtil::createFromArray("MDConditionNode", $aOptions); }
  40 + function &getList($sWhereClause = null) { global $default; return KTEntityUtil::getList2("MDConditionNode", $sWhereClause); }
  41 +
  42 + /** end boilerplate. anything interesting goes below here. */
  43 +
  44 +}
  45 +
  46 +class MDConditionChain extends KTEntity {
  47 + /** boilerplate DB code. */
  48 + /** primary key */
  49 + var $iId = -1;
  50 + var $iParentCondition;
  51 + var $iChildCondition;
  52 +
  53 + var $_aFieldToSelect = array(
  54 + "iId" => "id",
  55 + "iChildCondition" => "child_condition",
  56 + "iParentCondition" => "parent_condition",
  57 + );
  58 +
  59 + var $_bUsePearError = true;
  60 +
  61 + function getID() { return $this->iId; }
  62 + function setID($iId) { $this->iId = $iId; }
  63 + function getParentConditionId() { return $this->iParentCondition; }
  64 + function setParentConditionId($iParentCondition) { $this->$iParentCondition = $iParentCondition; }
  65 + function getChildConditionId() { return $this->iChildCondition; }
  66 + function setChildConditionId($iChildCondition) { $this->$iChildCondition = $iParentCondition; }
  67 +
  68 + function _table () {
  69 + global $default;
  70 + return $default->md_condition_chain_table;
  71 + }
  72 +
  73 + // Static Functions (dull)
  74 + function &get($iId) { return KTEntityUtil::get("MDConditionChain", $iId); }
  75 + function &createFromArray($aOptions) { return KTEntityUtil::createFromArray("MDConditionChain", $aOptions); }
  76 + function &getList($sWhereClause = null) { global $default; return KTEntityUtil::getList2("MDConditionChain", $sWhereClause); }
  77 +
  78 + /** end boilerplate. anything interesting goes below here. */
  79 +
  80 +}
... ...
lib/documentmanagement/MDTree.inc 0 → 100644
  1 +<?php
  2 +
  3 +require_once(KT_LIB_DIR . "/ktentity.inc");
  4 +//require_once("../../../../../config/dmsDefaults.php"); // gak.
  5 +require_once(KT_LIB_DIR . "/documentmanagement/DocumentField.inc");
  6 +require_once(KT_LIB_DIR . "/documentmanagement/MetaData.inc");
  7 +
  8 +class MDTreeNode extends KTEntity {
  9 + /** boilerplate DB code. */
  10 + /** primary key */
  11 + var $iId = -1;
  12 + var $iFieldId;
  13 + var $sName;
  14 + var $iParentNode;
  15 +
  16 + var $_aFieldToSelect = array(
  17 + "iId" => "id",
  18 + "iFieldId" => "document_field_id",
  19 + "sName" => "name",
  20 + "iParentNode" => "metadata_lookup_tree_parent",
  21 + );
  22 +
  23 + var $_bUsePearError = true;
  24 +
  25 + function getID() { return $this->iId; }
  26 + function setID($iId) { $this->iId = $iId; }
  27 + function getFieldId() { return $this->iFieldId; }
  28 + function setFieldId($iFieldId) { $this->iFieldId = $iFieldId; }
  29 + function getName() { return $this ->sName; }
  30 + function setName($sName) { $this->sName = $sName; }
  31 + function getParentNode() { return $this->iParentNode; }
  32 + function setParentNode($iNode) { $this->iParentNode = $iParentNode; }
  33 +
  34 + function _table () {
  35 + global $default;
  36 + return $default->metadata_treenode_table;
  37 + }
  38 +
  39 + // Static Functions (dull)
  40 + function &get($iId) { return KTEntityUtil::get('MDTreeNode', $iId); }
  41 + function &createFromArray($aOptions) { return KTEntityUtil::createFromArray('MDTreeNode', $aOptions); }
  42 + function &getList($sWhereClause = null) { global $default; return KTEntityUtil::getList2('MDTreeNode', $sWhereClause); }
  43 +
  44 + /** end boilerplate. anything interesting goes below here. */
  45 +
  46 +}
  47 +
  48 +/* simple class to encapsulate tree-as-a-whole behaviour.
  49 + NBM - should this move, be refactored? It certainly doesn't belong in the DB,
  50 + since its just an aggregate utility.
  51 +*/
  52 +class MDTree {
  53 + var $contents = null; // private.
  54 + var $mapnodes = null;
  55 + var $root = null;
  56 + var $field_id;
  57 + var $lookups;
  58 +
  59 + function getRoot() { return $this->root; }
  60 + function getMapping() { return $this->mapnodes; }
  61 + function clear() {
  62 + $this->contents = null;
  63 + $this->mapnodes = null;
  64 + $this->root = null;
  65 + $this->lookups = null;
  66 + $this->field_id = null;
  67 + }
  68 +
  69 + /* function buildForField
  70 + *
  71 + * build a tree for a particular field instance.
  72 + * sets contents, so we can edit "stuff".
  73 + */
  74 + function buildForField($iFieldId)
  75 + {
  76 + global $default;
  77 + // before we start, we need to check that
  78 + // the specified field exists and is organised into a tree.
  79 + $organisedField =& DocumentField::get($iFieldId);
  80 + if (PEAR::isError($organisedField) || ($organisedField === false)) {
  81 + $this->clear(); // make sure we don't get pollution.
  82 + return ; // and leave all null. WHY DOESN'T PHP HAVE EXCEPTIONS?
  83 + }
  84 +
  85 + if ($organisedField->getHasLookupTree() === false) {
  86 + $this->clear(); // make sure we don't get pollution.
  87 + return ; // not a tree-lookup.
  88 + }
  89 + // right. we are now ready to start with the treebuild.
  90 + // root is a virtual node (id: 0).
  91 + $this->field_id = $iFieldId;
  92 + $orderedTreeNodes =& MDTreeNode::getList('WHERE document_field_id = '.$iFieldId.' ORDER BY metadata_lookup_tree_parent, name ASC');
  93 +
  94 + // since we have these nodes ordered by parent, we can perform a build
  95 + // we can build:
  96 + // $this->mapnodes [node_id => node]
  97 + // $this->root [node_id => subtree_root_arr]
  98 + // $this->contents [node_id => subtree_root_arr]
  99 + // THIS IS IMPORTANT: BOTH subtree_root_arr are the same object.
  100 + // Without this, we CAN'T build the tree this way. PLEASE, let PHP support
  101 + // this magic.
  102 +
  103 + // initialise.
  104 +
  105 + $this->mapnodes = array(); // will hold the actual nodes, mapped by id.
  106 + $this->contents = array(); // will hold references to each nodes subtree.
  107 + $this->contents[0] = array();
  108 +
  109 + foreach ($orderedTreeNodes as $treeNode) {
  110 +
  111 + // step 1: set the map entry for this item.
  112 + $iParent = $treeNode->getParentNode();
  113 + $iCurrId = $treeNode->getId();
  114 + $this->mapnodes[$iCurrId] = $treeNode; // always works, setting our own value.
  115 +
  116 + $parent_arr = null;
  117 + if (!array_key_exists($iParent, $this->contents)) { $this->contents[$iParent] = array(); }
  118 + if (!array_key_exists($iCurr, $this->contents)) { $this->contents[$iCurrId] = array(); }
  119 +
  120 + $this->contents[$iParent][] = $iCurrId;
  121 + //$default->log->debug("MDTree::buildForField bound to subtree " . print_r($this->contents, true));
  122 + }
  123 +
  124 + $md_list =& MetaData::getList('document_field_id = ' .$organisedField->getId());
  125 + $this->lookups = array();
  126 + foreach ($md_list as $lookup_value) {
  127 +
  128 + // failsafe. unparented and orphaned items go into root.
  129 + $iParentId = $lookup_value->getTreeParent();
  130 + if ($iParentId === null) $iParentId = 0;
  131 + if (array_key_exists($iParentId, $this->contents)) {
  132 + $target_set =& $this->contents[$iParentId];
  133 + }
  134 + else
  135 + {
  136 + $target_set =& $this->contents[0];
  137 + }
  138 +
  139 + $leafArray = null;
  140 + if (!array_key_exists("leaves", $target_set)) {
  141 + $target_set["leaves"] = array($lookup_value->getId());
  142 + }
  143 + else
  144 + {
  145 +
  146 + array_push($target_set["leaves"], $lookup_value->getId());
  147 + }
  148 +
  149 + $this->lookups[$lookup_value->getId()] = $lookup_value;
  150 +
  151 + }
  152 + $this->root =& $this->contents[0];
  153 + $default->log->debug("MDTree::buildForField done: " . print_r($this, true));
  154 +
  155 + }
  156 +
  157 + // handle deleting subtrees
  158 + function deleteNode($iNode) {
  159 + $stack = array();
  160 + array_push($stack, $iNode);
  161 + while (count($stack) != 0)
  162 + {
  163 + $currentNode = array_pop($stack);
  164 + foreach ($this->contents[$currentNode] as $label => $value)
  165 + {
  166 + if ($label === "leaves")
  167 + {
  168 + foreach ($value as $leaf)
  169 + {
  170 + $this->lookups[$leaf]->setTreeParent(0);
  171 + $this->lookups[$leaf]->update();
  172 + $this->contents[0]["leaves"][] = $leaf;
  173 + }
  174 + }
  175 + else array_push($stack, $value);
  176 + }
  177 + $this->mapnodes[$currentNode]->delete();
  178 + }
  179 + // finally, we prune the appropriate item from its parent.
  180 + $iParent = $this->mapnodes[$iNode]->getParentNode();
  181 + foreach ($this->contents[$iParent] as $index => $val)
  182 + if ($iNode === $val) unset($this->contents[$iParent][$index]);
  183 + }
  184 +
  185 + // add a node to the mapping after the fact (e.g. created later in the process.)
  186 + function addNode($oNode) {
  187 + $iParent = $oNode->getParentNode();
  188 + $this->mapnodes[$oNode->getId()] =& $oNode;
  189 + $this->contents[$oNode->getId()] = array();
  190 + array_push($this->contents[$iParent], $oNode->getId());
  191 + }
  192 +
  193 + function reparentKeyword($lookup_id, $destination_parent_id)
  194 + {
  195 + global $default;
  196 +
  197 + $oKeyword = $this->lookups[$lookup_id];
  198 + $oldParent = $oKeyword->getTreeParent();
  199 + $oNewParent = $this->mapnodes[$destination_parent_id];
  200 + // we will have failed by here if its bogus.
  201 + //$default->log->debug('MDTree::reparentKeyword '.print_r($oNewParent, true));
  202 +
  203 + // if its 0 or NULL, we reparent to null.
  204 + if (($oNewParent === null) or ($desintation_parent_id === 0)) {
  205 + $new_home = 0;
  206 + } else {
  207 + $new_home = $oNewParent->getId();
  208 + }
  209 + $oKeyword->setTreeParent($new_home);
  210 + // don't assume we're reparenting from 0.
  211 + $KWIndex = array_search($lookup_id, $this->contents[$oldParent]["leaves"]);
  212 + unset($this->contents[$oldParent]["leaves"][$KWIndex]);
  213 + $this->contents[$new_home]["leaves"][] = $oKeyword->getId();
  214 + $oKeyword->update();
  215 + }
  216 +
  217 +
  218 + // STUB FUNCTIONS: need to be filled in.
  219 +
  220 +
  221 + // REALLY need to deprecate this, but how?
  222 + function render($bEditable) { return null; } // render using a template (with edit / buttons.) FIXME build a widget / renderer.
  223 +
  224 +
  225 + /* ----------------------- EVIL HACK --------------------------
  226 + *
  227 + * This whole thing needs to replaced, as soon as I work out how
  228 + * to non-sucking Smarty recursion.
  229 + */
  230 +
  231 + function _evilTreeRecursion($subnode, $treeToRender, $inputname)
  232 + {
  233 + $treeStr = "<ul>";
  234 + foreach ($treeToRender->contents[$subnode] as $subnode_id => $subnode_val)
  235 + {
  236 + if ($subnode_id !== "leaves") {
  237 + $treeStr .= '<li class="treenode">' . $treeToRender->mapnodes[$subnode_val]->getName();
  238 + $treeStr .= $this->_evilTreeRecursion($subnode_val, $treeToRender, $inputname);
  239 + $treeStr .= '</li>';
  240 + }
  241 + else
  242 + {
  243 + foreach ($subnode_val as $leaf)
  244 + {
  245 + $treeStr .= '<li class="leafnode"><input type="radio" name="'.$inputname.'" value="'.$treeToRender->lookups[$leaf]->getName().'">' . $treeToRender->lookups[$leaf]->getName() .'</input>';
  246 +
  247 + $treeStr .= '</li>'; }
  248 + }
  249 + }
  250 + $treeStr .= '</ul>';
  251 + return $treeStr;
  252 +
  253 + }
  254 +
  255 + // I can't seem to do recursion in smarty, and recursive templates seems a bad solution.
  256 + // Come up with a better way to do this (? NBM)
  257 + function _evilTreeRenderer($treeToRender, $inputname) {
  258 + //global $default;
  259 + $treeStr = "<!-- this is rendered with an unholy hack. sorry. -->";
  260 + $stack = array();
  261 + $exitstack = array();
  262 +
  263 + // since the root is virtual, we need to fake it here.
  264 + // the inner section is generised.
  265 + $treeStr .= '<ul class="kt_treenodes"><li class="treenode"><a class="pathnode" onclick="toggleElementClass(\'active\', this.parentNode);">Root</a>';
  266 + $treeStr .= '<ul>';
  267 + //$default->log->debug("EVILRENDER: " . print_r($treeToRender, true));
  268 + foreach ($treeToRender->getRoot() as $node_id => $subtree_nodes)
  269 + {
  270 + //$default->log->debug("EVILRENDER: ".$node_id." => ".$subtree_nodes." (".($node_id === "leaves").")");
  271 + // leaves are handled differently.
  272 + if ($node_id !== "leaves") {
  273 + // $default->log->debug("EVILRENDER: " . print_r($subtree_nodes, true));
  274 + $treeStr .= '<li class="treenode"><a class="pathnode" onclick="toggleElementClass(\'active\', this.parentNode);">' . $treeToRender->mapnodes[$subtree_nodes]->getName().'</a>';
  275 + $treeStr .= $this->_evilTreeRecursion($subtree_nodes, $treeToRender, $inputname);
  276 + $treeStr .= '</li>';
  277 + }
  278 + else
  279 + {
  280 + foreach ($subtree_nodes as $leaf)
  281 + {
  282 + $treeStr .= '<li class="leafnode"><input type="radio" name="'.$inputname.'" value="'.$treeToRender->lookups[$leaf]->getName().'">' . $treeToRender->lookups[$leaf]->getName() .'</input>';
  283 + $treeStr .= '</li>';
  284 + }
  285 + }
  286 + }
  287 + $treeStr .= '</ul></li>';
  288 + $treeStr .= '</ul>';
  289 +
  290 + return $treeStr;
  291 +
  292 + }
  293 +}
  294 +
  295 +?>
... ...
lib/documentmanagement/MetaData.inc
... ... @@ -27,11 +27,13 @@
27 27 class MetaData extends KTEntity {
28 28  
29 29 /** primary key value */
30   - var $iId;
  30 + var $iId = -1;
31 31 //document field id
32 32 var $iDocFieldId;
33 33 /** MetaData name */
34 34 var $sName;
  35 + /** _if_ this field is a tree, which node in said tree is this one's parent. */
  36 + var $iTreeParent;
35 37  
36 38 /**
37 39 * Default constructor
... ... @@ -40,68 +42,28 @@ class MetaData extends KTEntity {
40 42 * @param MetaData data type
41 43 *
42 44 */
43   - function MetaData($iNewDocFieldID,$sNewName) {
  45 + function MetaData($iNewDocFieldID = null,$sNewName = null, $iNewParent = null) {
44 46 //object not created yet
45 47 $this->iId = -1;
46 48 $this->iDocFieldID = $iNewDocFieldID;
47 49 $this->sName = $sNewName;
48   -
49   - }
50   -
51   - /**
52   - * Get the MetaData's primary key value
53   - *
54   - * @return int MetaData's primary key value
55   - *
56   - */
57   - function getID() {
58   - return $this->iId;
59   - }
60   -
61   - /**
62   - * Get the MetaData's name
63   - *
64   - * @return String MetaData's name
65   - *
66   - */
67   - function getName() {
68   - return $this->sName;
  50 + $this->iTreeParent = $iNewParent;
69 51 }
70 52  
71   -
72   - /**
73   - * Set the MetaData's name
74   - *
75   - * @param MetaData's new name
76   - *
77   - */
78   - function setName($sNewValue) {
79   - $this->sName = $sNewValue;
80   - }
81   - /**
82   - * Set the MetaData's docField
83   - *
84   - * @param MetaData's new name
85   - *
86   - */
87   - function setDocFieldID($sNewValue) {
88   - $this->iDocFieldID = $sNewValue;
89   - }
90   -
91   - /**
92   - * Get the MetaData's docfield
93   - *
94   - * @return String MetaData's name
95   - *
96   - */
97   - function getDocFieldID() {
98   - return $this->iDocFieldID;
99   - }
  53 + function getID() { return $this->iId; }
  54 + function getName() { return $this->sName; }
  55 + function setName($sNewValue) { $this->sName = $sNewValue; }
  56 + function getDocFieldID() { return $this->iDocFieldID; }
  57 + function setDocFieldID($iNewValue) { $this->iDocFieldID = $iNewValue; }
  58 + function getTreeParent() { return $this->iTreeParent; }
  59 + function setTreeParent($iNewValue) { $this->iTreeParent = $iNewValue; }
  60 +
100 61  
101 62 function _fieldValues () {
102 63 return array(
103 64 'document_field_id' => $this->iDocFieldID,
104 65 'name' => $this->sName,
  66 + 'treeorg_parent' => $this->iTreeParent,
105 67 );
106 68 }
107 69  
... ... @@ -152,6 +114,7 @@ class MetaData extends KTEntity {
152 114 if ($sql->next_record()) {
153 115 $oDocumentType = & new MetaData($sql->f("document_field_id"),$sql->f("name"));
154 116 $oDocumentType->iId = $sql->f("id");
  117 + $oDocumentType->iTreeParent = $sql->f("treeorg_parent");
155 118 return $oDocumentType;
156 119 }
157 120 $_SESSION["errorMessage"] = $lang_err_object_not_exist . "id = " . $iDocumentID . " table = document_types";
... ...
lib/visualpatterns/PatternMetaData.inc
1 1 <?php
2 2  
3 3 require_once("PatternListBox.inc");
  4 +require_once(KT_LIB_DIR . "/documentmanagement/MDTree.inc");
  5 +
4 6 /**
5 7 * $Id$
6 8 *
... ... @@ -44,12 +46,12 @@ class PatternMetaData {
44 46  
45 47 function render() {
46 48 global $default;
47   - $sQuery = "SELECT has_lookup, data_type FROM $default->document_fields_table WHERE name LIKE '" . DBUtil::escapeSimple($this->sMetaDataField) . "'";/*ok*/
  49 + $sQuery = "SELECT has_lookup, data_type, has_lookuptree, id FROM $default->document_fields_table WHERE name LIKE '" . DBUtil::escapeSimple($this->sMetaDataField) . "'";/*ok*/
48 50  
49 51 $sql = $default->db;
50 52 $sql->query($sQuery);
51 53 if ($sql->next_record()) {
52   - if ($sql->f("has_lookup")) {
  54 + if ($sql->f("has_lookup") and (!$sql->f("has_lookuptree"))) {
53 55 //is a lookup, so display a drop down list
54 56 $sWhereClause = "DF.name LIKE '" . $this->sMetaDataField . "'";
55 57 $sFromClause = "INNER JOIN $default->document_fields_table AS DF ON ST.document_field_id = DF.id";
... ... @@ -60,6 +62,10 @@ class PatternMetaData {
60 62 $oPatternListBox->setFromClause($sFromClause);
61 63 $oPatternListBox->setWhereClause($sWhereClause);
62 64 return $oPatternListBox->render();
  65 + } else if ($sql->f("has_lookup") and ($sql->f("has_lookuptree"))) {
  66 + $fieldTree = new MDTree();
  67 + $fieldTree->buildForField($sql->f("id"));
  68 + return $fieldTree->_evilTreeRenderer($fieldTree, $this->sFormName);
63 69 } else {
64 70 $textboxlength = null;
65 71 switch($sql->f("data_type")) {
... ...
presentation/lookAndFeel/knowledgeTree/administration/docfieldmanagement/manageLookupTrees.php 0 → 100644
  1 +<?php
  2 +require_once("../../../../../config/dmsDefaults.php");
  3 +require_once(KT_DIR . "/presentation/Html.inc");
  4 +require_once(KT_LIB_DIR . "/templating/templating.inc.php");
  5 +require_once(KT_LIB_DIR . "/documentmanagement/DocumentField.inc");
  6 +require_once(KT_LIB_DIR . "/documentmanagement/MetaData.inc");
  7 +require_once(KT_LIB_DIR . "/documentmanagement/MDTree.inc");
  8 +require_once(KT_LIB_DIR . "/dispatcher.inc.php");
  9 +$sectionName = "Administration";
  10 +require_once(KT_DIR . "/presentation/webpageTemplate.inc");
  11 +
  12 +class ManageLookupTreeDispatcher extends KTAdminDispatcher {
  13 + function do_main() {
  14 + $oTemplating = new KTTemplating;
  15 + $aTreeFields =& DocumentField::getList('has_lookuptree = 1');
  16 + $aLookupFields =& DocumentField::getList('has_lookup = 1 AND (has_lookuptree IS NULL or has_lookuptree = 0)');
  17 + $oTemplate = $oTemplating->loadTemplate("ktcore/manage_lookuptrees");
  18 + $aTemplateData = array(
  19 + "treefields" => $aTreeFields,
  20 + "lookupfields" => $aLookupFields,
  21 + );
  22 + return $oTemplate->render($aTemplateData);
  23 + }
  24 +
  25 + function handleOutput($data) {
  26 + global $main;
  27 + $main->bFormDisabled = true;
  28 + $main->setCentralPayload($data);
  29 + $main->render();
  30 + }
  31 +
  32 + function do_createTree() {
  33 + // extract.
  34 + $field_id = KTUtil::arrayGet($_REQUEST, 'field_id');
  35 +
  36 + // validate
  37 + if (empty($field_id)) { return $this->errorRedirectToMain("Must select a field to convert."); }
  38 + $oField =& DocumentField::get($field_id);
  39 + if (PEAR::isError($oField)) { return $this->errorRedirectToMain("Invalid field."); }
  40 +
  41 + // set as a metadata tree.
  42 + $oField->setHasLookupTree(1);
  43 + $oField->update();
  44 + $this->errorRedirectToMain("Converted ".$oField->getName()." to a tree.");
  45 + }
  46 +
  47 +
  48 + // create and display the tree editing form.
  49 + function do_editTree() {
  50 + global $default;
  51 + // extract.
  52 + $field_id = KTUtil::arrayGet($_REQUEST, 'field_id');
  53 + $current_node = KTUtil::arrayGet($_REQUEST, 'current_node', 0);
  54 + $subaction = KTUtil::arrayGet($_REQUEST, 'subaction');
  55 +
  56 + // validate
  57 + if (empty($field_id)) { return $this->errorRedirectToMain("Must select a field to edit."); }
  58 + $oField =& DocumentField::get($field_id);
  59 + if (PEAR::isError($oField)) { return $this->errorRedirectToMain("Invalid field."); }
  60 +
  61 + // under here we do the subaction rendering.
  62 + // we do this so we don't have to do _very_ strange things with multiple actions.
  63 + $default->log->debug("Subaction: " . $subaction);
  64 + $fieldTree =& new MDTree();
  65 + $fieldTree->buildForField($oField->getId());
  66 +
  67 + if ($subaction !== null) {
  68 + if ($subaction === "addCategory") {
  69 + $new_category = KTUtil::arrayGet($_REQUEST, 'category_name');
  70 + if (empty($new_category)) { return $this->errorRedirectTo("editTree", "Must enter a name for the new category.", array("field_id" => $field_id)); }
  71 + else { $this->subact_addCategory($field_id, $current_node, $new_category, $fieldTree);}
  72 + }
  73 + if ($subaction === "deleteCategory") {
  74 + $this->subact_deleteCategory($fieldTree, $current_node);
  75 + $current_node = 0; // clear out, and don't try and render the newly deleted category.
  76 + }
  77 + if ($subaction === "linkKeywords") {
  78 + $keywords = KTUtil::arrayGet($_REQUEST, 'keywordsToAdd');
  79 + $this->subact_linkKeywords($fieldTree, $current_node, $keywords);
  80 + $current_node = 0; // clear out, and don't try and render the newly deleted category.
  81 + }
  82 + if ($subaction === "unlinkKeyword") {
  83 + $keyword = KTUtil::arrayGet($_REQUEST, 'keyword_id');
  84 + $this->subact_unlinkKeyword($fieldTree, $keyword);
  85 + }
  86 + }
  87 +
  88 + if ($fieldTree->root === null) {
  89 + return $this->errorRedirectToMain("Error building tree. Is this a valid tree-lookup field?");
  90 + }
  91 +
  92 + // FIXME extract this from MDTree (helper method?)
  93 + $free_metadata = MetaData::getList('document_field_id = '.$oField->getId().' AND (treeorg_parent = 0 OR treeorg_parent IS NULL)');
  94 +
  95 + // render edit template.
  96 + $oTemplating = new KTTemplating;
  97 + $oTemplate = $oTemplating->loadTemplate("ktcore/edit_lookuptrees");
  98 + $renderedTree = $this->_evilTreeRenderer($fieldTree);
  99 + $aTemplateData = array(
  100 + "field" => $oField,
  101 + "tree" => $fieldTree,
  102 + "renderedTree" => $renderedTree,
  103 + "currentNode" => $current_node,
  104 + "freechildren" => $free_metadata,
  105 + );
  106 + return $oTemplate->render($aTemplateData);
  107 + }
  108 +
  109 + function subact_addCategory($field_id, $current_node, $new_category, &$constructedTree) {
  110 + $newCategory = MDTreeNode::createFromArray(array (
  111 + "iFieldId" => $field_id,
  112 + "sName" => $new_category,
  113 + "iParentNode" => $current_node,
  114 + ));
  115 + if (PEAR::isError($newCategory))
  116 + {
  117 + return false;
  118 + }
  119 + $constructedTree->addNode($newCategory);
  120 + return true;
  121 + }
  122 +
  123 + function subact_deleteCategory(&$constructedTree, $current_node) {
  124 + $constructedTree->deleteNode($current_node);
  125 + return true;
  126 + }
  127 +
  128 + function subact_unlinkKeyword(&$constructedTree, $keyword) {
  129 + $oKW = MetaData::get($keyword);
  130 + $constructedTree->reparentKeyword($oKW->getId(), 0);
  131 + return true;
  132 + }
  133 +
  134 +
  135 + function subact_linkKeywords(&$constructedTree, $current_node, $keywords) {
  136 + foreach ($keywords as $md_id)
  137 + {
  138 + $constructedTree->reparentKeyword($md_id, $current_node);
  139 + }
  140 + return true;
  141 + }
  142 +
  143 + /* ----------------------- EVIL HACK --------------------------
  144 + *
  145 + * This whole thing needs to replaced, as soon as I work out how
  146 + * to non-sucking Smarty recursion.
  147 + */
  148 +
  149 + function _evilTreeRecursion($subnode, $treeToRender)
  150 + {
  151 + $treeStr = "<ul>";
  152 + foreach ($treeToRender->contents[$subnode] as $subnode_id => $subnode_val)
  153 + {
  154 + if ($subnode_id !== "leaves") {
  155 + $treeStr .= '<li class="treenode">' . $treeToRender->mapnodes[$subnode_val]->getName();
  156 + $treeStr .= $this->_evilActionHelper($treeToRender->field_id, false, $subnode_val);
  157 + $treeStr .= $this->_evilTreeRecursion($subnode_val, $treeToRender);
  158 + $treeStr .= '</li>';
  159 + }
  160 + else
  161 + {
  162 + foreach ($subnode_val as $leaf)
  163 + {
  164 + $treeStr .= '<li class="leafnode">' . $treeToRender->lookups[$leaf]->getName();
  165 + $treeStr .= $this->_evilActionHelper($treeToRender->field_id, true, $leaf);
  166 + $treeStr .= '</li>'; }
  167 + }
  168 + }
  169 + $treeStr .= '</ul>';
  170 + return $treeStr;
  171 +
  172 + }
  173 +
  174 + // I can't seem to do recursion in smarty, and recursive templates seems a bad solution.
  175 + // Come up with a better way to do this (? NBM)
  176 + function _evilTreeRenderer($treeToRender) {
  177 + //global $default;
  178 + $treeStr = "<!-- this is rendered with an unholy hack. sorry. -->";
  179 + $stack = array();
  180 + $exitstack = array();
  181 +
  182 + // since the root is virtual, we need to fake it here.
  183 + // the inner section is generised.
  184 + $treeStr .= '<ul class="kt_treenodes"><li class="treenode"><a class="pathnode" onclick="toggleElementClass(\'active\', this.parentNode);">Root</a>';
  185 + $treeStr .= ' (<a href="manageLookupTrees.php?action=editTree&field_id='.$treeToRender->field_id.'&current_node=0">edit</a>)';
  186 + $treeStr .= '<ul>';
  187 + //$default->log->debug("EVILRENDER: " . print_r($treeToRender, true));
  188 + foreach ($treeToRender->getRoot() as $node_id => $subtree_nodes)
  189 + {
  190 + //$default->log->debug("EVILRENDER: ".$node_id." => ".$subtree_nodes." (".($node_id === "leaves").")");
  191 + // leaves are handled differently.
  192 + if ($node_id !== "leaves") {
  193 + // $default->log->debug("EVILRENDER: " . print_r($subtree_nodes, true));
  194 + $treeStr .= '<li class="treenode"><a class="pathnode" onclick="toggleElementClass(\'active\', this.parentNode);">' . $treeToRender->mapnodes[$subtree_nodes]->getName() . '</a>';
  195 + $treeStr .= $this->_evilActionHelper($treeToRender->field_id, false, $subtree_nodes);
  196 + $treeStr .= $this->_evilTreeRecursion($subtree_nodes, $treeToRender);
  197 + $treeStr .= '</li>';
  198 + }
  199 + else
  200 + {
  201 + foreach ($subtree_nodes as $leaf)
  202 + {
  203 + $treeStr .= '<li class="leafnode">' . $treeToRender->lookups[$leaf]->getName();
  204 + $treeStr .= $this->_evilActionHelper($treeToRender->field_id, true, $leaf);
  205 + $treeStr .= '</li>';
  206 + }
  207 + }
  208 + }
  209 + $treeStr .= '</ul></li>';
  210 + $treeStr .= '</ul>';
  211 +
  212 + return $treeStr;
  213 + }
  214 +
  215 + // don't hate me.
  216 + function _evilActionHelper($iFieldId, $bIsKeyword, $current_node) {
  217 + $actionStr = " (";
  218 + if ($bIsKeyword === true) {
  219 + $actionStr .= '<a href="manageLookupTrees.php?action=editTree&field_id='.$iFieldId.'&keyword_id='.$current_node.'&subaction=unlinkKeyword">unlink</a>';
  220 + }
  221 + else
  222 + {
  223 + $actionStr .= '<a href="manageLookupTrees.php?action=editTree&field_id='.$iFieldId.'&current_node='.$current_node.'">add items</a> ';
  224 + $actionStr .= '| <a href="manageLookupTrees.php?action=editTree&field_id='.$iFieldId.'&current_node='.$current_node.'&subaction=deleteCategory">delete</a>';
  225 + }
  226 + $actionStr .= ")";
  227 + return $actionStr;
  228 + }
  229 +
  230 +}
  231 +
  232 +$oDispatcher = new ManageLookupTreeDispatcher();
  233 +$oDispatcher->dispatch();
  234 +
  235 +?>
... ...
presentation/lookAndFeel/knowledgeTree/handleConditional.php 0 → 100644
  1 +<?php
  2 +require_once("../../../config/dmsDefaults.php");
  3 +require_once(KT_DIR . "/presentation/Html.inc");
  4 +require_once(KT_LIB_DIR . "/templating/templating.inc.php");
  5 +require_once(KT_LIB_DIR . "/documentmanagement/DocumentFieldSet.inc");
  6 +require_once(KT_LIB_DIR . "/documentmanagement/DocumentField.inc");
  7 +require_once(KT_LIB_DIR . "/documentmanagement/MDCondition.inc");
  8 +require_once(KT_LIB_DIR . "/database/dbutil.inc");
  9 +require_once(KT_LIB_DIR . "/util/ktutil.inc");
  10 +require_once(KT_LIB_DIR . "/dispatcher.inc.php");
  11 +$sectionName = "Administration";
  12 +require_once(KT_DIR . "/presentation/webpageTemplate.inc");
  13 +
  14 +class HandleConditionalDispatcher extends KTDispatcher {
  15 + function do_main() {
  16 + global $default;
  17 + $fieldset_id = KTUtil::arrayGet($_REQUEST, 'fieldset_id');
  18 + $active_fields = KTUtil::arrayGet($_REQUEST, 'fields');
  19 +
  20 +
  21 + if (empty($fieldset_id)) {
  22 + return 'invalid fieldset_id';
  23 + }
  24 +
  25 + $fieldsToRender = array(); // contains "name" => rows.
  26 +
  27 + $oFieldSet = DocumentFieldSet::get($fieldset_id);
  28 + if (empty($active_fields)) {
  29 + $res = $this->getMasterFieldInfo($oFieldSet);
  30 + $fieldsToRender[$oFieldSet->getMasterField()] = $res;
  31 + } else {
  32 + // urgh. we use this to generate our list of things to extract from the input.
  33 + $pairings = array();
  34 + foreach ($active_fields as $field_id) {
  35 + $current = KTUtil::arrayGet($_REQUEST, 'conditional_field_'.$field_id);
  36 + if ($current === null) {
  37 + return 'invalid input sequence.';
  38 + }
  39 + else {
  40 + $pairings[$field_id] = $current;
  41 + }
  42 +
  43 + }
  44 + $res = $this->validatePath($oFieldSet, $pairings, true);
  45 + if ($res === true) {
  46 + return 'validated input.';
  47 + }
  48 + else if ($res === false) {
  49 + return 'invalid input';
  50 + }
  51 + // quick collation process.
  52 + foreach ($res as $aRow) {
  53 + $fieldsToRender[$aRow["document_field_id"]][] = $aRow;
  54 + }
  55 + }
  56 +
  57 + $default->log->debug('validatePath: results '.print_r($fieldsToRender,true));
  58 +
  59 + $oTemplating = new KTTemplating;
  60 +
  61 + $oTemplate = $oTemplating->loadTemplate("ktcore/handle_conditional");
  62 + $aTemplateData = array(
  63 + "fieldset" => $oFieldSet->getId(),
  64 + "oldfields" => $pairings,
  65 + "fieldsToRender" => $fieldsToRender,
  66 + );
  67 + return $oTemplate->render($aTemplateData);
  68 + }
  69 +
  70 + function handleOutput($data) {
  71 + print $data;
  72 + }
  73 +
  74 + function getMasterFieldInfo($oFieldSet) {
  75 + global $default;
  76 + $masterField = $oFieldSet->getMasterField();
  77 + $sQuery = "SELECT md_cond.document_field_id AS document_field_id, md_cond.metadata_lookup_id AS val, md_lookup.name AS name FROM $default->md_condition_table AS md_cond LEFT JOIN $default->md_condition_chain_table AS md_chain ON (md_cond.id = md_chain.child_condition) LEFT JOIN $default->metadata_table AS md_lookup ON (md_cond.metadata_lookup_id = md_lookup.id) WHERE md_cond.document_field_id = ? ";
  78 + return DBUtil::getResultArray(array($sQuery, array($masterField)));
  79 + }
  80 +
  81 + // either returns the set of pathid's for the input,
  82 + // or false (FIXME: should be PEAR::raiseError)
  83 + function validatePath($oFieldSet, $aPairings, $bPartial) {
  84 + /* An explanation of what happens below, since NBM's comment was "Put that crack-pipe back in the freezer."
  85 + *
  86 + * $aPairings are the inputs we were handed. Within these, there are 3
  87 + * important things:
  88 + * 1. we have a limited set of document_fields within the $oFieldSet.
  89 + * 2. one of these fields is the "master field" - the _ONLY_ one without
  90 + * any parent conditions.
  91 + * 3. some fields may get passed _out_ as <input type="hidden" name="conditional_field_x" value="-1" />
  92 + * which indicates that there is NO VALUE for that field (again, saved in the db as "-1" so we don't go mad.
  93 + *
  94 + * We essentially have 3 stacks: inputs, parent_conditions, free_field_ids
  95 + *
  96 + * $bPartial indicates what we need to do once we've run out of inputs to validate: either fail (if we don't have coverage, and its not partial)
  97 + * or return the possible next choices.
  98 + *
  99 + * We start with the master-field, and get its entry in the table. If it isn't there, fail.
  100 + * i. push its field onto the "parent_conditions" stack, and remove the field_id from the "option_fields".
  101 + * ii. remove its entry from the inputs.
  102 + * While we have inputs ->
  103 + * find any matches that have a (field_id, lookup_id) in the input set, and parent_condition in the parent_conditions stack.
  104 + * if no values found, FAIL - invalid input (we can't match what we've been given).
  105 + * otherwise ->
  106 + * remove them from the input stack, push their id's onto the parent_condition stack, and remove their field_id's from the field_id stack.
  107 + * If $bPartial == true ->
  108 + * get anything which has a parent_condition in the parent_condition set, and a field_id in the free_field_id's set -> this will give you
  109 + * the next set of "inputs" - (column, lookup) pairs with parent-rules that have been activated.
  110 + * If $bPartial == false and free_field_id's still has anything in it WE FAIL OUT.
  111 + */
  112 + global $default;
  113 + $free_field_ids = array();
  114 + $parent_conditions = array();
  115 + $inputs = $aPairings; // please tell me this does a copy ...
  116 +
  117 + // step 1: generate free_field_ids.
  118 + $childFields = DocumentField::getList('parent_fieldset = '.$oFieldSet->getId());
  119 + foreach ($childFields as $oField) {
  120 + $free_field_ids[$oField->getId()] = 1; // placeholder.
  121 + }
  122 + $master_field = $oFieldSet->getMasterField(); // this is the id of the master field.
  123 + if (!array_key_exists($master_field, $inputs)) {
  124 + return false; // no master field in the input.
  125 + }
  126 +
  127 + // step 2: get the first parent, to get the ball rolling.
  128 + $sQuery = "SELECT * FROM $default->md_condition_table WHERE document_field_id = ? and metadata_lookup_id = ? ";
  129 + $aParams = array($master_field, $inputs[$master_field]);
  130 + $res = DBUtil::getOneResult(array($sQuery, $aParams));
  131 +
  132 + if (PEAR::isError($res)) {
  133 + return false; // no value matched on the master field input, the rest MUST fail.
  134 + }
  135 + else {
  136 + unset($free_field_ids[$master_field]); // master is no longer free.
  137 + $rule_id = $res["id"];
  138 + $parent_conditions[$rule_id] = 1;
  139 + unset($inputs[$master_field]);
  140 + }
  141 +
  142 + $default->log->debug('validatePath: parent_conditions '.print_r($parent_conditions,true));
  143 +
  144 +
  145 + while (count($inputs) != 0) { // we'll return out inside here if necessary.
  146 + // check for items in inputs, with parents in parent_conditions.
  147 +
  148 + // $testStr = "";
  149 + // $testarr = array();
  150 + // for ($i=0; $i<3; $i++) {
  151 + // $testarr[] = "( ? )";
  152 + // }
  153 + // $testStr = "(".join(" OR ", $testarr).")";
  154 + // return $testStr;
  155 +
  156 + // we need something like "parent_conditions IN (1,2,3) AND ((f=1 AND v=2) OR (f=2 AND v=5) OR (f=4 AND v=7))
  157 + $sParentClause = "md_chain.parent_condition IN (";
  158 + $aInputParts = array();
  159 + for ($i=0; $i<count($parent_conditions); $i++) {
  160 + if ($i == count($parent_conditions)-1) {
  161 + $sParentClause .= '?';
  162 + } else {
  163 + $sParentClause .= '? ,';
  164 + }
  165 + }
  166 + $sParentClause .= ')';
  167 +
  168 + $aInputs = array();
  169 +
  170 + foreach ($inputs as $fid => $lookid) {
  171 + $aInputs[] = $fid;
  172 + $aInputs[] = $lookid;
  173 + $aInputParts[] = '(md_cond.document_field_id = ? AND md_cond.metadata_lookup_id = ?)';
  174 + }
  175 + $sInputs = join(" OR ", $aInputParts);
  176 + $sInputs = '(' . $sInputs . ')';
  177 +
  178 +
  179 + $default->log->debug('validatePath: parent_conditions '.print_r($parent_conditions,true));
  180 +
  181 + $sFieldClause = "md_cond.document_field_id IN (";
  182 + for ($i=0; $i<count($free_field_ids); $i++) {
  183 + if ($i == count($free_field_ids)-1) {
  184 + $sFieldClause .= '?';
  185 + } else {
  186 + $sFieldClause .= '? ,';
  187 + }
  188 + }
  189 + $sFieldClause .= ')';
  190 + $default->log->debug('validatePath: sParentClause '.print_r($sParentClause,true));
  191 +
  192 + $sWhere = KTUtil::whereToString(array(array($sParentClause, array_keys($parent_conditions)), array($sInputs, $aInputs)));
  193 + $sQuery = "SELECT md_cond.id as rule_id, md_cond.document_field_id AS field_id FROM $default->md_condition_table AS md_cond LEFT JOIN $default->md_condition_chain_table AS md_chain ON (md_cond.id = md_chain.child_condition) WHERE ";
  194 + $default->log->debug('validatePath: '.print_r(array($sQuery . $sWhere[0], $sWhere[1]),true));
  195 + $res = DBUtil::getResultArray(array($sQuery . $sWhere[0], $sWhere[1]));
  196 + if (PEAR::isError($res)) {
  197 + return false;
  198 + }
  199 +
  200 + // if there's anything is $res, its a match (must be - we can't have crossed chains.)
  201 + if (count($res) == 0) {
  202 + return false; // fail - no matches from inputs against parent_conditions.
  203 + } else {
  204 + // we must have a match - MAY have multiple matches.
  205 + foreach ($res as $aRow) {
  206 + $default->log->debug('validatePath: output_row '.print_r($aRow,true));
  207 + $parent_conditions[$aRow["rule_id"]] = 1; // add this as a possible parent condition.
  208 + unset($free_field_ids[$aRow["field_id"]]); // no longer free
  209 + unset($inputs[$aRow["field_id"]]); // no longer an un-processed input, so reduce the input-count.
  210 + }
  211 + }
  212 + }
  213 +
  214 + // ok: we got this far, and have run out of inputs without running out of matches.
  215 + // IF we're looking for a partial match, and still have free fields, return the set of free-and-parent matches.
  216 + // OTHERWISE if we have free fields, fail
  217 + // finally, pass the input as valid.
  218 +
  219 + if (($bPartial === true) and (count($free_field_ids) > 0)) {
  220 + // generate the set of matches for free_fields with appropriate parents.
  221 + // UNFORTUNATELY, there is no "nice" way to do this.
  222 + // Wax On. Wax Off.
  223 +
  224 + $sParentClause = "md_chain.parent_condition IN (";
  225 + for ($i=0; $i<count($parent_conditions); $i++) {
  226 + if ($i == count($parent_conditions)-1) {
  227 + $sParentClause .= '?';
  228 + } else {
  229 + $sParentClause .= '? ,';
  230 + }
  231 + }
  232 + $sParentClause .= ')';
  233 + $default->log->debug('validatePath: parent_conditions '.print_r($parent_conditions,true));
  234 +
  235 + $sFieldClause = "md_cond.document_field_id IN (";
  236 + for ($i=0; $i<count($free_field_ids); $i++) {
  237 + if ($i == count($free_field_ids)-1) {
  238 + $sFieldClause .= '?';
  239 + } else {
  240 + $sFieldClause .= '? ,';
  241 + }
  242 + }
  243 + $sFieldClause .= ')';
  244 + $default->log->debug('validatePath: sParentClause '.print_r($sParentClause,true));
  245 +
  246 + $aWhere = KTUtil::whereToString(array(array($sParentClause, array_keys($parent_conditions)), array($sFieldClause,array_keys($free_field_ids))));
  247 + $sQuery = "SELECT md_cond.document_field_id AS document_field_id, md_cond.metadata_lookup_id AS val, md_lookup.name as name FROM $default->md_condition_table AS md_cond LEFT JOIN $default->md_condition_chain_table AS md_chain ON (md_cond.id = md_chain.child_condition) LEFT JOIN $default->metadata_table AS md_lookup ON (md_cond.metadata_lookup_id = md_lookup.id) WHERE ";
  248 + $default->log->debug('validatePath: sParentClause '.print_r(array($sQuery . $aWhere[0], $aWhere[1]), true));
  249 + $sOrderClause = " ORDER BY document_field_id ASC, name ASC";
  250 + return DBUtil::getResultArray(array($sQuery . $aWhere[0] . $sOrderClause, $aWhere[1])); // FIXME catch errors?
  251 + } else if (count($free_field_ids) != 0) {
  252 + return false; // incomplete - could actually catch this at the start.
  253 + } else {
  254 + return true; // note - this ALSO matches whenever $bPartial is true, but we have completed. UP THE STACK: true => valid and committable.
  255 + }
  256 + }
  257 +
  258 + // FIXME: this need s to move into MDCondition.inc, or manageConditionalMetadata.php
  259 + // actually, this needs to die - its DEBUG only, really.
  260 + function do_createCondition() {
  261 + global $default;
  262 + $fieldset_id = KTUtil::arrayGet($_REQUEST, 'fieldset_id');
  263 + $parent_id = KTUtil::arrayGet($_REQUEST, 'parent_id');
  264 + $field_id = KTUtil::arrayGet($_REQUEST, 'field_id');
  265 + $lookup_id = KTUtil::arrayGet($_REQUEST, 'lookup_id');
  266 +
  267 + $resObj = MDConditionNode::createFromArray(array(
  268 + "iFieldId" => $field_id,
  269 + "iLookupId" => $lookup_id,
  270 + ));
  271 +
  272 + $default->log->debug("CREATE_CONDITION_DEBUG: ".print_r($resObj,true));
  273 +
  274 + $resObj2 = MDConditionChain::createFromArray(array(
  275 + "iParentCondition" => $parent_id, // may be null.
  276 + "iChildCondition" => $resObj->getId(),
  277 + ));
  278 +
  279 + $default->log->debug("CREATE_CONDITION_DEBUG: ".print_r($resObj2,true));
  280 + }
  281 +
  282 +}
  283 +
  284 +$oDispatcher = new HandleConditionalDispatcher();
  285 +$oDispatcher->dispatch();
  286 +
  287 +?>
... ...