Commit d10ad037a311d7aa39ff815d4ccc1eefb4b83283

Authored by Kevin Fourie
1 parent 559bc597

Merge of Bryn Divey's work on Conditional Metadata into DEV branch.

Committed By: Kevin Fourie
Reviewed By: Conrad Vermeulen

git-svn-id: https://kt-dms.svn.sourceforge.net/svnroot/kt-dms/trunk@7935 c91229c3-7414-0410-bfa2-8a42b809f60b
lib/metadata/fieldsetregistry.inc.php
... ... @@ -97,12 +97,53 @@ class KTFieldsetRegistry {
97 97  
98 98 // FIXME delegate.
99 99 $oFieldset =& $fieldsetOrType;
100   - if ($oFieldset->getIsConditional()) {
101   - return PEAR::raiseError(_kt("Conditional Fieldsets are not yet implemented"));
102   - } else {
  100 +
103 101 $widgets = array();
104 102 $fields = $oFieldset->getFields();
105 103  
  104 + if ($oFieldset->getIsConditional()) {
  105 + $iMasterId = $oFieldset->getMasterFieldId();
  106 +
  107 + $oMasterField = DocumentField::get($iMasterId);
  108 +
  109 + $newfields = array();
  110 + $newfields[] = $oMasterField;
  111 + foreach($fields as $oField) {
  112 + if($oField->getId() != $iMasterId) {
  113 + $newfields[] = $oField;
  114 + }
  115 + }
  116 +
  117 + foreach ($newfields as $oField) {
  118 + $fname = 'metadata_' . $oField->getId();
  119 + $value = null;
  120 +
  121 + if (!is_null($oDocument)) {
  122 + $oFL = DocumentFieldLink::getByDocumentAndField($oDocument, $oField);
  123 + if (!is_null($oFL) && (!PEAR::isError($oFL))) {
  124 + $value = $oFL->getValue();
  125 + }
  126 + }
  127 +
  128 + $widgets[] = $this->oWF->get('ktcore.widgets.conditionalselection',
  129 + array(
  130 + 'label' => $oField->getName(),
  131 + 'required' => $oField->getIsMandatory(),
  132 + 'name' => $fname,
  133 + 'value' => $value,
  134 + 'description' => $oField->getDescription(),
  135 + 'vocab' => MetaData::getEnabledByDocumentField($oField),
  136 + 'id_method' => 'getName',
  137 + 'label_method' => 'getName',
  138 + 'unselected_label' => _kt("No selection."),
  139 + 'simple_select' => false,
  140 + 'master' => ($oField->getId() == $iMasterId),
  141 + 'masterid' => $iMasterId,
  142 + 'fieldset' => $oFieldset->getId(),
  143 + 'field' => $oField->getId(),
  144 + ));
  145 + }
  146 + } else {
106 147 foreach ($fields as $oField) {
107 148  
108 149 $fname = 'metadata_' . $oField->getId();
... ... @@ -165,13 +206,17 @@ class KTFieldsetRegistry {
165 206 }
166 207 }
167 208  
168   - return array($this->oWF->get('ktcore.widgets.fieldset',array(
  209 +
  210 + }
  211 +
  212 + return array($this->oWF->get('ktcore.widgets.fieldset',
  213 + array(
169 214 'label' => $oFieldset->getName(),
170 215 'description' => $oFieldset->getDescription(),
171 216 'name' => $sContainerName,
172 217 'widgets' => $widgets,
173 218 )));
174   - }
  219 +
175 220 }
176 221  
177 222  
... ... @@ -184,7 +229,26 @@ class KTFieldsetRegistry {
184 229 // FIXME delegate.
185 230 $oFieldset =& $fieldsetOrType;
186 231 if ($oFieldset->getIsConditional()) {
187   - return PEAR::raiseError(_kt("Conditional Fieldsets are not yet implemented"));
  232 + $validators = array();
  233 + $fields = $oFieldset->getFields();
  234 +
  235 + if ($bIncludeAuto) {
  236 + $widgets = $this->widgetsForFieldset($oFieldset, $sContainerName, $sDocument);
  237 + $validators = kt_array_merge($validators, $widgets[0]->getValidators());
  238 + }
  239 +
  240 + foreach ($fields as $oField) {
  241 + $fname = 'metadata_' . $oField->getId();
  242 +
  243 + // Change back to 'membership'
  244 + $validators[] = $this->oVF->get('ktcore.validators.membership',
  245 + array(
  246 + 'test' => $fname,
  247 + 'output' => $fname,
  248 + 'vocab' => MetaData::getEnabledValuesByDocumentField($oField),
  249 + 'id_method' => 'getName',
  250 + ));
  251 + }
188 252 } else {
189 253 $validators = array();
190 254 $fields = $oFieldset->getFields();
... ... @@ -243,13 +307,14 @@ class KTFieldsetRegistry {
243 307 }
244 308 }
245 309  
246   - return array($this->oVF->get('ktcore.validators.fieldset',array(
  310 + }
  311 + return array($this->oVF->get('ktcore.validators.fieldset',
  312 + array(
247 313 'test' => $sContainerName,
248 314 'output' => $sContainerName,
249 315 'validators' => $validators,
250 316 )));
251 317 }
252 318 }
253   -}
254 319  
255 320 ?>
... ...
plugins/ktcore/KTCorePlugin.php
... ... @@ -181,6 +181,8 @@ class KTCorePlugin extends KTPlugin {
181 181 $this->registerWidget('KTDescriptorSelectionWidget', 'ktcore.widgets.descriptorselection', 'KTWidgets.php');
182 182 $this->registerWidget('KTCoreFolderCollectionWidget', 'ktcore.widgets.foldercollection', 'KTWidgets.php');
183 183  
  184 + $this->registerWidget('KTCoreConditionalSelectionWidget', 'ktcore.widgets.conditionalselection', 'KTWidgets.php');
  185 +
184 186 $this->registerPage('collection', 'KTCoreCollectionPage', 'KTWidgets.php');
185 187 $this->registerPage('notifications', 'KTNotificationOverflowPage', 'KTMiscPages.php');
186 188  
... ...
plugins/ktcore/KTWidgets.php
... ... @@ -767,4 +767,160 @@ class KTCoreCollectionPage extends KTStandardDispatcher {
767 767 }
768 768  
769 769  
770   -?>
  770 +
  771 +// based on the selection widget, this carries a mapping array,
  772 +// which is converted to JSON and inserted into the output. javascript
  773 +// enforces the various relationships between conditional fields.
  774 +
  775 +class KTCoreConditionalSelectionWidget extends KTCoreSelectionWidget {
  776 + var $sNamespace = 'ktcore.widgets.conditionalselection';
  777 +
  778 + var $sIdMethod;
  779 + var $sLabelMethod;
  780 +
  781 + var $bIsMaster;
  782 + var $bMappings;
  783 +
  784 + function _getFieldIdForMetadataId($iMetadata) {
  785 + $sTable = 'metadata_lookup';
  786 + $sQuery = "SELECT document_field_id FROM " . $sTable . " WHERE id = ?";
  787 + $aParams = array($iMetadata);
  788 +
  789 + $res = DBUtil::getOneResultKey(array($sQuery, $aParams), 'document_field_id');
  790 + if (PEAR::isError($res)) {
  791 + return false;
  792 + }
  793 + return $res;
  794 + }
  795 +
  796 +
  797 + function configure($aOptions) {
  798 + $res = parent::configure($aOptions);
  799 + if (PEAR::isError($res)) {
  800 + return $res;
  801 + }
  802 +
  803 + $this->sIdMethod = KTUtil::arrayGet($aOptions, 'id_method', 'getId');
  804 + $this->sLabelMethod = KTUtil::arrayGet($aOptions, 'label_method');
  805 + if (empty($this->sLabelMethod)) {
  806 + return PEAR::raiseError(_kt('No label method specified.'));
  807 + }
  808 + $existing_entities = (array) KTUtil::arrayGet($aOptions, 'existing_entities');
  809 +
  810 + if (empty($this->value)) {
  811 + $this->value = array();
  812 + foreach ($existing_entities as $oEntity) {
  813 + $this->value[] = call_user_func(array(&$oEntity, $this->sIdMethod));
  814 + }
  815 + }
  816 +
  817 + $this->iField = KTUtil::arrayGet($aOptions, 'field');
  818 + $this->iMasterId = KTUtil::arrayGet($aOptions, 'masterid');
  819 +
  820 + // if we're the master, we have to build the dependancy array and store it as JSON
  821 + // also, include the javascript
  822 + if(KTUtil::arrayGet($aOptions, 'master', false)) {
  823 + $this->bMaster = true;
  824 + $this->aJavascript = array('resources/js/conditional_selection.js');
  825 +
  826 + $oFieldset = KTFieldset::get(KTUtil::arrayGet($aOptions, 'fieldset'));
  827 + $aLookups = array();
  828 + $aConnections = array();
  829 +
  830 + foreach($oFieldset->getFields() as $oField) {
  831 + $c = array();
  832 +
  833 + foreach($oField->getEnabledValues() as $oMetadata) {
  834 + $a = array();
  835 + // print '<pre>';
  836 +
  837 + $nvals = KTMetadataUtil::getNextValuesForLookup($oMetadata->getId());
  838 + if($nvals) {
  839 + foreach($nvals as $i=>$aVals) {
  840 + $a = array_merge($a, $aVals);
  841 +
  842 + foreach($aVals as $id) {
  843 + $field = $this->_getFieldIdForMetadataId($id);
  844 + // print 'id ' . $id . ' is in field ' . $field . "<br/>";
  845 + if(!in_array($field, $c)) {
  846 + $c[] = $field;
  847 + }
  848 + }
  849 + }
  850 + }
  851 +
  852 + $aLookups[$oMetadata->getId()] = $a;
  853 + }
  854 + $aConnections[$oField->getId()] = $c;
  855 + }
  856 +
  857 + //exit(0);
  858 +
  859 + $oJSON = new Services_JSON;
  860 + $this->sLookupsJSON = $oJSON->encode($aLookups);
  861 + $this->sConnectionsJSON = $oJSON->encode($aConnections);
  862 + }
  863 +
  864 +
  865 + $new_vocab = array();
  866 + foreach ($this->aVocab as $oEntity) {
  867 + $id = call_user_func(array(&$oEntity, $this->sIdMethod));
  868 + $label = call_user_func(array(&$oEntity, $this->sLabelMethod));
  869 + $new_vocab[$id] = array($label, $oEntity->getId());
  870 + }
  871 + $this->aVocab = $new_vocab;
  872 + }
  873 +
  874 + function getWidget() {
  875 + $bHasErrors = false;
  876 + if (count($this->aErrors) != 0) { $bHasErrors = true; }
  877 +
  878 + $this->sTemplate = 'ktcore/forms/widgets/conditional_selection';
  879 +
  880 + $oTemplating =& KTTemplating::getSingleton();
  881 + $oTemplate = $oTemplating->loadTemplate($this->sTemplate);
  882 +
  883 + $unselected = KTUtil::arrayGet($this->aOptions, 'unselected_label');
  884 + if (!empty($unselected)) {
  885 + $vocab = array();
  886 + $vocab[] = $unselected;
  887 + foreach ($this->aVocab as $k => $v) {
  888 + $vocab[$k] = $v;
  889 + }
  890 + $this->aVocab = $vocab;
  891 + if (empty($this->value)) {
  892 + $this->value = '0';
  893 + }
  894 + }
  895 +
  896 + if ($this->bMulti) {
  897 + $this->_valuesearch = array();
  898 + $value = (array) $this->value;
  899 + foreach ($value as $v) {
  900 + $this->_valuesearch[$v] = true;
  901 + }
  902 + }
  903 +
  904 + $aTemplateData = array(
  905 + 'context' => $this,
  906 + 'name' => $this->sName,
  907 + 'has_id' => ($this->sId !== null),
  908 + 'id' => $this->sId,
  909 + 'has_value' => ($this->value !== null),
  910 + 'value' => $this->value,
  911 + 'options' => $this->aOptions,
  912 + 'vocab' => $this->aVocab,
  913 + 'lookups' => $this->sLookupsJSON,
  914 + 'connections' => $this->sConnectionsJSON,
  915 + 'master' => $this->bMaster,
  916 + 'masterid' => $this->iMasterId,
  917 + 'field' => $this->iField,
  918 + );
  919 + return $oTemplate->render($aTemplateData);
  920 + }
  921 +
  922 +
  923 +
  924 +}
  925 +
  926 +
... ...
plugins/ktcore/admin/fieldsets/conditional.inc.php
... ... @@ -414,6 +414,16 @@ ordering!&quot;);
414 414  
415 415 $oField = $data['master_field'];
416 416  
  417 + // remove all existing behaviors
  418 + $aFieldIds = array();
  419 + foreach($this->oFieldset->getFields() as $i) {
  420 + $aFieldIds[] = $i->getId();
  421 + }
  422 +
  423 + $sTable = KTUtil::getTableName('field_behaviours');
  424 + $aQuery = array("DELETE FROM $sTable WHERE field_id IN (" . DBUtil::paramArray($aFieldIds) . ")", $aFieldIds);
  425 + $res = DBUtil::runQuery($aQuery);
  426 +
417 427 $res = KTMetadataUtil::removeFieldOrdering($this->oFieldset);
418 428 $this->oFieldset->setMasterFieldId($oField->getId());
419 429 $res = $this->oFieldset->update();
... ...
resources/js/conditional_selection.js 0 โ†’ 100755
  1 +// 'lookups' and 'connections' are produced by the master conditional widget
  2 +var NOSELECTION = 'No selection.';
  3 +
  4 +function ConditionalSelection() {
  5 +}
  6 +
  7 +function in_array(a, val) {
  8 + if(!a.length) {
  9 + return;
  10 + }
  11 + for(var i=0;i<a.length;i++) {
  12 + if(a[i] == val) {
  13 + return true;
  14 + }
  15 + }
  16 + return false;
  17 +}
  18 +
  19 +function getId(elm) {
  20 + if(elm.id.search('field_') != -1) {
  21 + return elm.id.substring('field_'.length);
  22 + } else if(elm.id.search('lookup_') != -1) {
  23 + return elm.id.substring('lookup_'.length);
  24 + }
  25 + return false;
  26 +}
  27 +
  28 +ConditionalSelection.prototype = {
  29 + 'initialize' : function(masterId) {
  30 + // make clones of the original nodes to keep the options in
  31 + var fieldLookups = {};
  32 + var current = {};
  33 + var lookups = eval('lookups_' + masterId);
  34 + 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 + }
  65 +
  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 + }
  160 +}
  161 +
  162 +
  163 +
  164 +addLoadEvent(function() {
  165 + var masters = getElementsByTagAndClassName('select', 'is_master');
  166 + for(var i=0; i<masters.length; i++) {
  167 + var elm = masters[i];
  168 + var masterId = getId(elm);
  169 + var d = new ConditionalSelection();
  170 + d.initialize(masterId);
  171 + }
  172 +});
... ...
templates/ktcore/forms/widgets/conditional_selection.smarty 0 โ†’ 100755
  1 +{if $master}
  2 +<script type="text/javascript">
  3 +var lookups_{$masterid} = {$lookups};
  4 +var connections_{$masterid} = {$connections};
  5 +</script>
  6 +{/if}
  7 +
  8 +
  9 +{if empty($vocab)}
  10 + <div class="ktInfoMessage"><span>{$context->sEmptyMessage}</span></div>
  11 +{else}
  12 + <select name="{$name}" class="is_conditional_{$masterid}{if $master} is_master{/if}"
  13 + id="field_{$field}"
  14 + >
  15 + {if $options.initial_string}
  16 + <option value="">{$options.initial_string}</option>
  17 + {/if}
  18 + {foreach item=lookup key=lookup_key from=$vocab}
  19 + {if is_array($lookup)}
  20 + <option value="{$lookup_key}" id="lookup_{$lookup.1}"
  21 + {if ($value == $lookup_key)} selected="selected"{/if}>{$lookup.0}</option>
  22 + {else}
  23 + <option value="{$lookup_key}"
  24 + {if ($value == $lookup_key)} selected="selected"{/if}>{$lookup}</option>
  25 + {/if}
  26 + {/foreach}
  27 + </select>
  28 +{/if}
... ...
templates/ktcore/metadata/conditional/conditional_admin_overview.smarty
... ... @@ -29,30 +29,6 @@ values in the other fields. Only &lt;strong&gt;lookup&lt;/strong&gt; fields can be added to
29 29 </p>
30 30  
31 31  
32   -<p>
33   - <a class="ktAction ktEdit ktActionDescribed" href="{addQS context=$context}fieldset_action=renameBehaviours{/addQS}">{i18n}Rename Behaviours{/i18n}</a>
34   - <a href="{addQS context=$context}fieldset_action=renameBehaviours{/addQS}">{i18n}Rename Behaviours{/i18n}</a>
35   -</p>
36   -
37   -{if $oFieldset->getIsComplex()}
38   -<p>{i18n}The fieldset is currently designated as <strong>Complex</strong>.{/i18n} {i18n}Changing the conditional type set will remove all existing field
39   -ordering!{/i18n}</p>
40   -
41   -
42   -<p><a href="{addQS context=$context}fieldset_action=changeToSimple{/addQS}"
43   - class="ktAction ktActionDescribed ktEdit">{i18n}Change to simple{/i18n}</a>
44   -<a href="{addQS context=$context}fieldset_action=changeToSimple{/addQS}">{i18n}Change to simple{/i18n}</a></p>
45   -
46   -{else}
47   -
48   -<p class="descriptiveText important">{i18n}The fieldset is currently designated as <strong>Simple</strong>.{/i18n}</p>
49   -
50   -<p><a href="{addQS context=$context}fieldset_action=changeToComplex{/addQS}"
51   - class="ktAction ktActionDescribed ktEdit">{i18n}Change to complex{/i18n}</a>
52   -<a href="{addQS context=$context}fieldset_action=changeToComplex{/addQS}">{i18n}Change to complex{/i18n}</a></p>
53   -
54   -{/if}
55   -
56 32 </fieldset>
57 33  
58 34 <br />
... ...
templates/ktcore/metadata/conditional/manageConditional.smarty
1 1 <h2>{i18n}Manage conditional fieldset{/i18n}</h2>
2 2  
3 3 <p class="descriptiveText">{i18n}Conditional fieldsets allow you to restrict the options
4   -a user has for values in some fields based on the values in other fields. There
5   -are two kinds of conditional fieldsets: <strong>Simple</strong> and <strong>Complex</strong>
6   -. Simple fieldsets should be sufficient for most things: they allow you to say that
  4 +a user has for values in some fields based on the values in other fields, allowing you to say that
7 5 the values of one field are restricted to a certain subset of values if another field
8 6 has a specific value. For example, you could say that if the field "Street" is "Jeffrey",
9 7 then the field "Residents" must be one of "Jones","Smith" or "Friedman".{/i18n}</p>
10 8  
11   -<p class="descriptiveText">{i18n}Complex fieldsets allow you to give far more detailed structure to
12   -your information: The value of "Residents" can depend not only on "Street", but on
13   -how the user was allowed to select the specific street (given another field).{/i18n}</p>
14 9  
15 10 {* don't show warnings until the basics are done. *}
16 11  
... ... @@ -42,29 +37,6 @@ to complete{/i18n}: {$sIncomplete}&lt;/span&gt;
42 37  
43 38 <h2>{i18n}Conditional type{/i18n}</h2>
44 39  
45   -{if $oFieldset->getIsComplex()}
46   -<p>{i18n}The fieldset is currently designated as <strong>Complex</strong>{/i18n}</p>
47   -
48   -{if ($oMasterField && empty($free_fields))}<p><a
49   -class="ktActionLink ktEdit"
50   -href="{$rootUrl}/plugins/ktcore/admin/manageConditionals.php?action=editComplexFieldset&fieldset_id={$oFieldset->getId()}">{i18n}Manage
51   -complex conditional{/i18n}</a></p>{/if}
52   -
53   -<form action="{$smarty.server.PHP_SELF}" method="POST">
54   -<input type="hidden" name="action" value="changeToSimple" />
55   -<input type="hidden" name="fFieldsetId" value="{$oFieldset->getId()}" />
56   -<input type="submit" name="submit" value="{i18n}Change to simple{/i18n}" />
57   -</form>
58   -
59   -{else}
60   -
61   -<p>{i18n}The fieldset is currently designated as <strong>Simple</strong>{/i18n}</p>
62   -
63   -{if ($oMasterField && empty($free_fields))}<p><a
64   -class="ktActionLink ktEdit"
65   -href="{$rootUrl}/plugins/ktcore/admin/manageConditionals.php?action=editFieldset&fieldset_id={$oFieldset->getId()}">{i18n}Manage simple conditional{/i18n}</a>
66   -</p>
67   -{/if}
68 40  
69 41 <form action="{$smarty.server.PHP_SELF}" method="POST">
70 42 <input type="hidden" name="action" value="changeToComplex" />
... ...