diff --git a/config/config.ini b/config/config.ini index 93edbf0..326027a 100644 --- a/config/config.ini +++ b/config/config.ini @@ -149,3 +149,7 @@ antiword = antiword ; minimum password length on password-setting ; could be moved into DB-auth-config passwordLength = 6 + +; apply the minimum password length to admin while creating / editing accounts? +; default is set to "false" meaning that admins can create users with shorter passwords. +restrictAdminPasswords = default diff --git a/config/dmsDefaults.php b/config/dmsDefaults.php index f853f57..37fdde5 100644 --- a/config/dmsDefaults.php +++ b/config/dmsDefaults.php @@ -390,6 +390,9 @@ $oKTConfig->setdefaultns("tweaks", "genericMetaDataRequired", true); $oKTConfig->setdefaultns("tweaks", "phpErrorLogFile", false); $oKTConfig->setdefaultns("tweaks", "developmentWindowLog", false); +$oKTConfig->setdefaultns("user_prefs", "passwordLength", 6); +$oKTConfig->setdefaultns("user_prefs", "restrictAdminPasswords", false); + $oKTConfig->setdefaultns("ui", "ieGIF", true); $oKTConfig->setdefaultns("ui", "alwaysShowAll", false); diff --git a/lib/documentmanagement/DocumentFieldLink.inc b/lib/documentmanagement/DocumentFieldLink.inc index f910948..924e959 100644 --- a/lib/documentmanagement/DocumentFieldLink.inc +++ b/lib/documentmanagement/DocumentFieldLink.inc @@ -163,7 +163,7 @@ class DocumentFieldLink extends KTEntity { $iMetadataVersionId = $oDocument->getMetadataVersionId(); return KTEntityUtil::getByDict('DocumentFieldLink', array( 'metadata_version_id' => $iMetadataVersionId, - 'document_field_id' => KTUtil::getId($oDocument), + 'document_field_id' => KTUtil::getId($oField), )); } } diff --git a/lib/foldermanagement/folderutil.inc.php b/lib/foldermanagement/folderutil.inc.php index fe54161..a362922 100644 --- a/lib/foldermanagement/folderutil.inc.php +++ b/lib/foldermanagement/folderutil.inc.php @@ -223,10 +223,10 @@ class KTFolderUtil { $sFD = ''; $sFF = ''; if (!empty($aFailedDocuments)) { - $sFD = _('Documents: ') . array_join(', ', $aFailedDocuments) . '. '; + $sFD = _('Documents: ') . implode(', ', $aFailedDocuments) . '. '; } if (!empty($aFailedFolders)) { - $sFF = _('Folders: ') . array_join(', ', $aFailedFolders) . '.'; + $sFF = _('Folders: ') . implode(', ', $aFailedFolders) . '.'; } return PEAR::raiseError(_('You do not have permission to delete these items. ') . $sFD . $sFF); } @@ -267,7 +267,7 @@ class KTFolderUtil { $oPerm = KTPermission::getByName('ktcore.permissions.read'); $oBaseFolderPerm = KTPermission::getByName('ktcore.permissions.addFolder'); - if (!KTPermissionUtil::userHasPermissionOnItem($oUser, $oPerm, $oDestFolder)) { + if (!KTPermissionUtil::userHasPermissionOnItem($oUser, $oBaseFolderPerm, $oDestFolder)) { return PEAR::raiseError(_('You are not allowed to create folders in the destination.')); } @@ -298,7 +298,7 @@ class KTFolderUtil { // child documents $aChildDocs = Document::getList(array('folder_id = ?',array($iFolderId))); foreach ($aChildDocs as $oDoc) { - if (KTPermissionUtil::userHasPermissionOnItem($oUser, $oPerm, $oFolder)) { + if (KTPermissionUtil::userHasPermissionOnItem($oUser, $oPerm, $oDoc)) { $aDocuments[] = $oDoc; } else { $aFailedDocuments[] = $oDoc->getName(); @@ -314,10 +314,10 @@ class KTFolderUtil { $sFD = ''; $sFF = ''; if (!empty($aFailedDocuments)) { - $sFD = _('Documents: ') . array_join(', ', $aFailedDocuments) . '. '; + $sFD = _('Documents: ') . implode(', ', $aFailedDocuments) . '. '; } if (!empty($aFailedFolders)) { - $sFF = _('Folders: ') . array_join(', ', $aFailedFolders) . '.'; + $sFF = _('Folders: ') . implode(', ', $aFailedFolders) . '.'; } return PEAR::raiseError(_('You do not have permission to copy these items. ') . $sFD . $sFF); } @@ -325,6 +325,9 @@ class KTFolderUtil { // first we walk the tree, creating in the new location as we go. // essentially this is an "ok" pass. + + $oStorage =& KTStorageManagerUtil::getSingleton(); + $aFolderMap = array(); $sTable = KTUtil::getTableName('folders'); @@ -339,7 +342,13 @@ class KTFolderUtil { return $id; } $aFolderMap[$oFolder->getId()] = $id; - + $oNewBaseFolder = Folder::get($id); + $res = $oStorage->createFolder($oNewBaseFolder); + if (PEAR::isError($res)) { + // it doesn't exist, so rollback and raise.. + DBUtil::rollback(); + return $res; + } $aRemainingFolders = Folder::getList(array('parent_id = ?', array($oFolder->getId())), array('ids' => true)); @@ -359,34 +368,30 @@ class KTFolderUtil { return $id; } $aFolderMap[$iFolderId] = $id; + $oNewFolder = Folder::get($id); + $res = $oStorage->createFolder($oNewFolder); + if (PEAR::isError($res)) { + // first delete, then rollback, then fail out. + $oStorage->removeFolder($oNewBaseFolder); + DBUtil::rollback(); + return $res; + } $aCFIds = Folder::getList(array('parent_id = ?', array($iFolderId)), array('ids' => true)); $aRemainingFolders = array_merge($aRemainingFolders, $aCFIds); } + + // now we can go ahead. foreach ($aDocuments as $oDocument) { $oChildDestinationFolder = Folder::get($aFolderMap[$oDocument->getFolderID()]); $res = KTDocumentUtil::copy($oDocument, $oChildDestinationFolder); - if (PEAR::isError($res)) { + if (PEAR::isError($res) || ($res === false)) { DBUtil::rollback(); - return PEAR::raiseError(_('Delete Aborted. Unexpected failure to delete document: ') . $oDocument->getName() . $res->getMessage()); + return PEAR::raiseError(_('Delete Aborted. Unexpected failure to copydocument: ') . $oDocument->getName() . $res->getMessage()); } } - - $oStorage =& KTStorageManagerUtil::getSingleton(); - $oStorage->removeFolderTree($oStartFolder); - - // documents all cleared. - $sQuery = 'DELETE FROM ' . KTUtil::getTableName('folders') . ' WHERE id IN (' . DBUtil::paramArray($aFolderIds) . ')'; - $aParams = $aFolderIds; - - $res = DBUtil::runQuery(array($sQuery, $aParams)); - - if (PEAR::isError($res)) { - DBUtil::rollback(); - return PEAR::raiseError(_('Failure deleting folders.')); - } // and store DBUtil::commit(); diff --git a/lib/groups/Group.inc b/lib/groups/Group.inc index 45e5f53..b9e5e4a 100644 --- a/lib/groups/Group.inc +++ b/lib/groups/Group.inc @@ -155,7 +155,7 @@ class Group extends KTEntity { $aMembers = array(); foreach ($aUserIds as $iUserId) { $oUser = User::get($iUserId); - if ($oUser !== false) { + if ((!PEAR::isError($oUser)) && ($oUser !== false)) { $aMembers[] = $oUser; } } @@ -170,7 +170,7 @@ class Group extends KTEntity { $aMembers = array(); foreach ($aGroupIds as $iGroupId) { $oGroup = Group::get($iGroupId); - if ($oGroup !== false) { + if ((!PEAR::isError($oUser)) && ($oGroup !== false)) { $aMembers[] = $oGroup; } } diff --git a/plugins/ktcore/admin/ajaxComplexConditionals.php b/plugins/ktcore/admin/ajaxComplexConditionals.php index 3e7d29d..c4fe8bb 100755 --- a/plugins/ktcore/admin/ajaxComplexConditionals.php +++ b/plugins/ktcore/admin/ajaxComplexConditionals.php @@ -62,6 +62,88 @@ class AjaxConditionalAdminDispatcher extends KTAdminDispatcher { return $oTemplate->render(); } + function do_removeFromBehaviour() { + $oFieldset =& $this->oValidator->validateFieldset(KTUtil::arrayGet($_REQUEST, 'fieldset_id')); + $field_id = KTUtil::arrayGet($_REQUEST, 'field_id'); + $oField =& $this->oValidator->validateField(KTUtil::arrayGet($_REQUEST, 'field_id')); + + header('Content-type: application/xml'); + + $instances = (array) KTUtil::arrayGet($_REQUEST, 'fieldsToRemove'); + + $this->startTransaction(); + + foreach ($instances as $iInstanceId) { + $oInstance = KTValueInstance::get($iInstanceId); + if (PEAR::isError($oInstance) || ($oInstance === false)) { + $this->rollbackTransaction(); + return 'Not OK.'; + } + + $res = $oInstance->delete(); + if (PEAR::isError($res) || ($res === false)) { + $this->rollbackTransaction(); + return 'Not OK.'; + } + } + + $this->commitTransaction(); + + return 'OK.'; + } + + // get the list of ASSIGNED items for a given column, under a certain parent behaviour. + function do_getAssignedList() { + $parent_behaviour = KTUtil::arrayGet($_REQUEST, 'parent_behaviour'); + //$fieldset_id = KTUtil::arrayGet($_REQUEST, 'fieldset_id'); // + $oFieldset =& $this->oValidator->validateFieldset(KTUtil::arrayGet($_REQUEST, 'fieldset_id')); + $field_id = KTUtil::arrayGet($_REQUEST, 'field_id'); + $oField =& $this->oValidator->validateField(KTUtil::arrayGet($_REQUEST, 'field_id')); + + header('Content-type: application/xml'); + $oTemplating =& KTTemplating::getSingleton(); + $oTemplate =& $oTemplating->loadTemplate('ktcore/metadata/conditional/ajax_complex_get_item_list'); + + $aValues = array(); + $aBehaviours = array(); + foreach ($oField->getValues() as $oValue) { + if (empty($parent_behaviour)) { + $oInstance = KTValueInstance::getByLookupSingle($oValue); + if (!empty($oInstance)) { + if (is_null($aBehaviours[$oInstance->getBehaviourId()])) { + $aBehaviours[$oInstance->getBehaviourId()] = KTFieldBehaviour::get($oInstance->getBehaviourId()); + } + $aValues[$oInstance->getId()] = $oValue->getName() . ' - ' . $aBehaviours[$oInstance->getBehaviourId()]->getName(); + } + // No parent behaviour (thus master column), so any + // instance will do to prevent showing this value + continue; + } + + $iInstanceId = KTValueInstance::getByLookupAndParentBehaviour($oValue, $parent_behaviour, array('ids' => true)); + + if (!empty($iInstanceId)) { + + $oInstance = KTValueInstance::get($iInstanceId); + + //print $oInstance->getBehaviourId() . ' - '; + //continue; + + if (is_null($aBehaviours[$oInstance->getBehaviourId()])) { + $aBehaviours[$oInstance->getBehaviourId()] = KTFieldBehaviour::get($oInstance->getBehaviourId()); + } + + $aValues[$oInstance->getId()] = $oValue->getName() . ' - ' . $aBehaviours[$oInstance->getBehaviourId()]->getName(); + } + } + $aData = array( + 'values' => $aValues, + ); + $oTemplate->setData($aData); + + return $oTemplate->render(); + } + function do_getBehaviourList() { $parent_behaviour = KTUtil::arrayGet($_REQUEST, 'parent_behaviour'); $fieldset_id = KTUtil::arrayGet($_REQUEST, 'fieldset_id'); diff --git a/plugins/ktcore/admin/manageConditionals.php b/plugins/ktcore/admin/manageConditionals.php index 855427f..84aef3b 100755 --- a/plugins/ktcore/admin/manageConditionals.php +++ b/plugins/ktcore/admin/manageConditionals.php @@ -19,8 +19,8 @@ class ManageConditionalDispatcher extends KTAdminDispatcher { global $default; $this->ru = $default->rootUrl; // this is not useful: we _still_ don't chain through the right dispatcher (!) - $this->aBreadcrumbs[] = array('url' => $default->rootUrl . '/admin.php/documents', 'name' => _('Document Metadata and Workflow Configuration')); - $this->aBreadcrumbs[] = array('url' => $default->rootUrl . '/admin.php/documents/fieldmanagement', 'name' => _('Document Field Management')); + $this->aBreadcrumbs[] = array('url' => KTUtil::ktLink('/admin.php','documents'), 'name' => _('Document Metadata and Workflow Configuration')); + $this->aBreadcrumbs[] = array('url' => KTUtil::ktLink('/admin.php','documents/fieldmanagement'), 'name' => _('Document Field Management')); } @@ -49,10 +49,11 @@ class ManageConditionalDispatcher extends KTAdminDispatcher { * we can then render in/out. Everything "intelligent" happens * in AJAX (doing it with submits sucks arse. * - * FIXME we fake it here with nested arrays... */ + $oFieldset =& KTFieldset::get($fieldset_id); $aFields =& $oFieldset->getFields(); + $this->aBreadcrumbs[] = array( 'url' => $this->ru . '/admin.php/documents/fieldmanagement', 'query' => 'action=edit&fFieldsetId=' . $oFieldset->getId(), @@ -69,7 +70,7 @@ class ManageConditionalDispatcher extends KTAdminDispatcher { "context" => &$this, "fieldset_id" => $fieldset_id, "aFields" => $aFields, - "iMasterFieldId" => $aFields[0]->getId(), + "iMasterFieldId" => $oFieldset->getMasterFieldId(), ); return $oTemplate->render($aTemplateData); } @@ -104,7 +105,7 @@ class ManageConditionalDispatcher extends KTAdminDispatcher { "context" => &$this, "fieldset_id" => $fieldset_id, "aFields" => $aFields, - "iMasterFieldId" => $aFields[0]->getId(), + "iMasterFieldId" => $oFieldset->getMasterFieldId(), ); return $oTemplate->render($aTemplateData); } diff --git a/plugins/ktcore/admin/userManagement.php b/plugins/ktcore/admin/userManagement.php index e2c484e..b305ef5 100755 --- a/plugins/ktcore/admin/userManagement.php +++ b/plugins/ktcore/admin/userManagement.php @@ -73,13 +73,22 @@ class KTUserAdminDispatcher extends KTAdminDispatcher { $aOptions = array('autocomplete' => false); + // sometimes even admin is restricted in what they can do. + + $KTConfig =& KTConfig::getSingleton(); + $minLength = ((int) $KTConfig->get('user_prefs/passwordLength', 6)); + $restrictAdmin = ((bool) $KTConfig->get('user_prefs/restrictAdminPasswords', false)); + $passwordAddRequirement = ''; + if ($restrictAdmin) { + $passwordAddRequirement = ' ' . sprintf('Password must be at least %d characters long.', $minLength); + } $add_fields = array(); $add_fields[] = new KTStringWidget(_('Username'),_('The username the user will enter to gain access to KnowledgeTree. e.g. jsmith'), 'username', null, $this->oPage, true, null, null, $aOptions); $add_fields[] = new KTStringWidget(_('Name'),_('The full name of the user. This is shown in reports and listings. e.g. John Smith'), 'name', null, $this->oPage, true, null, null, $aOptions); $add_fields[] = new KTStringWidget(_('Email Address'), _('The email address of the user. Notifications and alerts are mailed to this address if email notifications is set below. e.g. jsmith@acme.com'), 'email_address', null, $this->oPage, false, null, null, $aOptions); $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 Dashboard"), 'email_notifications', true, $this->oPage, false, null, null, $aOptions); - $add_fields[] = new KTPasswordWidget(_('Password'), _('Specify an initial password for the user.'), 'password', null, $this->oPage, true, null, null, $aOptions); + $add_fields[] = new KTPasswordWidget(_('Password'), _('Specify an initial password for the user.') . $passwordAddRequirement, 'password', null, $this->oPage, true, null, null, $aOptions); $add_fields[] = new KTPasswordWidget(_('Confirm Password'), _('Confirm the password specified above.'), 'confirm_password', null, $this->oPage, true, null, null, $aOptions); // nice, easy bits. $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. 999 9999 999"), 'mobile_number', null, $this->oPage, false, null, null, $aOptions); @@ -162,7 +171,7 @@ class KTUserAdminDispatcher extends KTAdminDispatcher { $this->aBreadcrumbs[] = array('url' => $_SERVER['PHP_SELF'], 'name' => _('User Management')); $this->oPage->setBreadcrumbDetails(_('change user password')); $this->oPage->setTitle(_("Change User Password")); - + $user_id = KTUtil::arrayGet($_REQUEST, 'user_id'); $oUser =& User::get($user_id); @@ -193,11 +202,18 @@ class KTUserAdminDispatcher extends KTAdminDispatcher { $password = KTUtil::arrayGet($_REQUEST, 'password'); $confirm_password = KTUtil::arrayGet($_REQUEST, 'confirm_password'); - if (empty($password)) { + $KTConfig =& KTConfig::getSingleton(); + $minLength = ((int) $KTConfig->get('user_prefs/passwordLength', 6)); + $restrictAdmin = ((bool) $KTConfig->get('user_prefs/restrictAdminPasswords', false)); + + + if ($restrictAdmin && (strlen($password) < $minLength)) { + $this->errorRedirectToMain(sprintf(_("The password must be at least %d characters long."), $minLength)); + } else if (empty($password)) { $this->errorRedirectToMain(_("You must specify a password for the user.")); } else if ($password !== $confirm_password) { $this->errorRedirectToMain(_("The passwords you specified do not match.")); - } + } // FIXME more validation would be useful. // validated and ready.. $this->startTransaction(); @@ -376,7 +392,14 @@ class KTUserAdminDispatcher extends KTAdminDispatcher { $password = KTUtil::arrayGet($_REQUEST, 'password'); $confirm_password = KTUtil::arrayGet($_REQUEST, 'confirm_password'); - if (empty($password)) { + $KTConfig =& KTConfig::getSingleton(); + $minLength = ((int) $KTConfig->get('user_prefs/passwordLength', 6)); + $restrictAdmin = ((bool) $KTConfig->get('user_prefs/restrictAdminPasswords', false)); + + + if ($restrictAdmin && (strlen($password) < $minLength)) { + $this->errorRedirectTo('addUser', sprintf(_("The password must be at least %d characters long."), $minLength)); + } else if (empty($password)) { $this->errorRedirectTo('addUser', _("You must specify a password for the user.")); } else if ($password !== $confirm_password) { $this->errorRedirectTo('addUser', _("The passwords you specified do not match.")); diff --git a/resources/css/kt-framing.css b/resources/css/kt-framing.css index baa35cd..f1227b0 100644 --- a/resources/css/kt-framing.css +++ b/resources/css/kt-framing.css @@ -1,5 +1,9 @@ /* ------------------ generic ------------------ */ +html { + height: 100%; +} + body { padding: 1em ; border: 0; margin: 0; @@ -14,10 +18,12 @@ body margin: 0; border: 0; padding: 0; } +/* #kt-wrapper { position: relative; } +*/ .copyright { margin-top: 1em; @@ -173,8 +179,8 @@ a.main_nav_item { { position: absolute; width: 14em; - left: 0; - top: 0; + left: 1em; +/* top: 0; */ /* stupid IE bug: absolute position ate my scrollbars */ background: transparent; margin: 0 10px 0 0 ; border: 0; diff --git a/resources/js/conditional_complex_edit.js b/resources/js/conditional_complex_edit.js index 990554b..71bc2ce 100644 --- a/resources/js/conditional_complex_edit.js +++ b/resources/js/conditional_complex_edit.js @@ -191,6 +191,53 @@ function getPOSTRequest(fullurl) { return req; } +function removeFromBehaviour(field_id) { + var action = 'removeFromBehaviour'; + + simpleLog('DEBUG','initiating item list update on field '+field_id); + + var formKeys = Array(); + var formValues = Array(); + // action + formKeys.push('action'); + formValues.push(action); + + // fieldset-id + formKeys.push('fieldset_id'); + formValues.push(getFieldsetId()); + // field_id + formKeys.push('field_id'); + formValues.push(field_id); + + // get the assigned items that are selected. + var column = getColumnForField(field_id); + var assigned_sources = getElementsByTagAndClassName('SELECT','assigned_item_list', column); + if (assigned_sources.length == 0) { + simpleLog('ERROR','Unable to locate assigned items.'); + return; + } + var assigned_select = assigned_sources[0]; + for (var i=0; ioPage->requireJSStandalone($sJS)}

{i18n}Edit Complex Conditional Metadata{/i18n}

+ +

Complex Conditional Metadata depends on what are +called "behaviours". Essentially, behaviours are assigned to a single +field, and can contain any number of values that are available in that field. Each field +can have multiple behaviours assigned to it.

+ +

Each behaviour can cause a number of other behaviours — in +the field's child fields — to become available. By assigning values +to behaviours, and creating relationships between behaviours, you can create extremely complex +relationships between available lookup values.

+
@@ -54,6 +71,21 @@ addLoadEvent(updateActiveFields);

{$oField->getName()}

{i18n}This column is not active.{/i18n}

{i18n}Editing behaviour Jack{/i18n}

+
+

{i18n}Assigned Items{/i18n}

+ +
+

No items have been assigned with the current + parent behaviour.

+
+

{i18n}Unassigned/Unavailable{/i18n}

-
- {i18n}Change Assignments for this field.{/i18n} +
{/foreach} diff --git a/templates/ktcore/metadata/conditional/editsimple.smarty b/templates/ktcore/metadata/conditional/editsimple.smarty index c1de580..330e7d0 100644 --- a/templates/ktcore/metadata/conditional/editsimple.smarty +++ b/templates/ktcore/metadata/conditional/editsimple.smarty @@ -3,16 +3,20 @@ /* inactivity */ .active .inactivity_message { display: none; } +select { width: 100%; } + +.edit_button { margin-bottom: 0.5em; } .save_button, -.done_button { display: none; } +.done_button { display: none; margin-bottom: 0.5em;} .active.editing .save_button, .active.editing .done_button { display: block; } .active.editing .edit_button { display: none; } - .active { position: relative; } +td { vertical-align: top; } +.buttonset.inactive { background: transparent; } .inactive { background: #ccc; position: relative; } .inactive .fixed_message, @@ -61,14 +65,24 @@ addLoadEvent(partial(editSimpleField, {$iMasterFieldId}));

{i18n}Editing Fieldset Rules (Simple){/i18n}

+

To make a value in a child fieldavailable to the user when another value is +selected in a parent field, first ensure that the parent field is being edited (it will have "save" and "done" +as the buttons at the bottom of the column) and then select the value for the parent field. Now select the value(s) in +the child column(s) you wish to be available to the user when the parent field's value is selected, +and click "save". Note you that you can use Ctrl-<click> to select multiple child values +at the same time.

+ +

Changes made here are stored immediately, without you needing to +refresh the page.

+ - - - +{foreach from=$aFields item=oField} + +{/foreach} @@ -76,22 +90,28 @@ addLoadEvent(partial(editSimpleField, {$iMasterFieldId})); {/foreach} + +{foreach from=$aFields item=oField} + +{/foreach} +
Column 1Column 2Column 3{$oField->getName()}

{i18n}This field is not controlled by the currently active group.{/i18n}

-

Field: {$oField->getName()}

-
- - - -
+
+
+ + + +
+