Commit 95637c2003dea3d8c1ffdeebf6da2fb69cc2d167

Authored by Brad Shuttleworth
1 parent 12f57aa2

Brad Shuttleworth 2006-02-15 fix KTS-164

Brad Shuttleworth 2006-02-15 fix for KTS-145.
    Brad Shuttleworth 2006-02-15 getByDocumentAndField needs to use oField t...
    Brad Shuttleworth 2006-02-14 make transition screen more helpful.
    Brad Shuttleworth 2006-02-14 fix for KTS-384
    Brad Shuttleworth 2006-02-14 attempt a fix of the folder::copy bug,.
    Brad Shuttleworth 2006-02-14 make password length admin override a confi...
    Brad Shuttleworth 2006-02-14 beef up the way that users are fetched...
    Brad Shuttleworth 2006-02-14 folder delete errors were using "array_join...


git-svn-id: https://kt-dms.svn.sourceforge.net/svnroot/kt-dms/trunk@4927 c91229c3-7414-0410-bfa2-8a42b809f60b
config/config.ini
... ... @@ -149,3 +149,7 @@ antiword = antiword
149 149 ; minimum password length on password-setting
150 150 ; could be moved into DB-auth-config
151 151 passwordLength = 6
  152 +
  153 +; apply the minimum password length to admin while creating / editing accounts?
  154 +; default is set to "false" meaning that admins can create users with shorter passwords.
  155 +restrictAdminPasswords = default
... ...
config/dmsDefaults.php
... ... @@ -390,6 +390,9 @@ $oKTConfig->setdefaultns("tweaks", "genericMetaDataRequired", true);
390 390 $oKTConfig->setdefaultns("tweaks", "phpErrorLogFile", false);
391 391 $oKTConfig->setdefaultns("tweaks", "developmentWindowLog", false);
392 392  
  393 +$oKTConfig->setdefaultns("user_prefs", "passwordLength", 6);
  394 +$oKTConfig->setdefaultns("user_prefs", "restrictAdminPasswords", false);
  395 +
393 396 $oKTConfig->setdefaultns("ui", "ieGIF", true);
394 397 $oKTConfig->setdefaultns("ui", "alwaysShowAll", false);
395 398  
... ...
lib/documentmanagement/DocumentFieldLink.inc
... ... @@ -163,7 +163,7 @@ class DocumentFieldLink extends KTEntity {
163 163 $iMetadataVersionId = $oDocument->getMetadataVersionId();
164 164 return KTEntityUtil::getByDict('DocumentFieldLink', array(
165 165 'metadata_version_id' => $iMetadataVersionId,
166   - 'document_field_id' => KTUtil::getId($oDocument),
  166 + 'document_field_id' => KTUtil::getId($oField),
167 167 ));
168 168 }
169 169 }
... ...
lib/foldermanagement/folderutil.inc.php
... ... @@ -223,10 +223,10 @@ class KTFolderUtil {
223 223 $sFD = '';
224 224 $sFF = '';
225 225 if (!empty($aFailedDocuments)) {
226   - $sFD = _('Documents: ') . array_join(', ', $aFailedDocuments) . '. ';
  226 + $sFD = _('Documents: ') . implode(', ', $aFailedDocuments) . '. ';
227 227 }
228 228 if (!empty($aFailedFolders)) {
229   - $sFF = _('Folders: ') . array_join(', ', $aFailedFolders) . '.';
  229 + $sFF = _('Folders: ') . implode(', ', $aFailedFolders) . '.';
230 230 }
231 231 return PEAR::raiseError(_('You do not have permission to delete these items. ') . $sFD . $sFF);
232 232 }
... ... @@ -267,7 +267,7 @@ class KTFolderUtil {
267 267 $oPerm = KTPermission::getByName('ktcore.permissions.read');
268 268 $oBaseFolderPerm = KTPermission::getByName('ktcore.permissions.addFolder');
269 269  
270   - if (!KTPermissionUtil::userHasPermissionOnItem($oUser, $oPerm, $oDestFolder)) {
  270 + if (!KTPermissionUtil::userHasPermissionOnItem($oUser, $oBaseFolderPerm, $oDestFolder)) {
271 271 return PEAR::raiseError(_('You are not allowed to create folders in the destination.'));
272 272 }
273 273  
... ... @@ -298,7 +298,7 @@ class KTFolderUtil {
298 298 // child documents
299 299 $aChildDocs = Document::getList(array('folder_id = ?',array($iFolderId)));
300 300 foreach ($aChildDocs as $oDoc) {
301   - if (KTPermissionUtil::userHasPermissionOnItem($oUser, $oPerm, $oFolder)) {
  301 + if (KTPermissionUtil::userHasPermissionOnItem($oUser, $oPerm, $oDoc)) {
302 302 $aDocuments[] = $oDoc;
303 303 } else {
304 304 $aFailedDocuments[] = $oDoc->getName();
... ... @@ -314,10 +314,10 @@ class KTFolderUtil {
314 314 $sFD = '';
315 315 $sFF = '';
316 316 if (!empty($aFailedDocuments)) {
317   - $sFD = _('Documents: ') . array_join(', ', $aFailedDocuments) . '. ';
  317 + $sFD = _('Documents: ') . implode(', ', $aFailedDocuments) . '. ';
318 318 }
319 319 if (!empty($aFailedFolders)) {
320   - $sFF = _('Folders: ') . array_join(', ', $aFailedFolders) . '.';
  320 + $sFF = _('Folders: ') . implode(', ', $aFailedFolders) . '.';
321 321 }
322 322 return PEAR::raiseError(_('You do not have permission to copy these items. ') . $sFD . $sFF);
323 323 }
... ... @@ -325,6 +325,9 @@ class KTFolderUtil {
325 325 // first we walk the tree, creating in the new location as we go.
326 326 // essentially this is an "ok" pass.
327 327  
  328 +
  329 + $oStorage =& KTStorageManagerUtil::getSingleton();
  330 +
328 331 $aFolderMap = array();
329 332  
330 333 $sTable = KTUtil::getTableName('folders');
... ... @@ -339,7 +342,13 @@ class KTFolderUtil {
339 342 return $id;
340 343 }
341 344 $aFolderMap[$oFolder->getId()] = $id;
342   -
  345 + $oNewBaseFolder = Folder::get($id);
  346 + $res = $oStorage->createFolder($oNewBaseFolder);
  347 + if (PEAR::isError($res)) {
  348 + // it doesn't exist, so rollback and raise..
  349 + DBUtil::rollback();
  350 + return $res;
  351 + }
343 352 $aRemainingFolders = Folder::getList(array('parent_id = ?', array($oFolder->getId())), array('ids' => true));
344 353  
345 354  
... ... @@ -359,34 +368,30 @@ class KTFolderUtil {
359 368 return $id;
360 369 }
361 370 $aFolderMap[$iFolderId] = $id;
  371 + $oNewFolder = Folder::get($id);
  372 + $res = $oStorage->createFolder($oNewFolder);
  373 + if (PEAR::isError($res)) {
  374 + // first delete, then rollback, then fail out.
  375 + $oStorage->removeFolder($oNewBaseFolder);
  376 + DBUtil::rollback();
  377 + return $res;
  378 + }
362 379  
363 380 $aCFIds = Folder::getList(array('parent_id = ?', array($iFolderId)), array('ids' => true));
364 381 $aRemainingFolders = array_merge($aRemainingFolders, $aCFIds);
365 382 }
366 383  
  384 +
  385 +
367 386 // now we can go ahead.
368 387 foreach ($aDocuments as $oDocument) {
369 388 $oChildDestinationFolder = Folder::get($aFolderMap[$oDocument->getFolderID()]);
370 389 $res = KTDocumentUtil::copy($oDocument, $oChildDestinationFolder);
371   - if (PEAR::isError($res)) {
  390 + if (PEAR::isError($res) || ($res === false)) {
372 391 DBUtil::rollback();
373   - return PEAR::raiseError(_('Delete Aborted. Unexpected failure to delete document: ') . $oDocument->getName() . $res->getMessage());
  392 + return PEAR::raiseError(_('Delete Aborted. Unexpected failure to copydocument: ') . $oDocument->getName() . $res->getMessage());
374 393 }
375 394 }
376   -
377   - $oStorage =& KTStorageManagerUtil::getSingleton();
378   - $oStorage->removeFolderTree($oStartFolder);
379   -
380   - // documents all cleared.
381   - $sQuery = 'DELETE FROM ' . KTUtil::getTableName('folders') . ' WHERE id IN (' . DBUtil::paramArray($aFolderIds) . ')';
382   - $aParams = $aFolderIds;
383   -
384   - $res = DBUtil::runQuery(array($sQuery, $aParams));
385   -
386   - if (PEAR::isError($res)) {
387   - DBUtil::rollback();
388   - return PEAR::raiseError(_('Failure deleting folders.'));
389   - }
390 395  
391 396 // and store
392 397 DBUtil::commit();
... ...
lib/groups/Group.inc
... ... @@ -155,7 +155,7 @@ class Group extends KTEntity {
155 155 $aMembers = array();
156 156 foreach ($aUserIds as $iUserId) {
157 157 $oUser = User::get($iUserId);
158   - if ($oUser !== false) {
  158 + if ((!PEAR::isError($oUser)) && ($oUser !== false)) {
159 159 $aMembers[] = $oUser;
160 160 }
161 161 }
... ... @@ -170,7 +170,7 @@ class Group extends KTEntity {
170 170 $aMembers = array();
171 171 foreach ($aGroupIds as $iGroupId) {
172 172 $oGroup = Group::get($iGroupId);
173   - if ($oGroup !== false) {
  173 + if ((!PEAR::isError($oUser)) && ($oGroup !== false)) {
174 174 $aMembers[] = $oGroup;
175 175 }
176 176 }
... ...
plugins/ktcore/admin/ajaxComplexConditionals.php
... ... @@ -62,6 +62,88 @@ class AjaxConditionalAdminDispatcher extends KTAdminDispatcher {
62 62 return $oTemplate->render();
63 63 }
64 64  
  65 + function do_removeFromBehaviour() {
  66 + $oFieldset =& $this->oValidator->validateFieldset(KTUtil::arrayGet($_REQUEST, 'fieldset_id'));
  67 + $field_id = KTUtil::arrayGet($_REQUEST, 'field_id');
  68 + $oField =& $this->oValidator->validateField(KTUtil::arrayGet($_REQUEST, 'field_id'));
  69 +
  70 + header('Content-type: application/xml');
  71 +
  72 + $instances = (array) KTUtil::arrayGet($_REQUEST, 'fieldsToRemove');
  73 +
  74 + $this->startTransaction();
  75 +
  76 + foreach ($instances as $iInstanceId) {
  77 + $oInstance = KTValueInstance::get($iInstanceId);
  78 + if (PEAR::isError($oInstance) || ($oInstance === false)) {
  79 + $this->rollbackTransaction();
  80 + return 'Not OK.';
  81 + }
  82 +
  83 + $res = $oInstance->delete();
  84 + if (PEAR::isError($res) || ($res === false)) {
  85 + $this->rollbackTransaction();
  86 + return 'Not OK.';
  87 + }
  88 + }
  89 +
  90 + $this->commitTransaction();
  91 +
  92 + return '<empty>OK.</empty>';
  93 + }
  94 +
  95 + // get the list of ASSIGNED items for a given column, under a certain parent behaviour.
  96 + function do_getAssignedList() {
  97 + $parent_behaviour = KTUtil::arrayGet($_REQUEST, 'parent_behaviour');
  98 + //$fieldset_id = KTUtil::arrayGet($_REQUEST, 'fieldset_id'); //
  99 + $oFieldset =& $this->oValidator->validateFieldset(KTUtil::arrayGet($_REQUEST, 'fieldset_id'));
  100 + $field_id = KTUtil::arrayGet($_REQUEST, 'field_id');
  101 + $oField =& $this->oValidator->validateField(KTUtil::arrayGet($_REQUEST, 'field_id'));
  102 +
  103 + header('Content-type: application/xml');
  104 + $oTemplating =& KTTemplating::getSingleton();
  105 + $oTemplate =& $oTemplating->loadTemplate('ktcore/metadata/conditional/ajax_complex_get_item_list');
  106 +
  107 + $aValues = array();
  108 + $aBehaviours = array();
  109 + foreach ($oField->getValues() as $oValue) {
  110 + if (empty($parent_behaviour)) {
  111 + $oInstance = KTValueInstance::getByLookupSingle($oValue);
  112 + if (!empty($oInstance)) {
  113 + if (is_null($aBehaviours[$oInstance->getBehaviourId()])) {
  114 + $aBehaviours[$oInstance->getBehaviourId()] = KTFieldBehaviour::get($oInstance->getBehaviourId());
  115 + }
  116 + $aValues[$oInstance->getId()] = $oValue->getName() . ' - ' . $aBehaviours[$oInstance->getBehaviourId()]->getName();
  117 + }
  118 + // No parent behaviour (thus master column), so any
  119 + // instance will do to prevent showing this value
  120 + continue;
  121 + }
  122 +
  123 + $iInstanceId = KTValueInstance::getByLookupAndParentBehaviour($oValue, $parent_behaviour, array('ids' => true));
  124 +
  125 + if (!empty($iInstanceId)) {
  126 +
  127 + $oInstance = KTValueInstance::get($iInstanceId);
  128 +
  129 + //print $oInstance->getBehaviourId() . ' - ';
  130 + //continue;
  131 +
  132 + if (is_null($aBehaviours[$oInstance->getBehaviourId()])) {
  133 + $aBehaviours[$oInstance->getBehaviourId()] = KTFieldBehaviour::get($oInstance->getBehaviourId());
  134 + }
  135 +
  136 + $aValues[$oInstance->getId()] = $oValue->getName() . ' - ' . $aBehaviours[$oInstance->getBehaviourId()]->getName();
  137 + }
  138 + }
  139 + $aData = array(
  140 + 'values' => $aValues,
  141 + );
  142 + $oTemplate->setData($aData);
  143 +
  144 + return $oTemplate->render();
  145 + }
  146 +
65 147 function do_getBehaviourList() {
66 148 $parent_behaviour = KTUtil::arrayGet($_REQUEST, 'parent_behaviour');
67 149 $fieldset_id = KTUtil::arrayGet($_REQUEST, 'fieldset_id');
... ...
plugins/ktcore/admin/manageConditionals.php
... ... @@ -19,8 +19,8 @@ class ManageConditionalDispatcher extends KTAdminDispatcher {
19 19 global $default;
20 20 $this->ru = $default->rootUrl;
21 21 // this is not useful: we _still_ don't chain through the right dispatcher (!)
22   - $this->aBreadcrumbs[] = array('url' => $default->rootUrl . '/admin.php/documents', 'name' => _('Document Metadata and Workflow Configuration'));
23   - $this->aBreadcrumbs[] = array('url' => $default->rootUrl . '/admin.php/documents/fieldmanagement', 'name' => _('Document Field Management'));
  22 + $this->aBreadcrumbs[] = array('url' => KTUtil::ktLink('/admin.php','documents'), 'name' => _('Document Metadata and Workflow Configuration'));
  23 + $this->aBreadcrumbs[] = array('url' => KTUtil::ktLink('/admin.php','documents/fieldmanagement'), 'name' => _('Document Field Management'));
24 24  
25 25  
26 26 }
... ... @@ -49,10 +49,11 @@ class ManageConditionalDispatcher extends KTAdminDispatcher {
49 49 * we can then render in/out. Everything "intelligent" happens
50 50 * in AJAX (doing it with submits sucks arse.
51 51 *
52   - * FIXME we fake it here with nested arrays...
53 52 */
  53 +
54 54 $oFieldset =& KTFieldset::get($fieldset_id);
55 55 $aFields =& $oFieldset->getFields();
  56 +
56 57 $this->aBreadcrumbs[] = array(
57 58 'url' => $this->ru . '/admin.php/documents/fieldmanagement',
58 59 'query' => 'action=edit&fFieldsetId=' . $oFieldset->getId(),
... ... @@ -69,7 +70,7 @@ class ManageConditionalDispatcher extends KTAdminDispatcher {
69 70 "context" => &$this,
70 71 "fieldset_id" => $fieldset_id,
71 72 "aFields" => $aFields,
72   - "iMasterFieldId" => $aFields[0]->getId(),
  73 + "iMasterFieldId" => $oFieldset->getMasterFieldId(),
73 74 );
74 75 return $oTemplate->render($aTemplateData);
75 76 }
... ... @@ -104,7 +105,7 @@ class ManageConditionalDispatcher extends KTAdminDispatcher {
104 105 "context" => &$this,
105 106 "fieldset_id" => $fieldset_id,
106 107 "aFields" => $aFields,
107   - "iMasterFieldId" => $aFields[0]->getId(),
  108 + "iMasterFieldId" => $oFieldset->getMasterFieldId(),
108 109 );
109 110 return $oTemplate->render($aTemplateData);
110 111 }
... ...
plugins/ktcore/admin/userManagement.php
... ... @@ -73,13 +73,22 @@ class KTUserAdminDispatcher extends KTAdminDispatcher {
73 73  
74 74 $aOptions = array('autocomplete' => false);
75 75  
  76 + // sometimes even admin is restricted in what they can do.
  77 +
  78 + $KTConfig =& KTConfig::getSingleton();
  79 + $minLength = ((int) $KTConfig->get('user_prefs/passwordLength', 6));
  80 + $restrictAdmin = ((bool) $KTConfig->get('user_prefs/restrictAdminPasswords', false));
  81 + $passwordAddRequirement = '';
  82 + if ($restrictAdmin) {
  83 + $passwordAddRequirement = ' ' . sprintf('Password must be at least %d characters long.', $minLength);
  84 + }
76 85  
77 86 $add_fields = array();
78 87 $add_fields[] = new KTStringWidget(_('Username'),_('The username the user will enter to gain access to KnowledgeTree. e.g. <strong>jsmith</strong>'), 'username', null, $this->oPage, true, null, null, $aOptions);
79 88 $add_fields[] = new KTStringWidget(_('Name'),_('The full name of the user. This is shown in reports and listings. e.g. <strong>John Smith</strong>'), 'name', null, $this->oPage, true, null, null, $aOptions);
80 89 $add_fields[] = new KTStringWidget(_('Email Address'), _('The email address of the user. Notifications and alerts are mailed to this address if <strong>email notifications</strong> is set below. e.g. <strong>jsmith@acme.com</strong>'), 'email_address', null, $this->oPage, false, null, null, $aOptions);
81 90 $add_fields[] = new KTCheckboxWidget(_('Email Notifications'), _("If this is specified then the user will have notifications sent to the email address entered above. If it isn't set, then the user will only see notifications on the <strong>Dashboard</strong>"), 'email_notifications', true, $this->oPage, false, null, null, $aOptions);
82   - $add_fields[] = new KTPasswordWidget(_('Password'), _('Specify an initial password for the user.'), 'password', null, $this->oPage, true, null, null, $aOptions);
  91 + $add_fields[] = new KTPasswordWidget(_('Password'), _('Specify an initial password for the user.') . $passwordAddRequirement, 'password', null, $this->oPage, true, null, null, $aOptions);
83 92 $add_fields[] = new KTPasswordWidget(_('Confirm Password'), _('Confirm the password specified above.'), 'confirm_password', null, $this->oPage, true, null, null, $aOptions);
84 93 // nice, easy bits.
85 94 $add_fields[] = new KTStringWidget(_('Mobile Number'), _("The mobile phone number of the user. If the system is configured to send notifications to cellphones, then this number will be SMS'd with notifications. e.g. <strong>999 9999 999</strong>"), 'mobile_number', null, $this->oPage, false, null, null, $aOptions);
... ... @@ -162,7 +171,7 @@ class KTUserAdminDispatcher extends KTAdminDispatcher {
162 171 $this->aBreadcrumbs[] = array('url' => $_SERVER['PHP_SELF'], 'name' => _('User Management'));
163 172 $this->oPage->setBreadcrumbDetails(_('change user password'));
164 173 $this->oPage->setTitle(_("Change User Password"));
165   -
  174 +
166 175 $user_id = KTUtil::arrayGet($_REQUEST, 'user_id');
167 176 $oUser =& User::get($user_id);
168 177  
... ... @@ -193,11 +202,18 @@ class KTUserAdminDispatcher extends KTAdminDispatcher {
193 202 $password = KTUtil::arrayGet($_REQUEST, 'password');
194 203 $confirm_password = KTUtil::arrayGet($_REQUEST, 'confirm_password');
195 204  
196   - if (empty($password)) {
  205 + $KTConfig =& KTConfig::getSingleton();
  206 + $minLength = ((int) $KTConfig->get('user_prefs/passwordLength', 6));
  207 + $restrictAdmin = ((bool) $KTConfig->get('user_prefs/restrictAdminPasswords', false));
  208 +
  209 +
  210 + if ($restrictAdmin && (strlen($password) < $minLength)) {
  211 + $this->errorRedirectToMain(sprintf(_("The password must be at least %d characters long."), $minLength));
  212 + } else if (empty($password)) {
197 213 $this->errorRedirectToMain(_("You must specify a password for the user."));
198 214 } else if ($password !== $confirm_password) {
199 215 $this->errorRedirectToMain(_("The passwords you specified do not match."));
200   - }
  216 + }
201 217 // FIXME more validation would be useful.
202 218 // validated and ready..
203 219 $this->startTransaction();
... ... @@ -376,7 +392,14 @@ class KTUserAdminDispatcher extends KTAdminDispatcher {
376 392 $password = KTUtil::arrayGet($_REQUEST, 'password');
377 393 $confirm_password = KTUtil::arrayGet($_REQUEST, 'confirm_password');
378 394  
379   - if (empty($password)) {
  395 + $KTConfig =& KTConfig::getSingleton();
  396 + $minLength = ((int) $KTConfig->get('user_prefs/passwordLength', 6));
  397 + $restrictAdmin = ((bool) $KTConfig->get('user_prefs/restrictAdminPasswords', false));
  398 +
  399 +
  400 + if ($restrictAdmin && (strlen($password) < $minLength)) {
  401 + $this->errorRedirectTo('addUser', sprintf(_("The password must be at least %d characters long."), $minLength));
  402 + } else if (empty($password)) {
380 403 $this->errorRedirectTo('addUser', _("You must specify a password for the user."));
381 404 } else if ($password !== $confirm_password) {
382 405 $this->errorRedirectTo('addUser', _("The passwords you specified do not match."));
... ...
resources/css/kt-framing.css
1 1 /* ------------------ generic ------------------ */
2 2  
  3 +html {
  4 + height: 100%;
  5 +}
  6 +
3 7 body
4 8 {
5 9 padding: 1em ; border: 0; margin: 0;
... ... @@ -14,10 +18,12 @@ body
14 18 margin: 0; border: 0; padding: 0;
15 19 }
16 20  
  21 +/*
17 22 #kt-wrapper
18 23 {
19 24 position: relative;
20 25 }
  26 +*/
21 27  
22 28 .copyright {
23 29 margin-top: 1em;
... ... @@ -173,8 +179,8 @@ a.main_nav_item {
173 179 {
174 180 position: absolute;
175 181 width: 14em;
176   - left: 0;
177   - top: 0;
  182 + left: 1em;
  183 +/* top: 0; */ /* stupid IE bug: absolute position ate my scrollbars */
178 184 background: transparent;
179 185 margin: 0 10px 0 0 ;
180 186 border: 0;
... ...
resources/js/conditional_complex_edit.js
... ... @@ -191,6 +191,53 @@ function getPOSTRequest(fullurl) {
191 191 return req;
192 192 }
193 193  
  194 +function removeFromBehaviour(field_id) {
  195 + var action = 'removeFromBehaviour';
  196 +
  197 + simpleLog('DEBUG','initiating item list update on field '+field_id);
  198 +
  199 + var formKeys = Array();
  200 + var formValues = Array();
  201 + // action
  202 + formKeys.push('action');
  203 + formValues.push(action);
  204 +
  205 + // fieldset-id
  206 + formKeys.push('fieldset_id');
  207 + formValues.push(getFieldsetId());
  208 + // field_id
  209 + formKeys.push('field_id');
  210 + formValues.push(field_id);
  211 +
  212 + // get the assigned items that are selected.
  213 + var column = getColumnForField(field_id);
  214 + var assigned_sources = getElementsByTagAndClassName('SELECT','assigned_item_list', column);
  215 + if (assigned_sources.length == 0) {
  216 + simpleLog('ERROR','Unable to locate assigned items.');
  217 + return;
  218 + }
  219 + var assigned_select = assigned_sources[0];
  220 + for (var i=0; i<assigned_select.options.length; i++) {
  221 + var opt = assigned_select.options[i];
  222 + if (opt.selected) {
  223 + formKeys.push('fieldsToRemove[]');
  224 + formValues.push(opt.value);
  225 + }
  226 + }
  227 +
  228 +
  229 + // boilerplate.
  230 + var POSTval = queryString(formKeys, formValues);
  231 + var req = getPOSTRequest(targeturl);
  232 + simpleLog('DEBUG','sending request (to '+targeturl+'): \n'+repr(map(null, formKeys, formValues))+'\nqueryString: '+POSTval);
  233 + var deferred = sendXMLHttpRequest(req, POSTval);
  234 + deferred.addCallback(partial(do_removeItems, field_id));
  235 + deferred.addErrback(handleError);
  236 +}
  237 +
  238 +function do_removeItems(field_id, req) {
  239 + updateItemListForField(field_id);
  240 +}
194 241  
195 242 // updates the item list for a given field to the items which are "free".
196 243 function updateItemListForField(field_id) {
... ... @@ -226,6 +273,39 @@ function updateItemListForField(field_id) {
226 273 var deferred = sendXMLHttpRequest(req, POSTval);
227 274 deferred.addCallback(partial(do_updateItemList, field_id));
228 275 deferred.addErrback(handleError);
  276 +
  277 +
  278 +
  279 +
  280 + action = 'getAssignedList';
  281 +
  282 + formKeys = Array();
  283 + formValues = Array();
  284 +
  285 + formKeys.push('action');
  286 + formValues.push(action);
  287 + // we need the fixed parent (or null - both are equivalent).
  288 + formKeys.push('parent_behaviour');
  289 + if (fixed_value == null) {
  290 + formValues.push('');
  291 + } else {
  292 + formValues.push(fixed_value);
  293 + }
  294 + // fieldset-id
  295 + formKeys.push('fieldset_id');
  296 + formValues.push(getFieldsetId());
  297 + // field_id
  298 + formKeys.push('field_id');
  299 + formValues.push(field_id);
  300 +
  301 +
  302 + // boilerplate.
  303 + POSTval = queryString(formKeys, formValues);
  304 + req2 = getPOSTRequest(targeturl);
  305 + simpleLog('DEBUG','sending request (to '+targeturl+'): \n'+repr(map(null, formKeys, formValues))+'\nqueryString: '+POSTval);
  306 + var deferred2 = sendXMLHttpRequest(req2, POSTval);
  307 + deferred2.addCallback(partial(do_updateAssignedItemList, field_id));
  308 + deferred2.addErrback(handleError);
229 309 }
230 310  
231 311 // updates the available behaviours for a given field.
... ... @@ -459,6 +539,34 @@ function do_updateItemList(field_id, req) {
459 539 replaceChildNodes(item_select, itemNodes);
460 540 }
461 541  
  542 +
  543 +function do_updateAssignedItemList(field_id, req) {
  544 + simpleLog('DEBUG','entering callback for assigned-item-list update.');
  545 + var items = req.responseXML.getElementsByTagName('item');
  546 +
  547 + var itemNodes = Array();
  548 + for (var i=0; i<items.length; i++) {
  549 + var item = items[i];
  550 + itemNodes.push(createDOM('option',{'value':item.getAttribute('value')},item.getAttribute('label')));
  551 + }
  552 + // now, find the array and replaceChildNodes() it.
  553 + var column = getColumnForField(field_id);
  554 + var assigned_wrapper = getElementsByTagAndClassName('DIV','assigned_items',column)[0];
  555 +
  556 + if (itemNodes.length == 0) {
  557 + setElementClass(assigned_wrapper, 'assigned_items empty');
  558 + } else { setElementClass(assigned_wrapper, 'assigned_items'); }
  559 +
  560 +
  561 + var is_sources = getElementsByTagAndClassName('select','assigned_item_list',column);
  562 + if (is_sources.length == 0) {
  563 + simpleLog('ERROR','Could not find the assigned item list in field '+field_id);
  564 + return;
  565 + }
  566 + var item_select = is_sources[0];
  567 + replaceChildNodes(item_select, itemNodes);
  568 +}
  569 +
462 570 function do_updateBehaviours(field_id, req) {
463 571 simpleLog('DEBUG','entering callback for behaviour-lists update.');
464 572 // we handle this slightly differently to updateItemList.
... ...
resources/js/conditional_simple_edit.js
... ... @@ -17,6 +17,16 @@ function getColumnForField(field_id) {
17 17 return getElement('md_'+field_id);
18 18 }
19 19  
  20 +function getButtonsForField(field_id) {
  21 + return getElement('buttons_'+field_id);
  22 +}
  23 +
  24 +function getNameForField(field_id) {
  25 + var h = getElement('header_'+field_id);
  26 + //alert(field_id);
  27 + return scrapeText(h);
  28 +}
  29 +
20 30 function getFieldIdFromColumn(column) {
21 31 return column.id.substr(3,column.id.length);
22 32 }
... ... @@ -29,6 +39,7 @@ function setActiveFields(active_fields) {
29 39 var column = getColumnForField(active_fields[i]);
30 40 setElementClass(column, 'active');
31 41 }
  42 +
32 43 }
33 44  
34 45 // takes a field, and sets all items in active_lookups to be active. other items are deleted.
... ... @@ -99,13 +110,13 @@ function do_handleAjaxError(err_source, err) {
99 110 }
100 111  
101 112 // from a selected_lookup, get the fixed_field and pass through, getting the items that selection currently activates.
102   -function updateActiveLookups(selected_lookup) {
  113 +function updateActiveLookups(selected_lookup, lookup_label) {
103 114  
104 115 simpleLog('DEBUG','function updateActiveLookups called.');
105 116 var req = getXMLHttpRequest();
106 117 req.open('GET',target_url+'?action=updateActiveLookups&active_field='+current_fixed+'&selected_lookup='+selected_lookup, true);
107 118 var deferred = sendXMLHttpRequest(req);
108   - deferred.addCallback(do_updateActiveLookups);
  119 + deferred.addCallback(partial(do_updateActiveLookups, lookup_label));
109 120 deferred.addErrback(partial(do_handleAjaxError, 'updateActiveLookups'));
110 121 }
111 122  
... ... @@ -152,7 +163,7 @@ function storeRelationship(selected_lookup, child_lookups) {
152 163  
153 164 // inform the user that something has happened.
154 165 // FIXME this isn't i18n friendly.
155   - addInformationNote('Values updated at ' + new Date());
  166 + addInformationNote('Dependencies saved. (at ' + new Date() + ')');
156 167  
157 168 deferred.addCallback(do_updateActiveLookups);
158 169 deferred.addErrback(partial(do_handleAjaxError, 'storeRelationship'));
... ... @@ -187,7 +198,7 @@ function do_updateActiveFields(req) {
187 198  
188 199 // should receive a simple-enough set of "field" elements,
189 200 // filled with "lookup" items.
190   -function do_updateActiveLookups(req) {
  201 +function do_updateActiveLookups(label, req) {
191 202 simpleLog('DEBUG','AJAX function do_updateActiveLookups triggered');
192 203 var active_fields = Array();
193 204 var incoming_fields = req.responseXML.getElementsByTagName('field');
... ... @@ -211,6 +222,8 @@ function do_updateActiveLookups(req) {
211 222 setActiveLookupsForField(active_fields[i].field_id, active_fields[i].lookups);
212 223 }
213 224 }
  225 +
  226 + addInformationNote('Dependencies for value "'+label+' loaded."(at ' + new Date() + ')');
214 227 }
215 228  
216 229  
... ... @@ -220,19 +233,27 @@ function setExclusiveEditing(field_id) {
220 233 var rootItem = getConditionalTable();
221 234 var columns = rootItem.getElementsByTagName('TD');
222 235 for (var i=0; i<columns.length; i++) {
223   - setElementClass(columns[i], 'inactive');
224   - var item_list = getElementsByTagAndClassName('SELECT','item_list',columns[i])[0]; // FIXME catch potential failure here (pathalogical)
225   - item_list.multiple=true;
226   - updateNodeAttributes(item_list, {'onchange':null});
  236 + var col = columns[i];
  237 + if (hasElementClass(col, "buttonset")) { setElementClass(col, 'buttonset inactive'); }
  238 + else {
  239 + setElementClass(columns[i], 'inactive');
  240 + var item_list = getElementsByTagAndClassName('SELECT','item_list',columns[i])[0]; // FIXME catch potential failure here (pathalogical)
  241 + item_list.multiple=true;
  242 + updateNodeAttributes(item_list, {'onchange':null});
  243 + }
227 244 }
228 245  
229 246 // get the "right" column.
230 247 var column = getColumnForField(field_id);
  248 + simpleLog('DEBUG','setExclusiveEditing found column' + column + ' for id ' + field_id);
231 249 setElementClass(column, 'active editing');
232 250 var item_list = getElementsByTagAndClassName('SELECT','item_list',column)[0]; // FIXME catch potential failure here (pathalogical)
233 251 item_list.multiple = false;
234 252 updateNodeAttributes(item_list, {'onchange':partial(handleChangedSelection, field_id, item_list)});
235 253  
  254 + var buttons = getButtonsForField(field_id);
  255 + setElementClass(buttons, 'buttonset active editing');
  256 +
236 257 simpleLog('ERROR','setExclusiveEditing needs to alter the options so nothing is selected.');
237 258 }
238 259  
... ... @@ -248,6 +269,7 @@ function editSimpleField(field_id) {
248 269 setExclusiveEditing(field_id);
249 270 updateActiveFields(); // trigger an update of the backend.
250 271 // rest is asynchronous.
  272 + addInformationNote('Now editing field "'+getNameForField(field_id)+'".');
251 273 }
252 274  
253 275  
... ... @@ -279,7 +301,8 @@ function finishSimpleField(field_id) {
279 301  
280 302 // called when a single-view dropdown is activated.
281 303 function handleChangedSelection(field_id, select_input) {
282   - updateActiveLookups(select_input.value); // handles everything for us.
  304 + addInformationNote('Loading Dependencies for value "'+scrapeText(select_input.options[select_input.selectedIndex])+'"(at ' + new Date() + ')');
  305 + updateActiveLookups(select_input.value, scrapeText(select_input.options[select_input.selectedIndex])); // handles everything for us.
283 306 }
284 307  
285 308 // push onto the "fixed" stack the field which is being edited at the moment.
... ...
templates/ktcore/metadata/conditional/editcomplex.smarty
... ... @@ -15,6 +15,12 @@
15 15 .inactive .behaviour_edit_options
16 16 { display: none; }
17 17  
  18 +.inactive .assigned_items { display: none; }
  19 +
  20 +.assigned_items.empty select { display: none; }
  21 +.assigned_items.empty .assigned_items_message { display: block; color: #666; }
  22 +.assigned_items_message { display: none; }
  23 +
18 24 .helpText { color: #666; }
19 25 {/literal}
20 26 {/capture}
... ... @@ -36,6 +42,17 @@ addLoadEvent(updateActiveFields);
36 42 {$context->oPage->requireJSStandalone($sJS)}
37 43  
38 44 <h2>{i18n}Edit Complex Conditional Metadata{/i18n}</h2>
  45 +
  46 +<p class="descriptiveText">Complex Conditional Metadata depends on what are
  47 +called "behaviours". Essentially, behaviours are assigned to a <strong>single</strong>
  48 +field, and can contain any number of values that are available in that field. Each field
  49 +can have multiple behaviours assigned to it.</p>
  50 +
  51 +<p class="descriptiveText important">Each behaviour can cause a number of other behaviours &mdash; in
  52 +the field's <strong>child</strong> fields &mdash; to become available. By assigning values
  53 +to behaviours, and creating relationships between behaviours, you can create extremely complex
  54 +relationships between available lookup values.</p>
  55 +
39 56 <form method="POST" action="{$smarty.server.PHP_SELF}">
40 57 <input type="hidden" name="fieldset_id" id="global-fieldset-id" value="{$fieldset_id}" />
41 58  
... ... @@ -54,6 +71,21 @@ addLoadEvent(updateActiveFields);
54 71 <h3>{$oField->getName()}</h3>
55 72 <p class="inactivity_message">{i18n}This column is not active.{/i18n}</p>
56 73 <p class="fixed_message">{i18n}Editing behaviour <strong>Jack</strong>{/i18n}</p>
  74 + <div class="assigned_items">
  75 + <h3>{i18n}Assigned Items{/i18n}</h3>
  76 + <select class="assigned_item_list" multiple="true">
  77 + <!-- These are orphaned - we fire a "activate" command on load / available.
  78 + <option value="-1">Test 1</option>
  79 + <option value="-2">Test 2</option>
  80 + <option value="-3">Test 3</option>
  81 + <option value="-4">Test 4</option>
  82 + -->
  83 + </select>
  84 + <br /><input class="assigned_item_list" type="button" value="{i18n}remove behaviour{/i18n}" onclick="removeFromBehaviour({$oField->getId()});" />
  85 + <p class="assigned_items_message">No items have been assigned with the current
  86 + parent behaviour.</p>
  87 + </div>
  88 +
57 89 <div class="unassigned_items">
58 90 <h3>{i18n}Unassigned/Unavailable{/i18n}</h3>
59 91 <select class="item_list" multiple="true">
... ... @@ -93,9 +125,7 @@ addLoadEvent(updateActiveFields);
93 125 <option value="4">Behaviour 4</option>
94 126 -->
95 127 </select>
96   - <br />
97   - <a href="#" onclick="changeAssignments({$oField->getId()}); return
98   -false;">{i18n}Change Assignments for this field.{/i18n}</a>
  128 +
99 129 </div>
100 130 </td>
101 131 {/foreach}
... ...
templates/ktcore/metadata/conditional/editsimple.smarty
... ... @@ -3,16 +3,20 @@
3 3 /* inactivity */
4 4 .active .inactivity_message { display: none; }
5 5  
  6 +select { width: 100%; }
  7 +
  8 +.edit_button { margin-bottom: 0.5em; }
6 9 .save_button,
7   -.done_button { display: none; }
  10 +.done_button { display: none; margin-bottom: 0.5em;}
8 11  
9 12 .active.editing .save_button,
10 13 .active.editing .done_button { display: block; }
11 14  
12 15 .active.editing .edit_button { display: none; }
13 16  
14   -
15 17 .active { position: relative; }
  18 +td { vertical-align: top; }
  19 +.buttonset.inactive { background: transparent; }
16 20  
17 21 .inactive { background: #ccc; position: relative; }
18 22 .inactive .fixed_message,
... ... @@ -61,14 +65,24 @@ addLoadEvent(partial(editSimpleField, {$iMasterFieldId}));
61 65  
62 66 <h2>{i18n}Editing Fieldset Rules (Simple){/i18n}</h2>
63 67  
  68 +<p class="descriptiveText">To make a value in a <strong>child field</strong>available to the user when another value is
  69 +selected in a <strong>parent field</strong>, first ensure that the parent field is being edited (it will have "save" and "done"
  70 +as the buttons at the bottom of the column) and then select the value for the parent field. Now select the value(s) in
  71 +the child column(s) you wish to be available to the user when the parent field's value is selected,
  72 +and click "save". Note you that you can use Ctrl-&lt;click&gt; to select multiple child values
  73 +at the same time.</p>
  74 +
  75 +<p class="descriptiveText important">Changes made here are stored immediately, without you needing to
  76 +refresh the page.</p>
  77 +
64 78 <form method="POST" action="{$smarty.server.PHP_SELF}">
65 79 <input type="hidden" name="fieldset_id" id="global-fieldset-id" value="{$fieldset_id}" />
66 80  
67 81 <table id="simple_conditional_edit">
68 82 <tr>
69   - <th>Column 1</th>
70   - <th>Column 2</th>
71   - <th>Column 3</th>
  83 +{foreach from=$aFields item=oField}
  84 + <th id="header_{$oField->getId()}">{$oField->getName()}</th>
  85 +{/foreach}
72 86 </tr>
73 87 <tr valign="top">
74 88  
... ... @@ -76,22 +90,28 @@ addLoadEvent(partial(editSimpleField, {$iMasterFieldId}));
76 90 <td class="inactive" id="md_{$oField->getId()}">
77 91 <p class="inactivity_message">{i18n}This field is not controlled by the currently active group.{/i18n}</p>
78 92 <div class="lookup_items">
79   - <h3>Field: {$oField->getName()}</h3>
80 93 <select class="item_list">
81 94 {foreach from=$oField->getValues() item=oMetaData}
82 95 <option value="{$oMetaData->getId()}">{$oMetaData->getName()|escape}</option>
83 96 {/foreach}
84 97 </select>
85   - <div class="buttonset">
86   - <input type="button" value="{i18n}edit{/i18n}" class="edit_button" onclick="editSimpleField({$oField->getId()})" />
87   - <input type="button" value="{i18n}save{/i18n}" class="save_button" onclick="saveSimpleField({$oField->getId()})" />
88   - <input type="button" value="{i18n}done{/i18n}" class="done_button" onclick="finishSimpleField({$oField->getId()})" />
89   - </div>
  98 +
90 99 </div>
91 100 </td>
92 101 {/foreach}
93 102  
94 103 </tr>
  104 +<tr>
  105 +{foreach from=$aFields item=oField}
  106 + <td class="buttonset" id="buttons_{$oField->getId()}">
  107 + <div class="form_actions">
  108 + <input type="button" value="{i18n}edit field{/i18n}" class="edit_button" onclick="editSimpleField({$oField->getId()})" />
  109 + <input type="button" value="{i18n}save this dependency{/i18n}" class="save_button" onclick="saveSimpleField({$oField->getId()})" />
  110 + <input type="button" value="{i18n}finished with this field{/i18n}" class="done_button" onclick="finishSimpleField({$oField->getId()})" />
  111 + </div>
  112 + </td>
  113 +{/foreach}
  114 +</tr>
95 115 </table>
96 116 <!--
97 117 <table id="brad-log">
... ...
templates/ktcore/metadata/conditional/manageConditional.smarty
1 1 <h2>{i18n}Manage conditional fieldset{/i18n}</h2>
2 2  
  3 +<p class="descriptiveText">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
  7 +the values of one field are restricted to a certain subset of values if another field
  8 +has a specific value. For example, you could say that if the field "Street" is "Jeffrey",
  9 +then the field "Residents" must be one of "Jones","Smith" or "Friedman".</p>
  10 +
  11 +<p class="descriptiveText">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).</p>
  14 +
  15 +{* don't show warnings until the basics are done. *}
  16 +
  17 +{if ($oMasterField && empty($free_fields))}
3 18 {if $sIncomplete || !$oFieldset->getIsComplete()}
4   -<div class="ktError">
  19 +<div class="ktInfo">
5 20 <p>{i18n}This conditional fieldset is marked such that it
6 21 cannot be used. This happens when the fieldset has been edited and has
7 22 not been set to complete. Setting the fieldset to complete will do a
8 23 check to see if the fieldset is usable by the user.{/i18n}</p>
9 24 </div>
  25 +{/if}
10 26  
11 27 {if $sIncomplete}
12 28 <div class="ktError">
... ... @@ -27,9 +43,12 @@ to complete{/i18n}: {$sIncomplete|escape}&lt;/p&gt;
27 43 <h2>{i18n}Conditional type{/i18n}</h2>
28 44  
29 45 {if $oFieldset->getIsComplex()}
30   -<p>{i18n}Complex{/i18n}: <a
  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"
31 50 href="{$rootUrl}/plugins/ktcore/admin/manageConditionals.php?action=editComplexFieldset&fieldset_id={$oFieldset->getId()}">{i18n}Manage
32   -complex conditional{/i18n}</a></p>
  51 +complex conditional{/i18n}</a></p>{/if}
33 52  
34 53 <form action="{$smarty.server.PHP_SELF}" method="POST">
35 54 <input type="hidden" name="action" value="changeToSimple" />
... ... @@ -39,9 +58,13 @@ complex conditional{/i18n}&lt;/a&gt;&lt;/p&gt;
39 58  
40 59 {else}
41 60  
42   -<p>{i18n}Simple{/i18n}: <a
  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"
43 65 href="{$rootUrl}/plugins/ktcore/admin/manageConditionals.php?action=editFieldset&fieldset_id={$oFieldset->getId()}">{i18n}Manage simple conditional{/i18n}</a>
44 66 </p>
  67 +{/if}
45 68  
46 69 <form action="{$smarty.server.PHP_SELF}" method="POST">
47 70 <input type="hidden" name="action" value="changeToComplex" />
... ... @@ -54,9 +77,10 @@ href=&quot;{$rootUrl}/plugins/ktcore/admin/manageConditionals.php?action=editFieldset
54 77 <p>{i18n}Changing the conditional type set will remove all existing field
55 78 ordering!{/i18n}</p>
56 79  
57   -<h2>Master field</h2>
  80 +
58 81  
59 82 {if !$oMasterField}
  83 +<h2>Master field</h2>
60 84 {i18n}No master field is set, please select the master field{/i18n}:
61 85  
62 86 <form action="{$smarty.server.PHP_SELF}" method="POST">
... ... @@ -70,15 +94,26 @@ ordering!{/i18n}&lt;/p&gt;
70 94 {else}
71 95  
72 96 <form action="{$smarty.server.PHP_SELF}" method="POST">
  97 +<fieldset>
  98 +<legend>Master field</legend>
73 99 <input type="hidden" name="action" value="setMasterField" />
74 100 <input type="hidden" name="fFieldsetId" value="{$oFieldset->getId()}" />
75 101  
  102 +<p class="descriptiveText">In order to have a chain of conditions, one initial field
  103 +must be shown to the user. This is called the <strong>master field</strong>.</p>
  104 +
  105 +<div class="form">
  106 +<p class="descriptiveText important">{i18n}Changing the master field set will remove all existing field
  107 +ordering!{/i18n}</p>
76 108 {entity_select entities=$oFieldset->getFields() name="fFieldId" selected=$oMasterField->getId()}
  109 +</div>
  110 +<div class="form_actions">
77 111 <input type="submit" name="submit" value="{i18n}Change master field{/i18n}" />
  112 +</div>
  113 +</fieldset>
78 114 </form>
79 115  
80   -<p>{i18n}Changing the master field set will remove all existing field
81   -ordering!{/i18n}</p>
  116 +
82 117  
83 118 <h2>{i18n}Field ordering{/i18n}</h2>
84 119  
... ... @@ -92,30 +127,38 @@ $this-&gt;assign(&quot;oParentField&quot;, DocumentField::get($this-&gt;_tpl_vars[&#39;aRow&#39;][&#39;paren
92 127 $this->assign("oChildField", DocumentField::get($this->_tpl_vars['aRow']['child_field_id']));
93 128 {/php}
94 129 <li>
95   -<span title="Field Id {$oParentField->getId()}">Field {$oParentField->getName()|escape}</span>
96   -affects the values available in
97   -<span title="Field Id {$oChildField->getId()}">field {$oChildField->getName()|escape}</span>
  130 + {$oParentField->getName()|escape}
  131 +<span class="descriptiveText">controls the values available in</span>
  132 + {$oChildField->getName()|escape}
98 133 </li>
99 134 {/foreach}
100 135 </ul>
101 136 {/if}
102 137  
103 138 { if $free_fields }
104   -<h3>{i18n}Order fields{/i18n}</h2>
  139 +
105 140  
106 141 <form action="{$smarty.server.PHP_SELF}" method="POST">
  142 +<fieldset>
107 143 <input type="hidden" name="fFieldsetId" value="{$oFieldset->getId()}" />
108 144 <input type="hidden" name="action" value="orderFields" />
109 145  
110   -The available values of the selected fields:
111   -<br />
112   -{entity_select entities=$free_fields name="fFreeFieldIds[]" multiple="yes"}
113   -<br />
114   -are conditional on the values of this field:
115   -<br />
  146 +<legend>{i18n}Order Fields{/i18n}</legend>
  147 +
  148 +<div class="field">
  149 +<p class="descriptiveText">The value of the field</p>
116 150 {entity_select entities=$parent_fields name="fParentFieldId"}
117   -<br />
  151 +</div>
  152 +
  153 +<div class="field">
  154 +<p class="descriptiveText">controls the values of the following fields</p>
  155 +{entity_select entities=$free_fields name="fFreeFieldIds[]" multiple="yes"}
  156 +</div>
  157 +
  158 +<div class="form_actions">
118 159 <input type="submit" name="submit" value="{i18n}Order{/i18n}" />
  160 +</div>
  161 +</fieldset>
119 162 </form>
120 163 {/if}
121 164  
... ...
templates/ktcore/workflow/manageTransitions.smarty
... ... @@ -20,6 +20,10 @@ while the workflow has no documents or document-versions assigned to the workflo
20 20  
21 21 </fieldset>
22 22  
  23 +{if (empty($workflow_info.transitions))}
  24 +<div class="ktInfo"><p>This workflow does not define any transitions. Use the "Create a new transition" link above
  25 +to add new transitions.</p></div>
  26 +{else}
23 27 <h3>{i18n}Transition Availability{/i18n}</h3>
24 28  
25 29 <p class="descriptiveText">Click on any transition below to edit it directly,
... ... @@ -66,3 +70,4 @@ or use the checkboxes to assign which states the transition is available from.&lt;/
66 70 </div>
67 71  
68 72 </form>
  73 +{/if}
69 74 \ No newline at end of file
... ...