Merge pull request #65 from LDAPAccountManager/feature/parallelEditing
Feature/parallel editing
This commit is contained in:
		
						commit
						2993588a2a
					
				|  | @ -1,6 +1,7 @@ | |||
| 6.8 | ||||
|   - Windows: added home drive and force password change to profile editor | ||||
|   - Parallel editing of multiple entries in different browser tabs supported | ||||
|   - LAM supports the progressive web app standard which allows to install LAM as an icon on home screen | ||||
|   - Windows: added home drive and force password change to profile editor | ||||
|   - LAM Pro: | ||||
|    -> Bind DLZ: entry table can show record data (use special attribute "#records" in server profile) | ||||
|   - Fixed bugs: | ||||
|  |  | |||
|  | @ -745,9 +745,8 @@ class accountContainer { | |||
| 	* | ||||
| 	* @param ConfiguredType $type account type | ||||
| 	* @param string $base key in $_SESSION where this object is saved | ||||
| 	* @param integer $randomID random ID to avoid parallel editing (default: null) | ||||
| 	*/ | ||||
| 	function __construct($type, $base, $randomID = null) { | ||||
| 	function __construct($type, $base) { | ||||
| 		if (!($type instanceof ConfiguredType)) { | ||||
| 			trigger_error('Argument of accountContainer must be ConfiguredType.', E_USER_ERROR); | ||||
| 		} | ||||
|  | @ -760,7 +759,6 @@ class accountContainer { | |||
| 		$this->current_page=0; | ||||
| 		$this->subpage='attributes'; | ||||
| 		$this->isNewAccount = false; | ||||
| 		$this->randomID = $randomID; | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
|  | @ -824,8 +822,6 @@ class accountContainer { | |||
| 	/** send password via mail to this alternate address */ | ||||
| 	private $sendPasswordViaMailAlternateAddress = null; | ||||
| 
 | ||||
| 	/** random ID number to avoid parallel editing of accounts in multiple browser tabs */ | ||||
| 	private $randomID = null; | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Returns the account module with the given class name | ||||
|  | @ -865,10 +861,6 @@ class accountContainer { | |||
| 	* It prints the HTML code of each account page. | ||||
| 	*/ | ||||
| 	function continue_main() { | ||||
| 		if (!empty($_POST['account_randomID']) && ($this->randomID != $_POST['account_randomID'])) { | ||||
| 			metaRefresh("../lists/list.php?type=" . $this->type->getId() . '&accountEditInvalidID=true'); | ||||
| 			exit(); | ||||
| 		} | ||||
| 		$oldPage = $this->current_page; | ||||
| 		$oldSubpage = $this->subpage; | ||||
| 		$result = array(); | ||||
|  | @ -1322,7 +1314,7 @@ class accountContainer { | |||
| 			$passwordButton = new htmlButton('accountContainerPassword', _('Set password')); | ||||
| 			$passwordButton->setIconClass('passwordButton'); | ||||
| 			$passwordButton->setOnClick('passwordShowChangeDialog(\'' . _('Set password') . '\', \'' . _('Ok') . '\', \'' | ||||
| 				 . _('Cancel') . '\', \'' . _('Set random password') . '\', \'../misc/ajax.php?function=passwordChange\',\'' | ||||
| 				 . _('Cancel') . '\', \'' . _('Set random password') . '\', \'../misc/ajax.php?function=passwordChange&editKey=' . htmlspecialchars($this->base) . '\',\'' | ||||
| 				 . getSecurityTokenName() . '\',\'' . getSecurityTokenValue() . '\');'); | ||||
| 			$leftButtonGroup->addElement($passwordButton); | ||||
| 		} | ||||
|  | @ -1377,8 +1369,7 @@ class accountContainer { | |||
| 					jQuery("#inputForm").validationEngine(); | ||||
| 				}); | ||||
| 			</script>'; | ||||
| 		echo "<form id=\"inputForm\" enctype=\"multipart/form-data\" action=\"edit.php\" method=\"post\" onSubmit=\"saveScrollPosition('inputForm')\" autocomplete=\"off\">\n"; | ||||
| 		echo '<input type="hidden" name="account_randomID" value="' . $this->randomID . '">'; | ||||
| 		echo "<form id=\"inputForm\" enctype=\"multipart/form-data\" action=\"edit.php?editKey=" . htmlspecialchars($this->base) . "\" method=\"post\" onSubmit=\"saveScrollPosition('inputForm')\" autocomplete=\"off\">\n"; | ||||
| 		echo '<input type="hidden" name="' . getSecurityTokenName() . '" value="' . getSecurityTokenValue() . '">'; | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -4,10 +4,9 @@ use \LAMException; | |||
| use \phpseclib\Net\SSH2; | ||||
| use \phpseclib\Crypt\RSA; | ||||
| /* | ||||
| $Id$ | ||||
| 
 | ||||
|   This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) | ||||
|   Copyright (C) 2017  Roland Gruber | ||||
|   Copyright (C) 2017 - 2019  Roland Gruber | ||||
| 
 | ||||
|   This program is free software; you can redistribute it and/or modify | ||||
|   it under the terms of the GNU General Public License as published by | ||||
|  | @ -108,8 +107,10 @@ class Remote { | |||
| 			$sr = @ldap_read($_SESSION['ldap']->server(), $credentials[0], "objectClass=posixAccount", array('uid'), 0, 0, 0, LDAP_DEREF_NEVER); | ||||
| 			if ($sr) { | ||||
| 				$entry = @ldap_get_entries($_SESSION['ldap']->server(), $sr); | ||||
| 				if (!empty($entry[0]['uid'])) { | ||||
| 					$username = $entry[0]['uid'][0]; | ||||
| 				} | ||||
| 			} | ||||
| 			if (empty($username)) { | ||||
| 				throw new LAMException(sprintf(_("Your LAM admin user (%s) must be a valid Unix account to work with lamdaemon!"), getAbstractDN($credentials[0]))); | ||||
| 			} | ||||
|  |  | |||
										
											Binary file not shown.
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -3,7 +3,7 @@ | |||
| 
 | ||||
|   This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) | ||||
|   Copyright (C) 2003 - 2006  Tilo Lutz | ||||
|                 2005 - 2018  Roland Gruber | ||||
|                 2005 - 2019  Roland Gruber | ||||
| 
 | ||||
|   This program is free software; you can redistribute it and/or modify | ||||
|   it under the terms of the GNU General Public License as published by | ||||
|  | @ -57,6 +57,31 @@ if (!isLoggedIn()) { | |||
| // Set correct language, codepages, ....
 | ||||
| setlanguage(); | ||||
| 
 | ||||
| $sessionAccountPrefix = 'editContainer'; | ||||
| if (isset($_GET['editKey'])) { | ||||
| 	$sessionKey = htmlspecialchars($_GET['editKey']); | ||||
| } | ||||
| else { | ||||
| 	$sessionKey = $sessionAccountPrefix . (new \DateTime(null, getTimeZone()))->getTimestamp() . getRandomNumber(); | ||||
| } | ||||
| 
 | ||||
| // cleanup account containers in session
 | ||||
| $cleanupCandidates = array(); | ||||
| foreach ($_SESSION as $key => $value) { | ||||
| 	if (strpos($key, $sessionAccountPrefix) === 0) { | ||||
| 		$cleanupCandidates[] = $key; | ||||
| 	} | ||||
| 	$candidateCount = sizeof($cleanupCandidates); | ||||
| 	if ($candidateCount > 100) { | ||||
| 		$numToDelete = $candidateCount - 100; | ||||
| 		natsort($cleanupCandidates); | ||||
| 		for ($i = 0; $i < $numToDelete; $i++) { | ||||
| 			$toDelete = array_shift($cleanupCandidates); | ||||
| 			unset($_SESSION[$toDelete]); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| $typeManager = new LAM\TYPES\TypeManager(); | ||||
| //load account
 | ||||
| if (isset($_GET['DN'])) { | ||||
|  | @ -80,8 +105,8 @@ if (isset($_GET['DN'])) { | |||
| 		logNewMessage(LOG_ERR, 'User tried to access entry of type ' . $type->getId() . ' outside suffix ' . $suffix); | ||||
| 		die(); | ||||
| 	} | ||||
| 	$_SESSION['account'] = new accountContainer($type, 'account', getRandomNumber()); | ||||
| 	$result = $_SESSION['account']->load_account($DN); | ||||
| 	$_SESSION[$sessionKey] = new accountContainer($type, $sessionKey); | ||||
| 	$result = $_SESSION[$sessionKey]->load_account($DN); | ||||
| 	if (sizeof($result) > 0) { | ||||
| 		include __DIR__ . '/../../lib/adminHeader.inc'; | ||||
| 		foreach ($result as $message) { | ||||
|  | @ -92,7 +117,7 @@ if (isset($_GET['DN'])) { | |||
| 	} | ||||
| } | ||||
| // new account
 | ||||
| else if (count($_POST) == 0) { | ||||
| elseif (empty($_POST)) { | ||||
| 	$type = $typeManager->getConfiguredType($_GET['type']); | ||||
| 	if ($type->isHidden()) { | ||||
| 		logNewMessage(LOG_ERR, 'User tried to access hidden account type: ' . $type->getId()); | ||||
|  | @ -102,11 +127,11 @@ else if (count($_POST) == 0) { | |||
| 		logNewMessage(LOG_ERR, 'User tried to create entry of forbidden account type: ' . $type->getId()); | ||||
| 		die(); | ||||
| 	} | ||||
| 	$_SESSION['account'] = new accountContainer($type, 'account', getRandomNumber()); | ||||
| 	$_SESSION['account']->new_account(); | ||||
| 	$_SESSION[$sessionKey] = new accountContainer($type, $sessionKey); | ||||
| 	$_SESSION[$sessionKey]->new_account(); | ||||
| } | ||||
| 
 | ||||
| // show account page
 | ||||
| $_SESSION['account']->continue_main(); | ||||
| $_SESSION[$sessionKey]->continue_main(); | ||||
| 
 | ||||
| ?>
 | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ use \htmlStatusMessage; | |||
| 
 | ||||
| 	This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/) | ||||
| 	Copyright (C) 2003 - 2006  Tilo Lutz | ||||
| 	Copyright (C) 2007 - 2018  Roland Gruber | ||||
| 	Copyright (C) 2007 - 2019  Roland Gruber | ||||
| 
 | ||||
| 	This program is free software; you can redistribute it and/or modify | ||||
| 	it under the terms of the GNU General Public License as published by | ||||
|  | @ -74,6 +74,14 @@ if (!empty($_POST)) { | |||
| 	validateSecurityToken(); | ||||
| } | ||||
| 
 | ||||
| $sessionAccountPrefix = 'deleteContainer'; | ||||
| foreach ($_SESSION as $key => $value) { | ||||
| 	if (strpos($key, $sessionAccountPrefix) === 0) { | ||||
| 		unset($_SESSION[$key]); | ||||
| 		logNewMessage(LOG_NOTICE, "del " . $key); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| $typeManager = new \LAM\TYPES\TypeManager(); | ||||
| 
 | ||||
| if (isset($_POST['type']) && ($typeManager->getConfiguredType($_POST['type']) === null)) { | ||||
|  | @ -99,8 +107,9 @@ if (isset($_GET['type']) && isset($_SESSION['delete_dn'])) { | |||
| 		$users[] = substr($dn, $start, $end-$start); | ||||
| 	} | ||||
| 
 | ||||
| 	$sessionKey = $sessionAccountPrefix . (new \DateTime(null, getTimeZone()))->getTimestamp() . getRandomNumber(); | ||||
| 	//load account
 | ||||
| 	$_SESSION['account'] = new \accountContainer($type, 'account'); | ||||
| 	$_SESSION[$sessionKey] = new \accountContainer($type, $sessionKey); | ||||
| 	// Show HTML Page
 | ||||
| 	include '../lib/adminHeader.inc'; | ||||
| 	echo "<div class=\"" . $type->getScope() . "-bright smallPaddingContent\">"; | ||||
|  | @ -116,8 +125,8 @@ if (isset($_GET['type']) && isset($_SESSION['delete_dn'])) { | |||
| 		$container->addField(new htmlOutputText($users[$i])); | ||||
| 		$container->addLabel(new htmlOutputText(_('DN') . ':')); | ||||
| 		$container->addField(new htmlOutputText($_SESSION['delete_dn'][$i])); | ||||
| 		$_SESSION['account']->load_account($_SESSION['delete_dn'][$i]); | ||||
| 		if (!$_SESSION['account']->hasOnlyVirtualChildren()) { | ||||
| 		$_SESSION[$sessionKey]->load_account($_SESSION['delete_dn'][$i]); | ||||
| 		if (!$_SESSION[$sessionKey]->hasOnlyVirtualChildren()) { | ||||
| 			$childCount = getChildCount($_SESSION['delete_dn'][$i]); | ||||
| 			if ($childCount > 0) { | ||||
| 				$container->addLabel(new htmlOutputText(_('Number of child entries') . ':')); | ||||
|  | @ -182,6 +191,8 @@ if (isset($_POST['delete'])) { | |||
| 	addSecurityTokenToMetaHTML($container); | ||||
| 	$container->add(new htmlHiddenInput('type', $type->getId()), 12); | ||||
| 
 | ||||
| 	$sessionKey = $sessionAccountPrefix . (new \DateTime(null, getTimeZone()))->getTimestamp() . getRandomNumber(); | ||||
| 	$_SESSION[$sessionKey] = new \accountContainer($type, $sessionKey); | ||||
| 	// Delete dns
 | ||||
| 	$allOk = true; | ||||
| 	$allErrors = array(); | ||||
|  | @ -189,10 +200,10 @@ if (isset($_POST['delete'])) { | |||
| 		// Set to true if an real error has happened
 | ||||
| 		$stopprocessing = false; | ||||
| 		// First load DN.
 | ||||
| 		$_SESSION['account']->load_account($deleteDN); | ||||
| 		$_SESSION[$sessionKey]->load_account($deleteDN); | ||||
| 		// get commands and changes of each attribute
 | ||||
| 		$moduleNames = array_keys($_SESSION['account']->getAccountModules()); | ||||
| 		$modules = $_SESSION['account']->getAccountModules(); | ||||
| 		$moduleNames = array_keys($_SESSION[$sessionKey]->getAccountModules()); | ||||
| 		$modules = $_SESSION[$sessionKey]->getAccountModules(); | ||||
| 		$attributes = array(); | ||||
| 		$errors = array(); | ||||
| 		// predelete actions
 | ||||
|  | @ -279,7 +290,7 @@ if (isset($_POST['delete'])) { | |||
| 			} | ||||
| 		} | ||||
| 		if (!$stopprocessing) { | ||||
| 			$recursive = !$_SESSION['account']->hasOnlyVirtualChildren(); | ||||
| 			$recursive = !$_SESSION[$sessionKey]->hasOnlyVirtualChildren(); | ||||
| 			$messages = deleteDN($deleteDN, $recursive); | ||||
| 			$errors = array_merge($errors, $messages); | ||||
| 			if (sizeof($errors) > 0) { | ||||
|  |  | |||
|  | @ -75,8 +75,12 @@ class Ajax { | |||
| 		if (isset($_GET['module']) && isset($_GET['scope']) && in_array($_GET['module'], getAvailableModules($_GET['scope']))) { | ||||
| 			enforceUserIsLoggedIn(); | ||||
| 			if (isset($_GET['useContainer']) && ($_GET['useContainer'] == '1')) { | ||||
| 				if (!isset($_SESSION['account'])) die(); | ||||
| 				$module = $_SESSION['account']->getAccountModule($_GET['module']); | ||||
| 				$sessionKey  = htmlspecialchars($_GET['editKey']); | ||||
| 				if (!isset($_SESSION[$sessionKey])) { | ||||
| 					logNewMessage(LOG_ERR, 'Unable to find account container'); | ||||
| 					die(); | ||||
| 				} | ||||
| 				$module = $_SESSION[$sessionKey]->getAccountModule($_GET['module']); | ||||
| 				$module->handleAjaxRequest(); | ||||
| 			} | ||||
| 			else { | ||||
|  | @ -157,7 +161,8 @@ class Ajax { | |||
| 	 * @param array $input input parameters | ||||
| 	 */ | ||||
| 	private static function managePasswordChange($input) { | ||||
| 		$return = $_SESSION['account']->setNewPassword($input); | ||||
| 		$sessionKey  = htmlspecialchars($_GET['editKey']); | ||||
| 		$return = $_SESSION[$sessionKey]->setNewPassword($input); | ||||
| 		echo json_encode($return); | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue