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,3 +149,7 @@ antiword = antiword
149 ; minimum password length on password-setting 149 ; minimum password length on password-setting
150 ; could be moved into DB-auth-config 150 ; could be moved into DB-auth-config
151 passwordLength = 6 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,6 +390,9 @@ $oKTConfig->setdefaultns("tweaks", "genericMetaDataRequired", true);
390 $oKTConfig->setdefaultns("tweaks", "phpErrorLogFile", false); 390 $oKTConfig->setdefaultns("tweaks", "phpErrorLogFile", false);
391 $oKTConfig->setdefaultns("tweaks", "developmentWindowLog", false); 391 $oKTConfig->setdefaultns("tweaks", "developmentWindowLog", false);
392 392
  393 +$oKTConfig->setdefaultns("user_prefs", "passwordLength", 6);
  394 +$oKTConfig->setdefaultns("user_prefs", "restrictAdminPasswords", false);
  395 +
393 $oKTConfig->setdefaultns("ui", "ieGIF", true); 396 $oKTConfig->setdefaultns("ui", "ieGIF", true);
394 $oKTConfig->setdefaultns("ui", "alwaysShowAll", false); 397 $oKTConfig->setdefaultns("ui", "alwaysShowAll", false);
395 398
lib/documentmanagement/DocumentFieldLink.inc
@@ -163,7 +163,7 @@ class DocumentFieldLink extends KTEntity { @@ -163,7 +163,7 @@ class DocumentFieldLink extends KTEntity {
163 $iMetadataVersionId = $oDocument->getMetadataVersionId(); 163 $iMetadataVersionId = $oDocument->getMetadataVersionId();
164 return KTEntityUtil::getByDict('DocumentFieldLink', array( 164 return KTEntityUtil::getByDict('DocumentFieldLink', array(
165 'metadata_version_id' => $iMetadataVersionId, 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,10 +223,10 @@ class KTFolderUtil {
223 $sFD = ''; 223 $sFD = '';
224 $sFF = ''; 224 $sFF = '';
225 if (!empty($aFailedDocuments)) { 225 if (!empty($aFailedDocuments)) {
226 - $sFD = _('Documents: ') . array_join(', ', $aFailedDocuments) . '. '; 226 + $sFD = _('Documents: ') . implode(', ', $aFailedDocuments) . '. ';
227 } 227 }
228 if (!empty($aFailedFolders)) { 228 if (!empty($aFailedFolders)) {
229 - $sFF = _('Folders: ') . array_join(', ', $aFailedFolders) . '.'; 229 + $sFF = _('Folders: ') . implode(', ', $aFailedFolders) . '.';
230 } 230 }
231 return PEAR::raiseError(_('You do not have permission to delete these items. ') . $sFD . $sFF); 231 return PEAR::raiseError(_('You do not have permission to delete these items. ') . $sFD . $sFF);
232 } 232 }
@@ -267,7 +267,7 @@ class KTFolderUtil { @@ -267,7 +267,7 @@ class KTFolderUtil {
267 $oPerm = KTPermission::getByName('ktcore.permissions.read'); 267 $oPerm = KTPermission::getByName('ktcore.permissions.read');
268 $oBaseFolderPerm = KTPermission::getByName('ktcore.permissions.addFolder'); 268 $oBaseFolderPerm = KTPermission::getByName('ktcore.permissions.addFolder');
269 269
270 - if (!KTPermissionUtil::userHasPermissionOnItem($oUser, $oPerm, $oDestFolder)) { 270 + if (!KTPermissionUtil::userHasPermissionOnItem($oUser, $oBaseFolderPerm, $oDestFolder)) {
271 return PEAR::raiseError(_('You are not allowed to create folders in the destination.')); 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,7 +298,7 @@ class KTFolderUtil {
298 // child documents 298 // child documents
299 $aChildDocs = Document::getList(array('folder_id = ?',array($iFolderId))); 299 $aChildDocs = Document::getList(array('folder_id = ?',array($iFolderId)));
300 foreach ($aChildDocs as $oDoc) { 300 foreach ($aChildDocs as $oDoc) {
301 - if (KTPermissionUtil::userHasPermissionOnItem($oUser, $oPerm, $oFolder)) { 301 + if (KTPermissionUtil::userHasPermissionOnItem($oUser, $oPerm, $oDoc)) {
302 $aDocuments[] = $oDoc; 302 $aDocuments[] = $oDoc;
303 } else { 303 } else {
304 $aFailedDocuments[] = $oDoc->getName(); 304 $aFailedDocuments[] = $oDoc->getName();
@@ -314,10 +314,10 @@ class KTFolderUtil { @@ -314,10 +314,10 @@ class KTFolderUtil {
314 $sFD = ''; 314 $sFD = '';
315 $sFF = ''; 315 $sFF = '';
316 if (!empty($aFailedDocuments)) { 316 if (!empty($aFailedDocuments)) {
317 - $sFD = _('Documents: ') . array_join(', ', $aFailedDocuments) . '. '; 317 + $sFD = _('Documents: ') . implode(', ', $aFailedDocuments) . '. ';
318 } 318 }
319 if (!empty($aFailedFolders)) { 319 if (!empty($aFailedFolders)) {
320 - $sFF = _('Folders: ') . array_join(', ', $aFailedFolders) . '.'; 320 + $sFF = _('Folders: ') . implode(', ', $aFailedFolders) . '.';
321 } 321 }
322 return PEAR::raiseError(_('You do not have permission to copy these items. ') . $sFD . $sFF); 322 return PEAR::raiseError(_('You do not have permission to copy these items. ') . $sFD . $sFF);
323 } 323 }
@@ -325,6 +325,9 @@ class KTFolderUtil { @@ -325,6 +325,9 @@ class KTFolderUtil {
325 // first we walk the tree, creating in the new location as we go. 325 // first we walk the tree, creating in the new location as we go.
326 // essentially this is an "ok" pass. 326 // essentially this is an "ok" pass.
327 327
  328 +
  329 + $oStorage =& KTStorageManagerUtil::getSingleton();
  330 +
328 $aFolderMap = array(); 331 $aFolderMap = array();
329 332
330 $sTable = KTUtil::getTableName('folders'); 333 $sTable = KTUtil::getTableName('folders');
@@ -339,7 +342,13 @@ class KTFolderUtil { @@ -339,7 +342,13 @@ class KTFolderUtil {
339 return $id; 342 return $id;
340 } 343 }
341 $aFolderMap[$oFolder->getId()] = $id; 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 $aRemainingFolders = Folder::getList(array('parent_id = ?', array($oFolder->getId())), array('ids' => true)); 352 $aRemainingFolders = Folder::getList(array('parent_id = ?', array($oFolder->getId())), array('ids' => true));
344 353
345 354
@@ -359,34 +368,30 @@ class KTFolderUtil { @@ -359,34 +368,30 @@ class KTFolderUtil {
359 return $id; 368 return $id;
360 } 369 }
361 $aFolderMap[$iFolderId] = $id; 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 $aCFIds = Folder::getList(array('parent_id = ?', array($iFolderId)), array('ids' => true)); 380 $aCFIds = Folder::getList(array('parent_id = ?', array($iFolderId)), array('ids' => true));
364 $aRemainingFolders = array_merge($aRemainingFolders, $aCFIds); 381 $aRemainingFolders = array_merge($aRemainingFolders, $aCFIds);
365 } 382 }
366 383
  384 +
  385 +
367 // now we can go ahead. 386 // now we can go ahead.
368 foreach ($aDocuments as $oDocument) { 387 foreach ($aDocuments as $oDocument) {
369 $oChildDestinationFolder = Folder::get($aFolderMap[$oDocument->getFolderID()]); 388 $oChildDestinationFolder = Folder::get($aFolderMap[$oDocument->getFolderID()]);
370 $res = KTDocumentUtil::copy($oDocument, $oChildDestinationFolder); 389 $res = KTDocumentUtil::copy($oDocument, $oChildDestinationFolder);
371 - if (PEAR::isError($res)) { 390 + if (PEAR::isError($res) || ($res === false)) {
372 DBUtil::rollback(); 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 // and store 396 // and store
392 DBUtil::commit(); 397 DBUtil::commit();
lib/groups/Group.inc
@@ -155,7 +155,7 @@ class Group extends KTEntity { @@ -155,7 +155,7 @@ class Group extends KTEntity {
155 $aMembers = array(); 155 $aMembers = array();
156 foreach ($aUserIds as $iUserId) { 156 foreach ($aUserIds as $iUserId) {
157 $oUser = User::get($iUserId); 157 $oUser = User::get($iUserId);
158 - if ($oUser !== false) { 158 + if ((!PEAR::isError($oUser)) && ($oUser !== false)) {
159 $aMembers[] = $oUser; 159 $aMembers[] = $oUser;
160 } 160 }
161 } 161 }
@@ -170,7 +170,7 @@ class Group extends KTEntity { @@ -170,7 +170,7 @@ class Group extends KTEntity {
170 $aMembers = array(); 170 $aMembers = array();
171 foreach ($aGroupIds as $iGroupId) { 171 foreach ($aGroupIds as $iGroupId) {
172 $oGroup = Group::get($iGroupId); 172 $oGroup = Group::get($iGroupId);
173 - if ($oGroup !== false) { 173 + if ((!PEAR::isError($oUser)) && ($oGroup !== false)) {
174 $aMembers[] = $oGroup; 174 $aMembers[] = $oGroup;
175 } 175 }
176 } 176 }
plugins/ktcore/admin/ajaxComplexConditionals.php
@@ -62,6 +62,88 @@ class AjaxConditionalAdminDispatcher extends KTAdminDispatcher { @@ -62,6 +62,88 @@ class AjaxConditionalAdminDispatcher extends KTAdminDispatcher {
62 return $oTemplate->render(); 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 function do_getBehaviourList() { 147 function do_getBehaviourList() {
66 $parent_behaviour = KTUtil::arrayGet($_REQUEST, 'parent_behaviour'); 148 $parent_behaviour = KTUtil::arrayGet($_REQUEST, 'parent_behaviour');
67 $fieldset_id = KTUtil::arrayGet($_REQUEST, 'fieldset_id'); 149 $fieldset_id = KTUtil::arrayGet($_REQUEST, 'fieldset_id');
plugins/ktcore/admin/manageConditionals.php
@@ -19,8 +19,8 @@ class ManageConditionalDispatcher extends KTAdminDispatcher { @@ -19,8 +19,8 @@ class ManageConditionalDispatcher extends KTAdminDispatcher {
19 global $default; 19 global $default;
20 $this->ru = $default->rootUrl; 20 $this->ru = $default->rootUrl;
21 // this is not useful: we _still_ don't chain through the right dispatcher (!) 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,10 +49,11 @@ class ManageConditionalDispatcher extends KTAdminDispatcher {
49 * we can then render in/out. Everything "intelligent" happens 49 * we can then render in/out. Everything "intelligent" happens
50 * in AJAX (doing it with submits sucks arse. 50 * in AJAX (doing it with submits sucks arse.
51 * 51 *
52 - * FIXME we fake it here with nested arrays...  
53 */ 52 */
  53 +
54 $oFieldset =& KTFieldset::get($fieldset_id); 54 $oFieldset =& KTFieldset::get($fieldset_id);
55 $aFields =& $oFieldset->getFields(); 55 $aFields =& $oFieldset->getFields();
  56 +
56 $this->aBreadcrumbs[] = array( 57 $this->aBreadcrumbs[] = array(
57 'url' => $this->ru . '/admin.php/documents/fieldmanagement', 58 'url' => $this->ru . '/admin.php/documents/fieldmanagement',
58 'query' => 'action=edit&fFieldsetId=' . $oFieldset->getId(), 59 'query' => 'action=edit&fFieldsetId=' . $oFieldset->getId(),
@@ -69,7 +70,7 @@ class ManageConditionalDispatcher extends KTAdminDispatcher { @@ -69,7 +70,7 @@ class ManageConditionalDispatcher extends KTAdminDispatcher {
69 "context" => &$this, 70 "context" => &$this,
70 "fieldset_id" => $fieldset_id, 71 "fieldset_id" => $fieldset_id,
71 "aFields" => $aFields, 72 "aFields" => $aFields,
72 - "iMasterFieldId" => $aFields[0]->getId(), 73 + "iMasterFieldId" => $oFieldset->getMasterFieldId(),
73 ); 74 );
74 return $oTemplate->render($aTemplateData); 75 return $oTemplate->render($aTemplateData);
75 } 76 }
@@ -104,7 +105,7 @@ class ManageConditionalDispatcher extends KTAdminDispatcher { @@ -104,7 +105,7 @@ class ManageConditionalDispatcher extends KTAdminDispatcher {
104 "context" => &$this, 105 "context" => &$this,
105 "fieldset_id" => $fieldset_id, 106 "fieldset_id" => $fieldset_id,
106 "aFields" => $aFields, 107 "aFields" => $aFields,
107 - "iMasterFieldId" => $aFields[0]->getId(), 108 + "iMasterFieldId" => $oFieldset->getMasterFieldId(),
108 ); 109 );
109 return $oTemplate->render($aTemplateData); 110 return $oTemplate->render($aTemplateData);
110 } 111 }
plugins/ktcore/admin/userManagement.php
@@ -73,13 +73,22 @@ class KTUserAdminDispatcher extends KTAdminDispatcher { @@ -73,13 +73,22 @@ class KTUserAdminDispatcher extends KTAdminDispatcher {
73 73
74 $aOptions = array('autocomplete' => false); 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 $add_fields = array(); 86 $add_fields = array();
78 $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); 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 $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); 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 $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); 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 $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); 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 $add_fields[] = new KTPasswordWidget(_('Confirm Password'), _('Confirm the password specified above.'), 'confirm_password', null, $this->oPage, true, null, null, $aOptions); 92 $add_fields[] = new KTPasswordWidget(_('Confirm Password'), _('Confirm the password specified above.'), 'confirm_password', null, $this->oPage, true, null, null, $aOptions);
84 // nice, easy bits. 93 // nice, easy bits.
85 $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); 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,7 +171,7 @@ class KTUserAdminDispatcher extends KTAdminDispatcher {
162 $this->aBreadcrumbs[] = array('url' => $_SERVER['PHP_SELF'], 'name' => _('User Management')); 171 $this->aBreadcrumbs[] = array('url' => $_SERVER['PHP_SELF'], 'name' => _('User Management'));
163 $this->oPage->setBreadcrumbDetails(_('change user password')); 172 $this->oPage->setBreadcrumbDetails(_('change user password'));
164 $this->oPage->setTitle(_("Change User Password")); 173 $this->oPage->setTitle(_("Change User Password"));
165 - 174 +
166 $user_id = KTUtil::arrayGet($_REQUEST, 'user_id'); 175 $user_id = KTUtil::arrayGet($_REQUEST, 'user_id');
167 $oUser =& User::get($user_id); 176 $oUser =& User::get($user_id);
168 177
@@ -193,11 +202,18 @@ class KTUserAdminDispatcher extends KTAdminDispatcher { @@ -193,11 +202,18 @@ class KTUserAdminDispatcher extends KTAdminDispatcher {
193 $password = KTUtil::arrayGet($_REQUEST, 'password'); 202 $password = KTUtil::arrayGet($_REQUEST, 'password');
194 $confirm_password = KTUtil::arrayGet($_REQUEST, 'confirm_password'); 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 $this->errorRedirectToMain(_("You must specify a password for the user.")); 213 $this->errorRedirectToMain(_("You must specify a password for the user."));
198 } else if ($password !== $confirm_password) { 214 } else if ($password !== $confirm_password) {
199 $this->errorRedirectToMain(_("The passwords you specified do not match.")); 215 $this->errorRedirectToMain(_("The passwords you specified do not match."));
200 - } 216 + }
201 // FIXME more validation would be useful. 217 // FIXME more validation would be useful.
202 // validated and ready.. 218 // validated and ready..
203 $this->startTransaction(); 219 $this->startTransaction();
@@ -376,7 +392,14 @@ class KTUserAdminDispatcher extends KTAdminDispatcher { @@ -376,7 +392,14 @@ class KTUserAdminDispatcher extends KTAdminDispatcher {
376 $password = KTUtil::arrayGet($_REQUEST, 'password'); 392 $password = KTUtil::arrayGet($_REQUEST, 'password');
377 $confirm_password = KTUtil::arrayGet($_REQUEST, 'confirm_password'); 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 $this->errorRedirectTo('addUser', _("You must specify a password for the user.")); 403 $this->errorRedirectTo('addUser', _("You must specify a password for the user."));
381 } else if ($password !== $confirm_password) { 404 } else if ($password !== $confirm_password) {
382 $this->errorRedirectTo('addUser', _("The passwords you specified do not match.")); 405 $this->errorRedirectTo('addUser', _("The passwords you specified do not match."));
resources/css/kt-framing.css
1 /* ------------------ generic ------------------ */ 1 /* ------------------ generic ------------------ */
2 2
  3 +html {
  4 + height: 100%;
  5 +}
  6 +
3 body 7 body
4 { 8 {
5 padding: 1em ; border: 0; margin: 0; 9 padding: 1em ; border: 0; margin: 0;
@@ -14,10 +18,12 @@ body @@ -14,10 +18,12 @@ body
14 margin: 0; border: 0; padding: 0; 18 margin: 0; border: 0; padding: 0;
15 } 19 }
16 20
  21 +/*
17 #kt-wrapper 22 #kt-wrapper
18 { 23 {
19 position: relative; 24 position: relative;
20 } 25 }
  26 +*/
21 27
22 .copyright { 28 .copyright {
23 margin-top: 1em; 29 margin-top: 1em;
@@ -173,8 +179,8 @@ a.main_nav_item { @@ -173,8 +179,8 @@ a.main_nav_item {
173 { 179 {
174 position: absolute; 180 position: absolute;
175 width: 14em; 181 width: 14em;
176 - left: 0;  
177 - top: 0; 182 + left: 1em;
  183 +/* top: 0; */ /* stupid IE bug: absolute position ate my scrollbars */
178 background: transparent; 184 background: transparent;
179 margin: 0 10px 0 0 ; 185 margin: 0 10px 0 0 ;
180 border: 0; 186 border: 0;
resources/js/conditional_complex_edit.js
@@ -191,6 +191,53 @@ function getPOSTRequest(fullurl) { @@ -191,6 +191,53 @@ function getPOSTRequest(fullurl) {
191 return req; 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 // updates the item list for a given field to the items which are "free". 242 // updates the item list for a given field to the items which are "free".
196 function updateItemListForField(field_id) { 243 function updateItemListForField(field_id) {
@@ -226,6 +273,39 @@ function updateItemListForField(field_id) { @@ -226,6 +273,39 @@ function updateItemListForField(field_id) {
226 var deferred = sendXMLHttpRequest(req, POSTval); 273 var deferred = sendXMLHttpRequest(req, POSTval);
227 deferred.addCallback(partial(do_updateItemList, field_id)); 274 deferred.addCallback(partial(do_updateItemList, field_id));
228 deferred.addErrback(handleError); 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 // updates the available behaviours for a given field. 311 // updates the available behaviours for a given field.
@@ -459,6 +539,34 @@ function do_updateItemList(field_id, req) { @@ -459,6 +539,34 @@ function do_updateItemList(field_id, req) {
459 replaceChildNodes(item_select, itemNodes); 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 function do_updateBehaviours(field_id, req) { 570 function do_updateBehaviours(field_id, req) {
463 simpleLog('DEBUG','entering callback for behaviour-lists update.'); 571 simpleLog('DEBUG','entering callback for behaviour-lists update.');
464 // we handle this slightly differently to updateItemList. 572 // we handle this slightly differently to updateItemList.
resources/js/conditional_simple_edit.js
@@ -17,6 +17,16 @@ function getColumnForField(field_id) { @@ -17,6 +17,16 @@ function getColumnForField(field_id) {
17 return getElement('md_'+field_id); 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 function getFieldIdFromColumn(column) { 30 function getFieldIdFromColumn(column) {
21 return column.id.substr(3,column.id.length); 31 return column.id.substr(3,column.id.length);
22 } 32 }
@@ -29,6 +39,7 @@ function setActiveFields(active_fields) { @@ -29,6 +39,7 @@ function setActiveFields(active_fields) {
29 var column = getColumnForField(active_fields[i]); 39 var column = getColumnForField(active_fields[i]);
30 setElementClass(column, 'active'); 40 setElementClass(column, 'active');
31 } 41 }
  42 +
32 } 43 }
33 44
34 // takes a field, and sets all items in active_lookups to be active. other items are deleted. 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,13 +110,13 @@ function do_handleAjaxError(err_source, err) {
99 } 110 }
100 111
101 // from a selected_lookup, get the fixed_field and pass through, getting the items that selection currently activates. 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 simpleLog('DEBUG','function updateActiveLookups called.'); 115 simpleLog('DEBUG','function updateActiveLookups called.');
105 var req = getXMLHttpRequest(); 116 var req = getXMLHttpRequest();
106 req.open('GET',target_url+'?action=updateActiveLookups&active_field='+current_fixed+'&selected_lookup='+selected_lookup, true); 117 req.open('GET',target_url+'?action=updateActiveLookups&active_field='+current_fixed+'&selected_lookup='+selected_lookup, true);
107 var deferred = sendXMLHttpRequest(req); 118 var deferred = sendXMLHttpRequest(req);
108 - deferred.addCallback(do_updateActiveLookups); 119 + deferred.addCallback(partial(do_updateActiveLookups, lookup_label));
109 deferred.addErrback(partial(do_handleAjaxError, 'updateActiveLookups')); 120 deferred.addErrback(partial(do_handleAjaxError, 'updateActiveLookups'));
110 } 121 }
111 122
@@ -152,7 +163,7 @@ function storeRelationship(selected_lookup, child_lookups) { @@ -152,7 +163,7 @@ function storeRelationship(selected_lookup, child_lookups) {
152 163
153 // inform the user that something has happened. 164 // inform the user that something has happened.
154 // FIXME this isn't i18n friendly. 165 // FIXME this isn't i18n friendly.
155 - addInformationNote('Values updated at ' + new Date()); 166 + addInformationNote('Dependencies saved. (at ' + new Date() + ')');
156 167
157 deferred.addCallback(do_updateActiveLookups); 168 deferred.addCallback(do_updateActiveLookups);
158 deferred.addErrback(partial(do_handleAjaxError, 'storeRelationship')); 169 deferred.addErrback(partial(do_handleAjaxError, 'storeRelationship'));
@@ -187,7 +198,7 @@ function do_updateActiveFields(req) { @@ -187,7 +198,7 @@ function do_updateActiveFields(req) {
187 198
188 // should receive a simple-enough set of "field" elements, 199 // should receive a simple-enough set of "field" elements,
189 // filled with "lookup" items. 200 // filled with "lookup" items.
190 -function do_updateActiveLookups(req) { 201 +function do_updateActiveLookups(label, req) {
191 simpleLog('DEBUG','AJAX function do_updateActiveLookups triggered'); 202 simpleLog('DEBUG','AJAX function do_updateActiveLookups triggered');
192 var active_fields = Array(); 203 var active_fields = Array();
193 var incoming_fields = req.responseXML.getElementsByTagName('field'); 204 var incoming_fields = req.responseXML.getElementsByTagName('field');
@@ -211,6 +222,8 @@ function do_updateActiveLookups(req) { @@ -211,6 +222,8 @@ function do_updateActiveLookups(req) {
211 setActiveLookupsForField(active_fields[i].field_id, active_fields[i].lookups); 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,19 +233,27 @@ function setExclusiveEditing(field_id) {
220 var rootItem = getConditionalTable(); 233 var rootItem = getConditionalTable();
221 var columns = rootItem.getElementsByTagName('TD'); 234 var columns = rootItem.getElementsByTagName('TD');
222 for (var i=0; i<columns.length; i++) { 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 // get the "right" column. 246 // get the "right" column.
230 var column = getColumnForField(field_id); 247 var column = getColumnForField(field_id);
  248 + simpleLog('DEBUG','setExclusiveEditing found column' + column + ' for id ' + field_id);
231 setElementClass(column, 'active editing'); 249 setElementClass(column, 'active editing');
232 var item_list = getElementsByTagAndClassName('SELECT','item_list',column)[0]; // FIXME catch potential failure here (pathalogical) 250 var item_list = getElementsByTagAndClassName('SELECT','item_list',column)[0]; // FIXME catch potential failure here (pathalogical)
233 item_list.multiple = false; 251 item_list.multiple = false;
234 updateNodeAttributes(item_list, {'onchange':partial(handleChangedSelection, field_id, item_list)}); 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 simpleLog('ERROR','setExclusiveEditing needs to alter the options so nothing is selected.'); 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,6 +269,7 @@ function editSimpleField(field_id) {
248 setExclusiveEditing(field_id); 269 setExclusiveEditing(field_id);
249 updateActiveFields(); // trigger an update of the backend. 270 updateActiveFields(); // trigger an update of the backend.
250 // rest is asynchronous. 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,7 +301,8 @@ function finishSimpleField(field_id) {
279 301
280 // called when a single-view dropdown is activated. 302 // called when a single-view dropdown is activated.
281 function handleChangedSelection(field_id, select_input) { 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 // push onto the "fixed" stack the field which is being edited at the moment. 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,6 +15,12 @@
15 .inactive .behaviour_edit_options 15 .inactive .behaviour_edit_options
16 { display: none; } 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 .helpText { color: #666; } 24 .helpText { color: #666; }
19 {/literal} 25 {/literal}
20 {/capture} 26 {/capture}
@@ -36,6 +42,17 @@ addLoadEvent(updateActiveFields); @@ -36,6 +42,17 @@ addLoadEvent(updateActiveFields);
36 {$context->oPage->requireJSStandalone($sJS)} 42 {$context->oPage->requireJSStandalone($sJS)}
37 43
38 <h2>{i18n}Edit Complex Conditional Metadata{/i18n}</h2> 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 <form method="POST" action="{$smarty.server.PHP_SELF}"> 56 <form method="POST" action="{$smarty.server.PHP_SELF}">
40 <input type="hidden" name="fieldset_id" id="global-fieldset-id" value="{$fieldset_id}" /> 57 <input type="hidden" name="fieldset_id" id="global-fieldset-id" value="{$fieldset_id}" />
41 58
@@ -54,6 +71,21 @@ addLoadEvent(updateActiveFields); @@ -54,6 +71,21 @@ addLoadEvent(updateActiveFields);
54 <h3>{$oField->getName()}</h3> 71 <h3>{$oField->getName()}</h3>
55 <p class="inactivity_message">{i18n}This column is not active.{/i18n}</p> 72 <p class="inactivity_message">{i18n}This column is not active.{/i18n}</p>
56 <p class="fixed_message">{i18n}Editing behaviour <strong>Jack</strong>{/i18n}</p> 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 <div class="unassigned_items"> 89 <div class="unassigned_items">
58 <h3>{i18n}Unassigned/Unavailable{/i18n}</h3> 90 <h3>{i18n}Unassigned/Unavailable{/i18n}</h3>
59 <select class="item_list" multiple="true"> 91 <select class="item_list" multiple="true">
@@ -93,9 +125,7 @@ addLoadEvent(updateActiveFields); @@ -93,9 +125,7 @@ addLoadEvent(updateActiveFields);
93 <option value="4">Behaviour 4</option> 125 <option value="4">Behaviour 4</option>
94 --> 126 -->
95 </select> 127 </select>
96 - <br />  
97 - <a href="#" onclick="changeAssignments({$oField->getId()}); return  
98 -false;">{i18n}Change Assignments for this field.{/i18n}</a> 128 +
99 </div> 129 </div>
100 </td> 130 </td>
101 {/foreach} 131 {/foreach}
templates/ktcore/metadata/conditional/editsimple.smarty
@@ -3,16 +3,20 @@ @@ -3,16 +3,20 @@
3 /* inactivity */ 3 /* inactivity */
4 .active .inactivity_message { display: none; } 4 .active .inactivity_message { display: none; }
5 5
  6 +select { width: 100%; }
  7 +
  8 +.edit_button { margin-bottom: 0.5em; }
6 .save_button, 9 .save_button,
7 -.done_button { display: none; } 10 +.done_button { display: none; margin-bottom: 0.5em;}
8 11
9 .active.editing .save_button, 12 .active.editing .save_button,
10 .active.editing .done_button { display: block; } 13 .active.editing .done_button { display: block; }
11 14
12 .active.editing .edit_button { display: none; } 15 .active.editing .edit_button { display: none; }
13 16
14 -  
15 .active { position: relative; } 17 .active { position: relative; }
  18 +td { vertical-align: top; }
  19 +.buttonset.inactive { background: transparent; }
16 20
17 .inactive { background: #ccc; position: relative; } 21 .inactive { background: #ccc; position: relative; }
18 .inactive .fixed_message, 22 .inactive .fixed_message,
@@ -61,14 +65,24 @@ addLoadEvent(partial(editSimpleField, {$iMasterFieldId})); @@ -61,14 +65,24 @@ addLoadEvent(partial(editSimpleField, {$iMasterFieldId}));
61 65
62 <h2>{i18n}Editing Fieldset Rules (Simple){/i18n}</h2> 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 <form method="POST" action="{$smarty.server.PHP_SELF}"> 78 <form method="POST" action="{$smarty.server.PHP_SELF}">
65 <input type="hidden" name="fieldset_id" id="global-fieldset-id" value="{$fieldset_id}" /> 79 <input type="hidden" name="fieldset_id" id="global-fieldset-id" value="{$fieldset_id}" />
66 80
67 <table id="simple_conditional_edit"> 81 <table id="simple_conditional_edit">
68 <tr> 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 </tr> 86 </tr>
73 <tr valign="top"> 87 <tr valign="top">
74 88
@@ -76,22 +90,28 @@ addLoadEvent(partial(editSimpleField, {$iMasterFieldId})); @@ -76,22 +90,28 @@ addLoadEvent(partial(editSimpleField, {$iMasterFieldId}));
76 <td class="inactive" id="md_{$oField->getId()}"> 90 <td class="inactive" id="md_{$oField->getId()}">
77 <p class="inactivity_message">{i18n}This field is not controlled by the currently active group.{/i18n}</p> 91 <p class="inactivity_message">{i18n}This field is not controlled by the currently active group.{/i18n}</p>
78 <div class="lookup_items"> 92 <div class="lookup_items">
79 - <h3>Field: {$oField->getName()}</h3>  
80 <select class="item_list"> 93 <select class="item_list">
81 {foreach from=$oField->getValues() item=oMetaData} 94 {foreach from=$oField->getValues() item=oMetaData}
82 <option value="{$oMetaData->getId()}">{$oMetaData->getName()|escape}</option> 95 <option value="{$oMetaData->getId()}">{$oMetaData->getName()|escape}</option>
83 {/foreach} 96 {/foreach}
84 </select> 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 </div> 99 </div>
91 </td> 100 </td>
92 {/foreach} 101 {/foreach}
93 102
94 </tr> 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 </table> 115 </table>
96 <!-- 116 <!--
97 <table id="brad-log"> 117 <table id="brad-log">
templates/ktcore/metadata/conditional/manageConditional.smarty
1 <h2>{i18n}Manage conditional fieldset{/i18n}</h2> 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 {if $sIncomplete || !$oFieldset->getIsComplete()} 18 {if $sIncomplete || !$oFieldset->getIsComplete()}
4 -<div class="ktError"> 19 +<div class="ktInfo">
5 <p>{i18n}This conditional fieldset is marked such that it 20 <p>{i18n}This conditional fieldset is marked such that it
6 cannot be used. This happens when the fieldset has been edited and has 21 cannot be used. This happens when the fieldset has been edited and has
7 not been set to complete. Setting the fieldset to complete will do a 22 not been set to complete. Setting the fieldset to complete will do a
8 check to see if the fieldset is usable by the user.{/i18n}</p> 23 check to see if the fieldset is usable by the user.{/i18n}</p>
9 </div> 24 </div>
  25 +{/if}
10 26
11 {if $sIncomplete} 27 {if $sIncomplete}
12 <div class="ktError"> 28 <div class="ktError">
@@ -27,9 +43,12 @@ to complete{/i18n}: {$sIncomplete|escape}&lt;/p&gt; @@ -27,9 +43,12 @@ to complete{/i18n}: {$sIncomplete|escape}&lt;/p&gt;
27 <h2>{i18n}Conditional type{/i18n}</h2> 43 <h2>{i18n}Conditional type{/i18n}</h2>
28 44
29 {if $oFieldset->getIsComplex()} 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 href="{$rootUrl}/plugins/ktcore/admin/manageConditionals.php?action=editComplexFieldset&fieldset_id={$oFieldset->getId()}">{i18n}Manage 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 <form action="{$smarty.server.PHP_SELF}" method="POST"> 53 <form action="{$smarty.server.PHP_SELF}" method="POST">
35 <input type="hidden" name="action" value="changeToSimple" /> 54 <input type="hidden" name="action" value="changeToSimple" />
@@ -39,9 +58,13 @@ complex conditional{/i18n}&lt;/a&gt;&lt;/p&gt; @@ -39,9 +58,13 @@ complex conditional{/i18n}&lt;/a&gt;&lt;/p&gt;
39 58
40 {else} 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 href="{$rootUrl}/plugins/ktcore/admin/manageConditionals.php?action=editFieldset&fieldset_id={$oFieldset->getId()}">{i18n}Manage simple conditional{/i18n}</a> 65 href="{$rootUrl}/plugins/ktcore/admin/manageConditionals.php?action=editFieldset&fieldset_id={$oFieldset->getId()}">{i18n}Manage simple conditional{/i18n}</a>
44 </p> 66 </p>
  67 +{/if}
45 68
46 <form action="{$smarty.server.PHP_SELF}" method="POST"> 69 <form action="{$smarty.server.PHP_SELF}" method="POST">
47 <input type="hidden" name="action" value="changeToComplex" /> 70 <input type="hidden" name="action" value="changeToComplex" />
@@ -54,9 +77,10 @@ href=&quot;{$rootUrl}/plugins/ktcore/admin/manageConditionals.php?action=editFieldset @@ -54,9 +77,10 @@ href=&quot;{$rootUrl}/plugins/ktcore/admin/manageConditionals.php?action=editFieldset
54 <p>{i18n}Changing the conditional type set will remove all existing field 77 <p>{i18n}Changing the conditional type set will remove all existing field
55 ordering!{/i18n}</p> 78 ordering!{/i18n}</p>
56 79
57 -<h2>Master field</h2> 80 +
58 81
59 {if !$oMasterField} 82 {if !$oMasterField}
  83 +<h2>Master field</h2>
60 {i18n}No master field is set, please select the master field{/i18n}: 84 {i18n}No master field is set, please select the master field{/i18n}:
61 85
62 <form action="{$smarty.server.PHP_SELF}" method="POST"> 86 <form action="{$smarty.server.PHP_SELF}" method="POST">
@@ -70,15 +94,26 @@ ordering!{/i18n}&lt;/p&gt; @@ -70,15 +94,26 @@ ordering!{/i18n}&lt;/p&gt;
70 {else} 94 {else}
71 95
72 <form action="{$smarty.server.PHP_SELF}" method="POST"> 96 <form action="{$smarty.server.PHP_SELF}" method="POST">
  97 +<fieldset>
  98 +<legend>Master field</legend>
73 <input type="hidden" name="action" value="setMasterField" /> 99 <input type="hidden" name="action" value="setMasterField" />
74 <input type="hidden" name="fFieldsetId" value="{$oFieldset->getId()}" /> 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 {entity_select entities=$oFieldset->getFields() name="fFieldId" selected=$oMasterField->getId()} 108 {entity_select entities=$oFieldset->getFields() name="fFieldId" selected=$oMasterField->getId()}
  109 +</div>
  110 +<div class="form_actions">
77 <input type="submit" name="submit" value="{i18n}Change master field{/i18n}" /> 111 <input type="submit" name="submit" value="{i18n}Change master field{/i18n}" />
  112 +</div>
  113 +</fieldset>
78 </form> 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 <h2>{i18n}Field ordering{/i18n}</h2> 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,30 +127,38 @@ $this-&gt;assign(&quot;oParentField&quot;, DocumentField::get($this-&gt;_tpl_vars[&#39;aRow&#39;][&#39;paren
92 $this->assign("oChildField", DocumentField::get($this->_tpl_vars['aRow']['child_field_id'])); 127 $this->assign("oChildField", DocumentField::get($this->_tpl_vars['aRow']['child_field_id']));
93 {/php} 128 {/php}
94 <li> 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 </li> 133 </li>
99 {/foreach} 134 {/foreach}
100 </ul> 135 </ul>
101 {/if} 136 {/if}
102 137
103 { if $free_fields } 138 { if $free_fields }
104 -<h3>{i18n}Order fields{/i18n}</h2> 139 +
105 140
106 <form action="{$smarty.server.PHP_SELF}" method="POST"> 141 <form action="{$smarty.server.PHP_SELF}" method="POST">
  142 +<fieldset>
107 <input type="hidden" name="fFieldsetId" value="{$oFieldset->getId()}" /> 143 <input type="hidden" name="fFieldsetId" value="{$oFieldset->getId()}" />
108 <input type="hidden" name="action" value="orderFields" /> 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 {entity_select entities=$parent_fields name="fParentFieldId"} 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 <input type="submit" name="submit" value="{i18n}Order{/i18n}" /> 159 <input type="submit" name="submit" value="{i18n}Order{/i18n}" />
  160 +</div>
  161 +</fieldset>
119 </form> 162 </form>
120 {/if} 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,6 +20,10 @@ while the workflow has no documents or document-versions assigned to the workflo
20 20
21 </fieldset> 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 <h3>{i18n}Transition Availability{/i18n}</h3> 27 <h3>{i18n}Transition Availability{/i18n}</h3>
24 28
25 <p class="descriptiveText">Click on any transition below to edit it directly, 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,3 +70,4 @@ or use the checkboxes to assign which states the transition is available from.&lt;/
66 </div> 70 </div>
67 71
68 </form> 72 </form>
  73 +{/if}
69 \ No newline at end of file 74 \ No newline at end of file