Commit 123491ecf05758ad4776d6b4fa7156862c57467d
1 parent
638ee683
KTS-4605 Conditional Metadata not working in KTAPI get_metadata(SUP-2537)
PT:2630614 Resolved javascript conflict for large text and conditional fields on the same page. Also fixed the bulk upload pages conditional list constraining on ajax load. Committed by: Charl Joseph Mert Reviewed by: Megan Watson
Showing
6 changed files
with
253 additions
and
128 deletions
lib/metadata/fieldset.inc.php
| ... | ... | @@ -186,6 +186,18 @@ class KTFieldset extends KTEntity { |
| 186 | 186 | ), $aOptions); |
| 187 | 187 | } |
| 188 | 188 | |
| 189 | + function &getConditionalFieldsets($aOptions = null) { | |
| 190 | + $aOptions = KTUtil::meldOptions( | |
| 191 | + $aOptions, | |
| 192 | + array('multi' => true,) | |
| 193 | + ); | |
| 194 | + return KTEntityUtil::getByDict('KTFieldset', array( | |
| 195 | + 'is_conditional' => true, | |
| 196 | + 'disabled' => false, | |
| 197 | + ), $aOptions); | |
| 198 | + } | |
| 199 | + | |
| 200 | + | |
| 189 | 201 | function &getForDocumentType($oDocumentType, $aOptions = null) { |
| 190 | 202 | $bIds = KTUtil::arrayGet($aOptions, 'ids'); |
| 191 | 203 | if (is_object($oDocumentType)) { | ... | ... |
lib/metadata/fieldsetregistry.inc.php
| ... | ... | @@ -92,7 +92,7 @@ class KTFieldsetRegistry { |
| 92 | 92 | * |
| 93 | 93 | */ |
| 94 | 94 | function getHtmlFields($oFieldset, $idPre = 'metadata_') { |
| 95 | - $fields = $oFieldset->getFields(); | |
| 95 | + $fields = $oFieldset->getFields(); | |
| 96 | 96 | $textAreaIds = array(); |
| 97 | 97 | |
| 98 | 98 | foreach($fields as $field) { |
| ... | ... | @@ -103,7 +103,6 @@ class KTFieldsetRegistry { |
| 103 | 103 | |
| 104 | 104 | return $textAreaIds; |
| 105 | 105 | } |
| 106 | - | |
| 107 | 106 | |
| 108 | 107 | /* |
| 109 | 108 | * Returns an array of fieldIds which belong to a generic fieldset |
| ... | ... | @@ -156,6 +155,11 @@ class KTFieldsetRegistry { |
| 156 | 155 | } |
| 157 | 156 | |
| 158 | 157 | foreach ($newfields as $oField) { |
| 158 | + // If a conditional field hasn't been set up properly this will throw errors. | |
| 159 | + // TODO: display a message indicating that admin must complete the setup. | |
| 160 | + if (PEAR::isError($oField)) { | |
| 161 | + continue; | |
| 162 | + } | |
| 159 | 163 | $fname = 'metadata_' . $oField->getId(); |
| 160 | 164 | $value = null; |
| 161 | 165 | ... | ... |
plugins/ktcore/KTWidgets.php
| ... | ... | @@ -973,7 +973,8 @@ class KTCoreTextAreaWidget extends KTWidget { |
| 973 | 973 | $oTemplate = $oTemplating->loadTemplate('ktcore/forms/widgets/base'); |
| 974 | 974 | |
| 975 | 975 | $this->aJavascript[] = 'thirdpartyjs/jquery/jquery-1.3.2.js'; |
| 976 | - $this->aJavascript[] = 'thirdpartyjs/tinymce/jscripts/tiny_mce/tiny_mce.js'; | |
| 976 | + $this->aJavascript[] = 'thirdpartyjs/jquery/jquery_noconflict.js'; | |
| 977 | + $this->aJavascript[] = 'thirdpartyjs/tinymce/jscripts/tiny_mce/tiny_mce.js'; | |
| 977 | 978 | $this->aJavascript[] = 'resources/js/kt_tinymce_init.js'; |
| 978 | 979 | |
| 979 | 980 | if (!empty($this->aJavascript)) { | ... | ... |
plugins/multiselect/BulkUpload.php
| ... | ... | @@ -141,7 +141,8 @@ class InetBulkUploadFolderAction extends KTFolderAction { |
| 141 | 141 | $aJavascript[] = 'thirdpartyjs/tinymce/jscripts/tiny_mce/tiny_mce.js'; |
| 142 | 142 | $aJavascript[] = 'resources/js/kt_tinymce_init.js'; |
| 143 | 143 | $aJavascript[] = 'thirdpartyjs/tinymce/jscripts/tiny_mce/jquery.tinymce.js'; |
| 144 | - | |
| 144 | + $aJavascript[] = 'resources/js/conditional_selection.js'; | |
| 145 | + | |
| 145 | 146 | $this->oPage->requireJSResources($aJavascript); |
| 146 | 147 | |
| 147 | 148 | //FIXME: Might really not need to load these styles, will check l8r | ... | ... |
presentation/lookAndFeel/knowledgeTree/ajaxConditional.php
| ... | ... | @@ -45,6 +45,11 @@ require_once(KT_LIB_DIR . "/util/ktutil.inc"); |
| 45 | 45 | require_once(KT_LIB_DIR . "/dispatcher.inc.php"); |
| 46 | 46 | require_once(KT_LIB_DIR . '/widgets/fieldsetDisplay.inc.php'); |
| 47 | 47 | |
| 48 | +require_once(KT_LIB_DIR . "/metadata/fieldsetregistry.inc.php"); | |
| 49 | +require_once(KT_LIB_DIR . "/widgets/widgetfactory.inc.php"); | |
| 50 | +require_once(KT_LIB_DIR . "/validation/validatorfactory.inc.php"); | |
| 51 | + | |
| 52 | + | |
| 48 | 53 | /* |
| 49 | 54 | * example code - tests the frontend behaviour. remember to check ajaxConditional.php |
| 50 | 55 | * |
| ... | ... | @@ -102,6 +107,82 @@ class AjaxConditionalDispatcher extends KTStandardDispatcher { |
| 102 | 107 | |
| 103 | 108 | return $sWidgets; |
| 104 | 109 | } |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + function _getFieldIdForMetadataId($iMetadata) { | |
| 115 | + $sTable = 'metadata_lookup'; | |
| 116 | + $sQuery = "SELECT document_field_id FROM " . $sTable . " WHERE id = ?"; | |
| 117 | + $aParams = array($iMetadata); | |
| 118 | + | |
| 119 | + $res = DBUtil::getOneResultKey(array($sQuery, $aParams), 'document_field_id'); | |
| 120 | + if (PEAR::isError($res)) { | |
| 121 | + return false; | |
| 122 | + } | |
| 123 | + return $res; | |
| 124 | + } | |
| 125 | + | |
| 126 | + //http://localhost/knowledgetree/presentation/lookAndFeel/knowledgeTree/ajaxConditional.php?action=getConditionalData&masterid=6&type=connections | |
| 127 | + //http://localhost/knowledgetree/presentation/lookAndFeel/knowledgeTree/ajaxConditional.php?action=getConditionalData&masterid=6&type=lookups | |
| 128 | + //$type can be lookups or connections | |
| 129 | + function do_getConditionalData(){ | |
| 130 | + $iMetadata = $_GET['masterid']; | |
| 131 | + $type = $_GET['type']; | |
| 132 | + | |
| 133 | + $oFReg =& KTFieldsetRegistry::getSingleton(); | |
| 134 | + $oFieldSets = KTFieldset::getConditionalFieldsets(); | |
| 135 | + | |
| 136 | + foreach ($oFieldSets as $oFieldset) { | |
| 137 | + | |
| 138 | + $aLookups = array(); | |
| 139 | + $aConnections = array(); | |
| 140 | + | |
| 141 | + foreach($oFieldset->getFields() as $oField) { | |
| 142 | + $c = array(); | |
| 143 | + | |
| 144 | + foreach($oField->getEnabledValues() as $oMetadata) { | |
| 145 | + $a = array(); | |
| 146 | + // print '<pre>'; | |
| 147 | + | |
| 148 | + $nvals = KTMetadataUtil::getNextValuesForLookup($oMetadata->getId()); | |
| 149 | + if($nvals) { | |
| 150 | + foreach($nvals as $i=>$aVals) { | |
| 151 | + $a = array_merge($a, $aVals); | |
| 152 | + | |
| 153 | + foreach($aVals as $id) { | |
| 154 | + $field = $this->_getFieldIdForMetadataId($id); | |
| 155 | + // print 'id ' . $id . ' is in field ' . $field . "<br/>"; | |
| 156 | + if(!in_array($field, $c)) { | |
| 157 | + $c[] = $field; | |
| 158 | + } | |
| 159 | + } | |
| 160 | + } | |
| 161 | + } | |
| 162 | + | |
| 163 | + $aLookups[$oMetadata->getId()] = $a; | |
| 164 | + } | |
| 165 | + $aConnections[$oField->getId()] = $c; | |
| 166 | + } | |
| 167 | + | |
| 168 | + //exit(0); | |
| 169 | + | |
| 170 | + $oJSON = new Services_JSON; | |
| 171 | + switch ($type) { | |
| 172 | + case 'lookups': | |
| 173 | + return $oJSON->encode($aLookups); | |
| 174 | + break; | |
| 175 | + case 'connections' : | |
| 176 | + return $oJSON->encode($aConnections); | |
| 177 | + break; | |
| 178 | + default: | |
| 179 | + return $oJSON->encode($aLookups); | |
| 180 | + } | |
| 181 | + | |
| 182 | + return false; | |
| 183 | + } | |
| 184 | + } | |
| 185 | + | |
| 105 | 186 | } |
| 106 | 187 | |
| 107 | 188 | $oDispatcher = new AjaxConditionalDispatcher(); | ... | ... |
resources/js/conditional_selection.js
| 1 | -// 'lookups' and 'connections' are produced by the master conditional widget | |
| 1 | +// depricated: 'lookups' and 'connections' are produced by the master conditional widget | |
| 2 | +// 'lookups' and 'connections' are now dynamically callable via ajax request to get a json response | |
| 3 | + | |
| 2 | 4 | var NOSELECTION = 'No selection.'; |
| 3 | 5 | |
| 4 | 6 | function ConditionalSelection() { |
| ... | ... | @@ -27,135 +29,159 @@ function getId(elm) { |
| 27 | 29 | |
| 28 | 30 | ConditionalSelection.prototype = { |
| 29 | 31 | 'initialize' : function(masterId) { |
| 32 | + | |
| 30 | 33 | // make clones of the original nodes to keep the options in |
| 31 | 34 | var fieldLookups = {}; |
| 32 | 35 | var current = {}; |
| 36 | + | |
| 37 | + /* | |
| 33 | 38 | var lookups = eval('lookups_' + masterId); |
| 34 | 39 | var connections = eval('connections_' + masterId); |
| 35 | - | |
| 36 | - // initialize - build various tables | |
| 37 | - forEach(getElementsByTagAndClassName(null, 'is_conditional_' + masterId), | |
| 38 | - function(elm) { | |
| 39 | - var fieldId = getId(elm); | |
| 40 | - for(var i=0; i<elm.options.length; i++) { | |
| 41 | - var oElm = elm.options[i]; | |
| 42 | - var lookupId = oElm.id.substring('lookup_'.length); | |
| 43 | - fieldLookups[lookupId] = {'parent':fieldId, 'value':oElm.innerHTML}; | |
| 44 | - | |
| 45 | - if(oElm.selected && oElm.value) { | |
| 46 | - current[fieldId] = oElm.value; | |
| 47 | - } | |
| 48 | - } | |
| 49 | - }); | |
| 50 | - | |
| 51 | - // the following function are defined inline, as they depend on the | |
| 52 | - // 'lookups' and 'connections' being specified above | |
| 53 | - | |
| 54 | - function clearConnected(fieldId) { | |
| 55 | - if(!fieldId in connections || !connections[fieldId].length) { | |
| 56 | - return; | |
| 57 | - } | |
| 58 | - for(var i=0; i<connections[fieldId].length; i++) { | |
| 59 | - var field = $('field_'+connections[fieldId][i]); | |
| 60 | - replaceChildNodes(field, OPTION(null, NOSELECTION)); | |
| 61 | - field.disabled = true; | |
| 62 | - clearConnected(connections[fieldId][i]); | |
| 63 | - } | |
| 64 | - } | |
| 40 | + */ | |
| 41 | + | |
| 42 | + var lookups = null; | |
| 43 | + var connections = null; | |
| 44 | + | |
| 45 | + // Making the conditional selection rules dynamically envokable. This enables conditional metadata fields to be used as ajax loaded dom elements. | |
| 46 | + // Needed for the Bulk Upload metadata page. | |
| 47 | + | |
| 48 | + jQuery.getJSON('presentation/lookAndFeel/knowledgeTree/ajaxConditional.php?action=getConditionalData&masterid=' + masterId + '&type=lookups', | |
| 49 | + function(json){ | |
| 50 | + lookups = json; | |
| 51 | + | |
| 52 | + jQuery.getJSON('presentation/lookAndFeel/knowledgeTree/ajaxConditional.php?action=getConditionalData&masterid=' + masterId + '&type=connections', | |
| 53 | + function(json){ | |
| 54 | + connections = json; | |
| 55 | + | |
| 56 | + // initialize - build various tables | |
| 57 | + forEach(getElementsByTagAndClassName(null, 'is_conditional_' + masterId), | |
| 58 | + function(elm) { | |
| 59 | + var fieldId = getId(elm); | |
| 60 | + for(var i=0; i<elm.options.length; i++) { | |
| 61 | + var oElm = elm.options[i]; | |
| 62 | + var lookupId = oElm.id.substring('lookup_'.length); | |
| 63 | + fieldLookups[lookupId] = {'parent':fieldId, 'value':oElm.innerHTML}; | |
| 64 | + | |
| 65 | + if(oElm.selected && oElm.value) { | |
| 66 | + current[fieldId] = oElm.value; | |
| 67 | + } | |
| 68 | + } | |
| 69 | + }); | |
| 70 | + | |
| 71 | + // the following function are defined inline, as they depend on the | |
| 72 | + // 'lookups' and 'connections' being specified above | |
| 73 | + | |
| 74 | + function clearConnected(fieldId) { | |
| 75 | + if(!fieldId in connections || !connections[fieldId].length) { | |
| 76 | + return; | |
| 77 | + } | |
| 78 | + for(var i=0; i<connections[fieldId].length; i++) { | |
| 79 | + var field = $('field_'+connections[fieldId][i]); | |
| 80 | + replaceChildNodes(field, OPTION(null, NOSELECTION)); | |
| 81 | + field.disabled = true; | |
| 82 | + clearConnected(connections[fieldId][i]); | |
| 83 | + } | |
| 84 | + } | |
| 85 | + | |
| 86 | + | |
| 87 | + function clearInvalid(fieldId) { | |
| 88 | + if(!fieldId in connections || !connections[fieldId].length) { | |
| 89 | + return; | |
| 90 | + } | |
| 91 | + | |
| 92 | + var parentField = $('field_'+fieldId); | |
| 93 | + var selectedId = getId(parentField.options[parentField.selectedIndex]); | |
| 94 | + var options = lookups[selectedId]; | |
| 95 | + | |
| 96 | + if(parentField.options[parentField.selectedIndex].innerHTML == NOSELECTION) { | |
| 97 | + clearConnected(fieldId); | |
| 98 | + } else { | |
| 99 | + for(var i=0; i<connections[fieldId].length; i++) { | |
| 100 | + var field = $('field_'+connections[fieldId][i]); | |
| 101 | + var newOptions = []; | |
| 102 | + var selected = null; | |
| 103 | + | |
| 104 | + for(var j=0; j<field.options.length; j++) { | |
| 105 | + var opt = field.options[j]; | |
| 106 | + if(!(opt.innerHTML != NOSELECTION && !in_array(options, getId(opt)))) { | |
| 107 | + newOptions.push(opt); | |
| 108 | + } | |
| 109 | + | |
| 110 | + if(j == field.selectedIndex && opt.id && in_array(options, getId(opt))) { | |
| 111 | + selected = opt.id; | |
| 112 | + } | |
| 113 | + } | |
| 114 | + | |
| 115 | + field.selectedIndex = 0; | |
| 116 | + replaceChildNodes(field, null); | |
| 117 | + | |
| 118 | + | |
| 119 | + for(var j=0; j<newOptions.length; j++) { | |
| 120 | + var opt = newOptions[j]; | |
| 121 | + appendChildNodes(field, opt); | |
| 122 | + | |
| 123 | + if(selected != null) { | |
| 124 | + if(opt.id && opt.id == selected) { // || j == 0 && field.selectedIndex == 0) { | |
| 125 | + field.selectedIndex = j; | |
| 126 | + } | |
| 127 | + } | |
| 128 | + } | |
| 129 | + | |
| 130 | + if(selected == null) { | |
| 131 | + field.selectedIndex = 0; | |
| 132 | + field.options[0].selected = 'selected'; | |
| 133 | + } | |
| 134 | + | |
| 135 | + | |
| 136 | + clearInvalid(connections[fieldId][i]); | |
| 137 | + } | |
| 138 | + } | |
| 139 | + } | |
| 140 | + | |
| 141 | + // instead of clearing here, we remove the non-applicable options | |
| 142 | + // this should handle the case with existing selections | |
| 143 | + clearInvalid(masterId); | |
| 144 | + | |
| 145 | + | |
| 146 | + function populateForSelection(selectedId) { | |
| 147 | + if(selectedId in lookups) { | |
| 148 | + for(var i=0; i<lookups[selectedId].length; i++) { | |
| 149 | + var lookupId = lookups[selectedId][i]; | |
| 150 | + var lookupInfo = fieldLookups[lookupId]; | |
| 151 | + | |
| 152 | + var parent = $('field_' + lookupInfo['parent']); | |
| 153 | + appendChildNodes(parent, | |
| 154 | + OPTION({'value':lookupInfo['value'], 'id':'lookup_' + lookupId}, | |
| 155 | + lookupInfo['value'])); | |
| 156 | + parent.disabled = false; | |
| 157 | + } | |
| 158 | + } | |
| 159 | + } | |
| 160 | + | |
| 161 | + | |
| 162 | + forEach(getElementsByTagAndClassName(null, 'is_conditional_' + masterId), function(elm) { | |
| 163 | + // check if this field connects to anything else | |
| 164 | + var fieldId = elm.id.substring('field_'.length); | |
| 165 | + if(fieldId in connections && connections[fieldId].length) { | |
| 166 | + var controller = true; | |
| 167 | + } | |
| 168 | + | |
| 169 | + if(controller) { | |
| 170 | + connect(elm, 'onchange', | |
| 171 | + function() { | |
| 172 | + var selectedId = elm.options[elm.selectedIndex].id.substring('lookup_'.length); | |
| 173 | + var touched = []; | |
| 174 | + clearConnected(fieldId); | |
| 175 | + populateForSelection(selectedId); | |
| 176 | + }); | |
| 177 | + } | |
| 178 | + }); | |
| 179 | + | |
| 180 | + //---------- End Inner Functions | |
| 181 | + }); | |
| 182 | + }); | |
| 183 | + | |
| 65 | 184 | |
| 66 | - | |
| 67 | - function clearInvalid(fieldId) { | |
| 68 | - if(!fieldId in connections || !connections[fieldId].length) { | |
| 69 | - return; | |
| 70 | - } | |
| 71 | - | |
| 72 | - var parentField = $('field_'+fieldId); | |
| 73 | - var selectedId = getId(parentField.options[parentField.selectedIndex]); | |
| 74 | - var options = lookups[selectedId]; | |
| 75 | - | |
| 76 | - if(parentField.options[parentField.selectedIndex].innerHTML == NOSELECTION) { | |
| 77 | - clearConnected(fieldId); | |
| 78 | - } else { | |
| 79 | - for(var i=0; i<connections[fieldId].length; i++) { | |
| 80 | - var field = $('field_'+connections[fieldId][i]); | |
| 81 | - var newOptions = []; | |
| 82 | - var selected = null; | |
| 83 | - | |
| 84 | - for(var j=0; j<field.options.length; j++) { | |
| 85 | - var opt = field.options[j]; | |
| 86 | - if(!(opt.innerHTML != NOSELECTION && !in_array(options, getId(opt)))) { | |
| 87 | - newOptions.push(opt); | |
| 88 | - } | |
| 89 | - | |
| 90 | - if(j == field.selectedIndex && opt.id && in_array(options, getId(opt))) { | |
| 91 | - selected = opt.id; | |
| 92 | - } | |
| 93 | - } | |
| 94 | - | |
| 95 | - field.selectedIndex = 0; | |
| 96 | - replaceChildNodes(field, null); | |
| 97 | - | |
| 98 | - | |
| 99 | - for(var j=0; j<newOptions.length; j++) { | |
| 100 | - var opt = newOptions[j]; | |
| 101 | - appendChildNodes(field, opt); | |
| 102 | - | |
| 103 | - if(selected != null) { | |
| 104 | - if(opt.id && opt.id == selected) { // || j == 0 && field.selectedIndex == 0) { | |
| 105 | - field.selectedIndex = j; | |
| 106 | - } | |
| 107 | - } | |
| 108 | - } | |
| 109 | - | |
| 110 | - if(selected == null) { | |
| 111 | - field.selectedIndex = 0; | |
| 112 | - field.options[0].selected = 'selected'; | |
| 113 | - } | |
| 114 | - | |
| 115 | - | |
| 116 | - clearInvalid(connections[fieldId][i]); | |
| 117 | - } | |
| 118 | - } | |
| 119 | - } | |
| 120 | - | |
| 121 | - // instead of clearing here, we remove the non-applicable options | |
| 122 | - // this should handle the case with existing selections | |
| 123 | - clearInvalid(masterId); | |
| 124 | - | |
| 125 | - | |
| 126 | - function populateForSelection(selectedId) { | |
| 127 | - if(selectedId in lookups) { | |
| 128 | - for(var i=0; i<lookups[selectedId].length; i++) { | |
| 129 | - var lookupId = lookups[selectedId][i]; | |
| 130 | - var lookupInfo = fieldLookups[lookupId]; | |
| 131 | - | |
| 132 | - var parent = $('field_' + lookupInfo['parent']); | |
| 133 | - appendChildNodes(parent, | |
| 134 | - OPTION({'value':lookupInfo['value'], 'id':'lookup_' + lookupId}, | |
| 135 | - lookupInfo['value'])); | |
| 136 | - parent.disabled = false; | |
| 137 | - } | |
| 138 | - } | |
| 139 | - } | |
| 140 | - | |
| 141 | - | |
| 142 | - forEach(getElementsByTagAndClassName(null, 'is_conditional_' + masterId), function(elm) { | |
| 143 | - // check if this field connects to anything else | |
| 144 | - var fieldId = elm.id.substring('field_'.length); | |
| 145 | - if(fieldId in connections && connections[fieldId].length) { | |
| 146 | - var controller = true; | |
| 147 | - } | |
| 148 | - | |
| 149 | - if(controller) { | |
| 150 | - connect(elm, 'onchange', | |
| 151 | - function() { | |
| 152 | - var selectedId = elm.options[elm.selectedIndex].id.substring('lookup_'.length); | |
| 153 | - var touched = []; | |
| 154 | - clearConnected(fieldId); | |
| 155 | - populateForSelection(selectedId); | |
| 156 | - }); | |
| 157 | - } | |
| 158 | - }); | |
| 159 | 185 | } |
| 160 | 186 | } |
| 161 | 187 | ... | ... |