From 71ed889da3d236785a741de4e3ce7018983bd00e Mon Sep 17 00:00:00 2001 From: nuxsmin Date: Wed, 1 Mar 2017 01:22:26 +0100 Subject: [PATCH] * [ADD] Added live update for master key changing process. --- inc/SP/Account/AccountCrypt.class.php | 28 +-- inc/SP/Account/AccountHistoryCrypt.class.php | 31 +-- inc/SP/Account/AccountUtil.class.php | 16 ++ inc/SP/Controller/AccountController.class.php | 18 +- .../ConfigActionController.class.php | 19 +- inc/SP/Controller/ConfigController.class.php | 5 + .../Controller/MainActionController.class.php | 9 + inc/SP/Controller/MainController.class.php | 5 +- inc/SP/Controller/TaskController.class.php | 155 +++++++++++---- inc/SP/Core/Init.class.php | 2 +- inc/SP/Core/Messages/TaskMessage.class.php | 22 +++ inc/SP/Core/Session.class.php | 4 +- inc/SP/Core/Task.class.php | 115 ++++++++--- inc/SP/Core/TaskFactory.class.php | 82 ++++++++ inc/SP/Core/Upgrade/Account.class.php | 21 +- inc/SP/Core/Upgrade/Category.class.php | 5 + inc/SP/Core/Upgrade/Crypt.class.php | 21 +- inc/SP/Core/Upgrade/Customer.class.php | 5 + inc/SP/Core/Upgrade/Group.class.php | 5 + inc/SP/Core/Upgrade/Profile.class.php | 5 + inc/SP/Core/Upgrade/Upgrade.class.php | 34 +++- inc/SP/Core/Upgrade/User.class.php | 5 + inc/SP/Util/Util.class.php | 44 ++++- inc/locales/en_US/LC_MESSAGES/messages.mo | Bin 88437 -> 88694 bytes inc/locales/en_US/LC_MESSAGES/messages.po | 184 +++++++++--------- .../material-blue/views/config/encryption.inc | 36 ++-- .../views/main/body-header-menu.inc | 8 +- .../material-blue/views/main/upgrade.inc | 7 +- js/app-actions.js | 67 +++++-- js/app-actions.min.js | 84 ++++---- js/app-requests.min.js | 2 +- js/strings.js.php | 3 +- 32 files changed, 736 insertions(+), 311 deletions(-) create mode 100644 inc/SP/Core/TaskFactory.class.php diff --git a/inc/SP/Account/AccountCrypt.class.php b/inc/SP/Account/AccountCrypt.class.php index 343546d8..6010700e 100644 --- a/inc/SP/Account/AccountCrypt.class.php +++ b/inc/SP/Account/AccountCrypt.class.php @@ -32,6 +32,7 @@ use SP\Core\OldCrypt; use SP\Core\Exceptions\SPException; use SP\Core\Session; use SP\Core\Task; +use SP\Core\TaskFactory; use SP\DataModel\AccountData; use SP\Log\Email; use SP\Log\Log; @@ -51,7 +52,7 @@ class AccountCrypt * Actualiza las claves de todas las cuentas con la clave maestra actual * usando nueva encriptación. * - * @param $currentMasterPass + * @param string $currentMasterPass * @return bool */ public function updateOldPass(&$currentMasterPass) @@ -87,10 +88,9 @@ class AccountCrypt } $AccountDataBase = new AccountData(); - $Task = Session::getTask(); - $Message = new TaskMessage(); - $Message->setTask(__('Actualizar Clave Maestra')); + TaskFactory::$Message->setTask(__('Actualizar Clave Maestra')); + TaskFactory::sendTaskMessage(); $counter = 0; $startTime = time(); @@ -107,11 +107,10 @@ class AccountCrypt if ($counter % 100 === 0) { $eta = Util::getETA($startTime, $counter, $numAccounts); - $Message->setMessage(__('Cuentas actualizadas') . ': ' . $counter . '/' . $numAccounts); - $Message->setProgress(round(($counter * 100) / $numAccounts, 2)); - $Message->setTime(sprintf('ETA: %ds (%.2f/s)', $eta[0], $eta[1])); - - $Task->writeJsonStatusAndFlush($Message); + TaskFactory::$Message->setMessage(__('Cuentas actualizadas') . ': ' . $counter . '/' . $numAccounts); + TaskFactory::$Message->setProgress(round(($counter * 100) / $numAccounts, 2)); + TaskFactory::$Message->setTime(sprintf('ETA: %ds (%.2f/s)', $eta[0], $eta[1])); + TaskFactory::sendTaskMessage(); } $AccountData = clone $AccountDataBase; @@ -178,11 +177,12 @@ class AccountCrypt /** * Actualiza las claves de todas las cuentas con la nueva clave maestra. * - * @param $currentMasterPass - * @param $newMasterPass + * @param string $currentMasterPass + * @param string $newMasterPass + * @param Task $Task * @return bool */ - public function updatePass($currentMasterPass, $newMasterPass) + public function updatePass($currentMasterPass, $newMasterPass, Task $Task) { set_time_limit(0); @@ -208,11 +208,13 @@ class AccountCrypt } $AccountDataBase = new AccountData(); - $Task = new Task(__FUNCTION__); $Message = new TaskMessage(); + $Message->setTaskId($Task->getTaskId()); $Message->setTask(__('Actualizar Clave Maestra')); + $Task->writeJsonStatusAndFlush($Message); + $counter = 0; $startTime = time(); diff --git a/inc/SP/Account/AccountHistoryCrypt.class.php b/inc/SP/Account/AccountHistoryCrypt.class.php index 110ab700..3f771743 100644 --- a/inc/SP/Account/AccountHistoryCrypt.class.php +++ b/inc/SP/Account/AccountHistoryCrypt.class.php @@ -31,7 +31,8 @@ use SP\Core\Exceptions\QueryException; use SP\Core\Messages\TaskMessage; use SP\Core\OldCrypt; use SP\Core\Exceptions\SPException; -use SP\Core\Session; +use SP\Core\Task; +use SP\Core\TaskFactory; use SP\Log\Email; use SP\Log\Log; use SP\Storage\DB; @@ -55,7 +56,7 @@ class AccountHistoryCrypt * Actualiza las claves de todas las cuentas con la clave maestra actual * usando nueva encriptación. * - * @param $currentMasterPass + * @param string $currentMasterPass * @return bool */ public function updateOldPass(&$currentMasterPass) @@ -95,10 +96,8 @@ class AccountHistoryCrypt $AccountDataBase->key = ''; $AccountDataBase->hash = Hash::hashKey($currentMasterPass); - $Task = Session::getTask(); - - $Message = new TaskMessage(); - $Message->setTask(__('Actualizar Clave Maestra (H)')); + TaskFactory::$Message->setTask(__('Actualizar Clave Maestra (H)')); + TaskFactory::sendTaskMessage(); $counter = 0; $startTime = time(); @@ -115,11 +114,11 @@ class AccountHistoryCrypt if ($counter % 100 === 0) { $eta = Util::getETA($startTime, $counter, $numAccounts); - $Message->setMessage(__('Cuentas actualizadas') . ': ' . $counter . '/' . $numAccounts); - $Message->setProgress(round(($counter * 100) / $numAccounts, 2)); - $Message->setTime(sprintf('ETA: %ds (%.2f/s)', $eta[0], $eta[1])); + TaskFactory::$Message->setMessage(__('Cuentas actualizadas') . ': ' . $counter . '/' . $numAccounts); + TaskFactory::$Message->setProgress(round(($counter * 100) / $numAccounts, 2)); + TaskFactory::$Message->setTime(sprintf('ETA: %ds (%.2f/s)', $eta[0], $eta[1])); - $Task->writeJsonStatusAndFlush($Message); + TaskFactory::sendTaskMessage(); } $AccountData = clone $AccountDataBase; @@ -189,11 +188,12 @@ class AccountHistoryCrypt /** * Actualiza las claves de todas las cuentas con la nueva clave maestra. * - * @param $currentMasterPass - * @param $newMasterPass + * @param string $currentMasterPass + * @param string $newMasterPass + * @param Task $Task * @return bool */ - public function updatePass($currentMasterPass, $newMasterPass) + public function updatePass($currentMasterPass, $newMasterPass, Task $Task) { set_time_limit(0); @@ -223,11 +223,12 @@ class AccountHistoryCrypt $AccountDataBase->key = ''; $AccountDataBase->hash = Hash::hashKey($newMasterPass); - $Task = Session::getTask(); - $Message = new TaskMessage(); + $Message->setTaskId($Task->getTaskId()); $Message->setTask(__('Actualizar Clave Maestra (H)')); + $Task->writeJsonStatusAndFlush($Message); + $counter = 0; $startTime = time(); diff --git a/inc/SP/Account/AccountUtil.class.php b/inc/SP/Account/AccountUtil.class.php index 6592cab1..f6a13239 100644 --- a/inc/SP/Account/AccountUtil.class.php +++ b/inc/SP/Account/AccountUtil.class.php @@ -296,4 +296,20 @@ class AccountUtil return DB::getResultsArray($Data); } + + /** + * Devolver el número de cuentas a procesar + * + * @return int + */ + public static function getTotalNumAccounts() + { + $query = /** @lang SQL */ + 'SELECT SUM(n) AS num FROM (SELECT COUNT(*) AS n FROM accounts UNION SELECT COUNT(*) AS n FROM accHistory) a'; + + $Data = new QueryData(); + $Data->setQuery($query); + + return (int)DB::getResults($Data)->num; + } } \ No newline at end of file diff --git a/inc/SP/Controller/AccountController.class.php b/inc/SP/Controller/AccountController.class.php index 602e01b2..a6e5d83b 100644 --- a/inc/SP/Controller/AccountController.class.php +++ b/inc/SP/Controller/AccountController.class.php @@ -287,7 +287,7 @@ class AccountController extends ControllerBase implements ActionsInterface $publicLinkUrl = (Checks::publicLinksIsEnabled() && $PublicLinkData ? Init::$WEBURI . '/index.php?h=' . $PublicLinkData->getPublicLinkHash() . '&a=link' : null); $this->view->assign('publicLinkUrl', $publicLinkUrl); - $this->view->assign('publicLinkId', $PublicLinkData->getPublicLinkId()); + $this->view->assign('publicLinkId', $PublicLinkData ? $PublicLinkData->getPublicLinkId() : 0); $this->view->assign('accountPassDate', date('Y-m-d H:i:s', $this->AccountData->getAccountPassDate())); $this->view->assign('accountPassDateChange', date('Y-m-d', $this->AccountData->getAccountPassDateChange() ?: 0)); @@ -324,6 +324,14 @@ class AccountController extends ControllerBase implements ActionsInterface $this->view->assign('customFields', CustomField::getItem(new CustomFieldData(ActionsInterface::ACTION_ACC))->getById($this->getId())); } + /** + * @return int + */ + private function getId() + { + return $this->id; + } + /** * @return \SP\Account\Account|AccountHistory */ @@ -378,14 +386,6 @@ class AccountController extends ControllerBase implements ActionsInterface return true; } - /** - * @return int - */ - private function getId() - { - return $this->id; - } - /** * Obtener los datos para mostrar el interface para editar cuenta * diff --git a/inc/SP/Controller/ConfigActionController.class.php b/inc/SP/Controller/ConfigActionController.class.php index 0efaf4e8..94f8a32f 100644 --- a/inc/SP/Controller/ConfigActionController.class.php +++ b/inc/SP/Controller/ConfigActionController.class.php @@ -38,6 +38,7 @@ use SP\Core\Init; use SP\Core\Messages\LogMessage; use SP\Core\Messages\NoticeMessage; use SP\Core\Session; +use SP\Core\Task; use SP\Core\XmlExport; use SP\Http\Request; use SP\Import\Import; @@ -420,7 +421,6 @@ class ConfigActionController implements ItemControllerInterface */ protected function mailAction() { - $Log = Log::newLog(__('Modificar Configuración', false)); $Config = Session::getConfig(); // Mail @@ -519,20 +519,27 @@ class ConfigActionController implements ItemControllerInterface return; } + $Task = new Task(__FUNCTION__, Request::analyze('taskId')); + $Task->register(); + $Account = new AccountCrypt(); - if (!$Account->updatePass($currentMasterPass, $newMasterPass)) { + if (!$Account->updatePass($currentMasterPass, $newMasterPass, $Task)) { DB::rollbackTransaction(); + $Task->end(); + $this->JsonResponse->setDescription(__('Errores al actualizar las claves de las cuentas', false)); return; } $AccountHistory = new AccountHistoryCrypt(); - if (!$AccountHistory->updatePass($currentMasterPass, $newMasterPass)) { + if (!$AccountHistory->updatePass($currentMasterPass, $newMasterPass, $Task)) { DB::rollbackTransaction(); + $Task->end(); + $this->JsonResponse->setDescription(__('Errores al actualizar las claves de las cuentas del histórico', false)); return; } @@ -540,15 +547,21 @@ class ConfigActionController implements ItemControllerInterface if (!CustomFieldsUtil::updateCustomFieldsCrypt($currentMasterPass, $newMasterPass)) { DB::rollbackTransaction(); + $Task->end(); + $this->JsonResponse->setDescription(__('Errores al actualizar datos de campos personalizados', false)); return; } if (!DB::endTransaction()) { + $Task->end(); + $this->JsonResponse->setDescription(__('No es posible finalizar una transacción', false)); return; } + $Task->end(); + Util::unlockApp(); } diff --git a/inc/SP/Controller/ConfigController.class.php b/inc/SP/Controller/ConfigController.class.php index bdcca632..7cc474d8 100644 --- a/inc/SP/Controller/ConfigController.class.php +++ b/inc/SP/Controller/ConfigController.class.php @@ -26,6 +26,7 @@ namespace SP\Controller; defined('APP_ROOT') || die(); +use SP\Account\AccountUtil; use SP\Config\Config; use SP\Config\ConfigData; use SP\Config\ConfigDB; @@ -37,6 +38,7 @@ use SP\Core\Language; use SP\Core\Plugin\PluginUtil; use SP\Core\Session; use SP\Core\SessionUtil; +use SP\Core\Task; use SP\Core\Template; use SP\Mgmt\Groups\Group; use SP\Mgmt\Profiles\Profile; @@ -312,6 +314,9 @@ class ConfigController extends ControllerBase implements ActionsInterface $this->view->addTemplate('encryption'); + $this->view->assign('numAccounts', AccountUtil::getTotalNumAccounts()); + $this->view->assign('taskId', Task::genTaskId('masterpass')); + $this->view->assign('lastUpdateMPass', isset($this->configDB['lastupdatempass']) ? $this->configDB['lastupdatempass'] : 0); $this->view->assign('tempMasterPassTime', isset($this->configDB['tempmaster_passtime']) ? $this->configDB['tempmaster_passtime'] : 0); $this->view->assign('tempMasterMaxTime', isset($this->configDB['tempmaster_maxtime']) ? $this->configDB['tempmaster_maxtime'] : 0); diff --git a/inc/SP/Controller/MainActionController.class.php b/inc/SP/Controller/MainActionController.class.php index ce292953..c5a7db98 100644 --- a/inc/SP/Controller/MainActionController.class.php +++ b/inc/SP/Controller/MainActionController.class.php @@ -27,6 +27,7 @@ namespace SP\Controller; use SP\Config\Config; use SP\Core\Exceptions\ValidationException; use SP\Core\Session; +use SP\Core\TaskFactory; use SP\Core\Upgrade\Upgrade; use SP\Http\JsonResponse; use SP\Http\Request; @@ -51,6 +52,7 @@ class MainActionController { $version = Request::analyze('version', $version); $type = Request::analyze('type'); + $taskId = Request::analyze('taskId'); if (Request::analyze('a') === 'upgrade' && Request::analyze('upgrade', 0) === 1 @@ -65,12 +67,17 @@ class MainActionController throw new ValidationException(__('Es necesario confirmar la actualización', false)); } + TaskFactory::createTask('upgrade', $taskId); + $this->upgrade($version, $type); + $JsonResponse->setDescription(__('Aplicación actualizada correctamente', false)); $JsonResponse->addMessage(__('En 5 segundos será redirigido al login', false)); $JsonResponse->setStatus(0); } catch (\Exception $e) { + TaskFactory::endTask(); + $JsonResponse->setDescription($e->getMessage()); } @@ -94,6 +101,8 @@ class MainActionController { Upgrade::doUpgrade($version); + TaskFactory::endTask(); + $Config = Config::getConfig(); $Config->setMaintenance(false); $Config->setUpgradeKey(''); diff --git a/inc/SP/Controller/MainController.class.php b/inc/SP/Controller/MainController.class.php index 1cea402c..34b10d86 100644 --- a/inc/SP/Controller/MainController.class.php +++ b/inc/SP/Controller/MainController.class.php @@ -26,6 +26,7 @@ namespace SP\Controller; defined('APP_ROOT') || die(); +use SP\Account\AccountUtil; use SP\Config\Config; use SP\Core\Acl; use SP\Core\ActionsInterface; @@ -37,6 +38,7 @@ use SP\Core\Messages\NoticeMessage; use SP\Core\Plugin\PluginUtil; use SP\Core\Session; use SP\Core\SessionUtil; +use SP\Core\Task; use SP\Core\Template; use SP\Core\Upgrade\Account; use SP\Core\Upgrade\Check; @@ -443,6 +445,7 @@ class MainController extends ControllerBase implements ActionsInterface $this->view->assign('type', $type); $this->view->assign('version', $version); $this->view->assign('upgradeVersion', implode('.', Util::getVersion(true))); + $this->view->assign('taskId', Task::genTaskId('masterpass')); if ($version < 1316011001) { $this->view->assign('checkConstraints', Check::checkConstraints()); @@ -459,7 +462,7 @@ class MainController extends ControllerBase implements ActionsInterface } if ($version < 21017022601) { - $this->view->assign('numAccounts', Account::getNumAccounts()); + $this->view->assign('numAccounts', AccountUtil::getTotalNumAccounts()); } $this->view(); diff --git a/inc/SP/Controller/TaskController.class.php b/inc/SP/Controller/TaskController.class.php index 0cff8032..845381b5 100644 --- a/inc/SP/Controller/TaskController.class.php +++ b/inc/SP/Controller/TaskController.class.php @@ -25,9 +25,9 @@ namespace SP\Controller; use SP\Core\Messages\TaskMessage; -use SP\Core\Session; use SP\Core\Task; use SP\Http\Request; +use SP\Util\Util; /** * Class TaskController @@ -52,6 +52,27 @@ class TaskController * @var string Archivo de bloqueo */ protected $lockFile; + /** + * @var string Directorio de las tareas + */ + protected $dir; + /** + * @var string ID de la tarea + */ + protected $taskId; + /** + * @var string Archivo de la tarea + */ + protected $taskFile; + + /** + * TaskController constructor. + */ + public function __construct() + { + $this->dir = Util::getTempDir(); + $this->taskId = Request::analyze('taskId'); + } /** * Realizar acción @@ -60,93 +81,94 @@ class TaskController */ public function doAction() { + session_write_close(); + $source = Request::analyze('source'); - if ($this->checkWait($source)) { + if ($this->dir === false || !$this->getLock($source)) { return false; } + $this->taskFile = $this->dir . DIRECTORY_SEPARATOR . $this->taskId . '.task'; + $count = 0; - do { - session_write_close(); - + while (!$this->checkTaskRegistered() || !$this->checkTaskFile()) { if ($count >= $this->startupWaitCount) { debugLog('Aborting ...'); - $this->removeLock(); - - return false; + die(1); } - debugLog('Waiting task ...'); + debugLog('Waiting for task ...'); - sleep($this->startupWaitTime); - - session_start(); - - $this->Task = Session::getTask(); $count++; - } while (!is_object($this->Task)); - - session_write_close(); - - if ($this->checkFile()) { - $this->readTaskStatus(); + sleep($this->startupWaitTime); } - $this->removeLock(); + $this->readTaskStatus(); - return true; + die(0); } /** - * Comprobar si hay una tarea a la espera + * Comprueba si una tarea ha sido registrada en la sesión * - * @param $source * @return bool */ - protected function checkWait($source) + protected function checkTaskRegistered() { - $this->lockFile = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $source . '.lock'; + if (is_object($this->Task)) { + debugLog('Task detected: ' . $this->Task->getTaskId()); - if (file_exists($this->lockFile) || !touch($this->lockFile)) { return true; } + if (file_exists($this->taskFile)) { + $task = file_get_contents($this->taskFile); + + if ($task !== false) { + $this->Task = unserialize($task); + } + + return is_object($this->Task); + } + return false; } /** * Comprobar si el archivo de salida de la tarea existe */ - protected function checkFile() + protected function checkTaskFile() { - return file_exists($this->Task->getFile()); + return file_exists($this->Task->getFileOut()); } /** - * Leer el estdo de una tarea y enviarlo + * Leer el estado de una tarea y enviarlo */ protected function readTaskStatus() { + debugLog('Tracking task: ' . $this->Task->getTaskId()); + $id = 0; $failCount = 0; - $file = $this->Task->getFile(); + $file = $this->Task->getFileOut(); $interval = $this->Task->getInterval(); $Message = new TaskMessage(); $Message->setTask($this->Task->getTaskId()); $Message->setMessage(__('Esperando actualización de progreso ...')); - while ($failCount <= 10) { + while ($failCount <= 30 && file_exists($this->taskFile)) { $content = file_get_contents($file); - if ($content !== false) { + if (!empty($content)) { $this->sendMessage($id, $content); $id++; } else { - debugLog($Message->composeText()); + debugLog($Message->composeJson()); $this->sendMessage($id, $Message->composeJson()); $failCount++; @@ -170,6 +192,33 @@ class TaskController flush(); } + /** + * Comprobar si hay una tarea a la espera + * + * @param $source + * @return bool + */ + protected function checkWait($source) + { + $this->lockFile = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $source . '.lock'; + + if (file_exists($this->lockFile)) { + $timeout = $this->startupWaitCount * $this->startupWaitTime; + + if (filemtime($this->lockFile) + $timeout < time()) { + unlink($this->lockFile); + + return false; + } + + return true; + } + + touch($this->lockFile); + + return false; + } + /** * Eliminar bloqueo */ @@ -179,4 +228,42 @@ class TaskController unlink($this->lockFile); } + + /** + * Obtener un bloqueo para la ejecución de la tarea + * + * @param $source + * + * @return bool + */ + private function getLock($source) + { + if ($source === '') { + $source = 'task'; + } + + $this->lockFile = $this->dir . DIRECTORY_SEPARATOR . $source . '.lock'; + + if (file_exists($this->lockFile)) { + $timeout = $this->startupWaitCount * $this->startupWaitTime; + + if (filemtime($this->lockFile) + $timeout < time()) { + unlink($this->lockFile); + + return $this->updateLock(); + } + + return false; + } else { + return $this->updateLock(); + } + } + + /** + * Actualizar el tiempo del archivo de bloqueo + */ + protected function updateLock() + { + return file_put_contents($this->lockFile, time()) !== false; + } } \ No newline at end of file diff --git a/inc/SP/Core/Init.class.php b/inc/SP/Core/Init.class.php index 19a8009f..b44eb928 100644 --- a/inc/SP/Core/Init.class.php +++ b/inc/SP/Core/Init.class.php @@ -563,7 +563,7 @@ class Init if ($check === true || Checks::isAjax() - || Request::analyze('a') === 'upgrade' + || (Request::analyze('a') === 'upgrade' && Request::analyze('type') !== '') || Request::analyze('nodbupgrade', 0) === 1 || (self::$LOCK > 0 && self::isLoggedIn() && self::$LOCK === Session::getUserData()->getUserId()) ) { diff --git a/inc/SP/Core/Messages/TaskMessage.class.php b/inc/SP/Core/Messages/TaskMessage.class.php index 89479de4..844d06f2 100644 --- a/inc/SP/Core/Messages/TaskMessage.class.php +++ b/inc/SP/Core/Messages/TaskMessage.class.php @@ -33,6 +33,10 @@ use JsonSerializable; */ class TaskMessage implements MessageInterface, JsonSerializable { + /** + * @var string + */ + protected $taskId; /** * @var string */ @@ -142,6 +146,7 @@ class TaskMessage implements MessageInterface, JsonSerializable public function composeText() { return implode(PHP_EOL, [ + 'taskId' => $this->taskId, 'task' => $this->task, 'message' => $this->message, 'time' => $this->time, @@ -158,6 +163,7 @@ class TaskMessage implements MessageInterface, JsonSerializable public function composeHtml() { return [ + 'taskId' => $this->taskId, 'task' => $this->task, 'message' => $this->message, 'time' => $this->time, @@ -186,4 +192,20 @@ class TaskMessage implements MessageInterface, JsonSerializable { return get_object_vars($this); } + + /** + * @return string + */ + public function getTaskId() + { + return $this->taskId; + } + + /** + * @param string $taskId + */ + public function setTaskId($taskId) + { + $this->taskId = $taskId; + } } \ No newline at end of file diff --git a/inc/SP/Core/Session.class.php b/inc/SP/Core/Session.class.php index 1ec01523..b46d2bc4 100644 --- a/inc/SP/Core/Session.class.php +++ b/inc/SP/Core/Session.class.php @@ -51,7 +51,7 @@ class Session * * @param UserData $UserData */ - public static function setUserData($UserData = null) + public static function setUserData(UserData $UserData = null) { self::setSessionKey('userData', $UserData); } @@ -681,7 +681,7 @@ class Session * * @param Task $task */ - public static function setTask(Task $task) + public static function setTask(Task $task = null) { self::setSessionKey('task', $task); } diff --git a/inc/SP/Core/Task.class.php b/inc/SP/Core/Task.class.php index 5256c5a5..45d5082a 100644 --- a/inc/SP/Core/Task.class.php +++ b/inc/SP/Core/Task.class.php @@ -25,6 +25,7 @@ namespace SP\Core; use SP\Core\Messages\TaskMessage; +use SP\Util\Util; /** * Class Task @@ -41,10 +42,14 @@ class Task * @var string ID de la tarea */ protected $taskId; + /** + * @var string Ruta y archivo salida de la tarea + */ + protected $fileOut; /** * @var string Ruta y archivo de la tarea */ - protected $file; + protected $fileTask; /** * @var resource Manejador del archivo */ @@ -62,11 +67,12 @@ class Task * Task constructor. * * @param string $name Nombre de la tarea + * @param string $id */ - public function __construct($name) + public function __construct($name, $id) { $this->name = $name; - $this->taskId = uniqid($this->name, true); + $this->taskId = $id; $this->initialized = $this->checkFile(); } @@ -77,29 +83,41 @@ class Task */ protected function checkFile() { - $fileName = $this->taskId . '.out'; - $fileTmp = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $fileName; + $tempDir = Util::getTempDir(); - if (touch($fileTmp)) { - $this->file = $fileTmp; - } else { - $fileTmpAlt = Init::$SERVERROOT . DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR . $fileName; - $dirAlt = dirname($fileTmpAlt); + if ($tempDir !== false) { + $this->fileOut = $tempDir . DIRECTORY_SEPARATOR . $this->taskId . '.out'; + $this->fileTask = $tempDir . DIRECTORY_SEPARATOR . $this->taskId . '.task'; - if (!file_exists($dirAlt) && !mkdir($dirAlt)) { - return false; - } + $this->deleteTaskFiles(); - if (!touch($fileTmpAlt)) { - return false; - } - - $this->file = $fileTmpAlt; + return true; } - debugLog('Start Task: ' . $this->name); + return false; + } - return true; + /** + * Eliminar los archivos de la tarea no usados + */ + protected function deleteTaskFiles() + { + $filesOut = dirname($this->fileOut) . DIRECTORY_SEPARATOR . $this->taskId . '*.out'; + $filesTask = dirname($this->fileOut) . DIRECTORY_SEPARATOR . $this->taskId . '*.task'; + + array_map('unlink', glob($filesOut)); + array_map('unlink', glob($filesTask)); + } + + /** + * Generar un ID de tarea + * + * @param $name + * @return string + */ + public static function genTaskId($name) + { + return uniqid($name, true); } /** @@ -120,7 +138,7 @@ class Task protected function openFile() { if ($this->initialized === false - || !$this->fileHandler = fopen($this->file, 'wb') + || !$this->fileHandler = fopen($this->fileOut, 'wb') ) { return false; } @@ -157,7 +175,7 @@ class Task { return $this->initialized === true && !is_resource($this->fileHandler) - && file_put_contents($this->file, $Message->composeText()) !== false; + && file_put_contents($this->fileOut, $Message->composeText()) !== false; } /** @@ -170,19 +188,34 @@ class Task { return $this->initialized === true && !is_resource($this->fileHandler) - && file_put_contents($this->file, $Message->composeJson()) !== false; + && file_put_contents($this->fileOut, $Message->composeJson()) !== false; } /** * Iniciar la tarea * + * @param bool $startSession * @return bool */ - public function end() + public function end($startSession = true) { - debugLog('End Task: ' . $this->name); + if ($startSession) { + session_start(); + } - return $this->closeFile() && @unlink($this->file); + $this->deregister(); + + return $this->closeFile() && @unlink($this->fileOut); + } + + /** + * Desregistrar la tarea en la sesión + */ + public function deregister() + { + debugLog('Deregister Task: ' . $this->name); + + unlink($this->fileTask); } /** @@ -226,8 +259,34 @@ class Task /** * @return string */ - public function getFile() + public function getFileOut() { - return $this->file; + return $this->fileOut; + } + + /** + * Registrar la tarea en la sesión. + * + * Es necesario bloquear la sesión para permitir la ejecución de otros scripts + * + * @param bool $lockSession Bloquear la sesión + */ + public function register($lockSession = true) + { + debugLog('Register Task: ' . $this->name); + + file_put_contents($this->fileTask, serialize($this)); + + if ($lockSession === true) { + session_write_close(); + } + } + + /** + * @return string + */ + public function getFileTask() + { + return $this->fileTask; } } \ No newline at end of file diff --git a/inc/SP/Core/TaskFactory.class.php b/inc/SP/Core/TaskFactory.class.php new file mode 100644 index 00000000..106efb39 --- /dev/null +++ b/inc/SP/Core/TaskFactory.class.php @@ -0,0 +1,82 @@ +. + */ + +namespace SP\Core; + +use SP\Core\Messages\TaskMessage; + +/** + * Class TaskFactory + * + * @package SP\Core + */ +class TaskFactory +{ + /** + * @var TaskMessage + */ + public static $Message; + /** + * @var Task + */ + private static $Task; + + /** + * Crear una tarea para la actualización de estado de la actualización + * + * @param $name + * @param $id + */ + public static function createTask($name, $id) + { + if (self::$Task === null) { + self::$Task = new Task($name, $id); + self::$Task->register(); + } + + self::$Message = new TaskMessage(); + self::$Message->setTaskId($id); + } + + /** + * Finalizar la tarea + */ + public static function endTask() + { + if (self::$Task !== null) { + self::$Task->end(); + self::$Task = null; + } + } + + /** + * Enviar un mensaje de actualización a la tarea + */ + public static function sendTaskMessage() + { + if (self::$Task !== null) { + self::$Task->writeJsonStatusAndFlush(self::$Message); + } + } +} \ No newline at end of file diff --git a/inc/SP/Core/Upgrade/Account.class.php b/inc/SP/Core/Upgrade/Account.class.php index 7ea0329d..51acff15 100644 --- a/inc/SP/Core/Upgrade/Account.class.php +++ b/inc/SP/Core/Upgrade/Account.class.php @@ -25,6 +25,7 @@ namespace SP\Core\Upgrade; use SP\Core\Exceptions\SPException; +use SP\Core\TaskFactory; use SP\Storage\DB; use SP\Storage\QueryData; @@ -42,6 +43,10 @@ class Account */ public static function fixAccountsId() { + TaskFactory::$Message->setTask(__FUNCTION__); + TaskFactory::$Message->setMessage(__('Actualizando IDs de cuentas')); + TaskFactory::sendTaskMessage(); + try { DB::beginTransaction(); @@ -61,20 +66,4 @@ class Account return false; } } - - /** - * Devolver el número de cuentas a procesar - * - * @return int - */ - public static function getNumAccounts() - { - $query = /** @lang SQL */ - 'SELECT SUM(n) AS num FROM (SELECT COUNT(*) AS n FROM accounts UNION SELECT COUNT(*) AS n FROM accHistory) a'; - - $Data = new QueryData(); - $Data->setQuery($query); - - return (int)DB::getResults($Data)->num; - } } \ No newline at end of file diff --git a/inc/SP/Core/Upgrade/Category.class.php b/inc/SP/Core/Upgrade/Category.class.php index cc312452..bedc5c3a 100644 --- a/inc/SP/Core/Upgrade/Category.class.php +++ b/inc/SP/Core/Upgrade/Category.class.php @@ -25,6 +25,7 @@ namespace SP\Core\Upgrade; use SP\Core\Exceptions\SPException; +use SP\Core\TaskFactory; use SP\Storage\DB; use SP\Storage\QueryData; @@ -43,6 +44,10 @@ class Category */ public static function fixCategoriesId($categoryId) { + TaskFactory::$Message->setTask(__FUNCTION__); + TaskFactory::$Message->setMessage(__('Actualizando IDs de categorías')); + TaskFactory::sendTaskMessage(); + try { DB::beginTransaction(); diff --git a/inc/SP/Core/Upgrade/Crypt.class.php b/inc/SP/Core/Upgrade/Crypt.class.php index dcddfb10..b1bd2dc8 100644 --- a/inc/SP/Core/Upgrade/Crypt.class.php +++ b/inc/SP/Core/Upgrade/Crypt.class.php @@ -31,9 +31,6 @@ use SP\Config\ConfigDB; use SP\Core\Crypt\Hash; use SP\Core\Exceptions\SPException; use SP\Core\Init; -use SP\Core\Messages\TaskMessage; -use SP\Core\Session; -use SP\Core\Task; use SP\Log\Log; use SP\Mgmt\CustomFields\CustomFieldsUtil; use SP\Mgmt\Users\UserMigrate; @@ -58,12 +55,6 @@ class Crypt global $timeStart; try { - $Task = new Task(__FUNCTION__); - - Session::setTask($Task); - - session_write_close(); - AccountHistoryCrypt::$currentMPassHash = ConfigDB::getValue('masterPwd'); if (!DB::beginTransaction()) { @@ -82,20 +73,12 @@ class Crypt debugLog('Total time: ' . (Init::microtime_float() - $timeStart)); - $Task->end(); - - session_start(); - return true; } catch (\Exception $e) { if (DB::rollbackTransaction()) { debugLog('Rollback: ' . __METHOD__); } - $Task->end(); - - session_start(); - throw $e; } } @@ -126,8 +109,8 @@ class Crypt // Hash de clave maestra anterior a 2.0.0.17013101 // Hash de clave maestra anterior a 2.0.0.17021601 - } elseif (hash_equals(crypt($masterPass, substr($configHashMPass, 0, 72)), substr($configHashMPass, 72)) - || hash_equals(crypt($masterPass, substr($configHashMPass, 0, 30)), substr($configHashMPass, 30)) + } elseif ((substr($configHashMPass, 72) !== false && hash_equals(crypt($masterPass, substr($configHashMPass, 0, 72)), substr($configHashMPass, 72))) + || (substr($configHashMPass, 30) !== false && hash_equals(crypt($masterPass, substr($configHashMPass, 0, 30)), substr($configHashMPass, 30))) ) { ConfigDB::setValue('masterPwd', Hash::hashKey($masterPass)); Log::writeNewLog(__('Aviso', false), __('Se ha regenerado el HASH de clave maestra. No es necesaria ninguna acción.', false), Log::NOTICE); diff --git a/inc/SP/Core/Upgrade/Customer.class.php b/inc/SP/Core/Upgrade/Customer.class.php index 403e4c56..b3bec748 100644 --- a/inc/SP/Core/Upgrade/Customer.class.php +++ b/inc/SP/Core/Upgrade/Customer.class.php @@ -25,6 +25,7 @@ namespace SP\Core\Upgrade; use SP\Core\Exceptions\SPException; +use SP\Core\TaskFactory; use SP\Storage\DB; use SP\Storage\QueryData; @@ -43,6 +44,10 @@ class Customer */ public static function fixCustomerId($customerId) { + TaskFactory::$Message->setTask(__FUNCTION__); + TaskFactory::$Message->setMessage(__('Actualizando IDs de clientes')); + TaskFactory::sendTaskMessage(); + try { DB::beginTransaction(); diff --git a/inc/SP/Core/Upgrade/Group.class.php b/inc/SP/Core/Upgrade/Group.class.php index 9de7b4ea..be0916fb 100644 --- a/inc/SP/Core/Upgrade/Group.class.php +++ b/inc/SP/Core/Upgrade/Group.class.php @@ -25,6 +25,7 @@ namespace SP\Core\Upgrade; use SP\Core\Exceptions\SPException; +use SP\Core\TaskFactory; use SP\Storage\DB; use SP\Storage\QueryData; @@ -46,6 +47,10 @@ class Group */ public static function fixGroupId($groupId) { + TaskFactory::$Message->setTask(__FUNCTION__); + TaskFactory::$Message->setMessage(__('Actualizando IDs de grupos')); + TaskFactory::sendTaskMessage(); + try { DB::beginTransaction(); diff --git a/inc/SP/Core/Upgrade/Profile.class.php b/inc/SP/Core/Upgrade/Profile.class.php index 40d9b62d..ffe8db8a 100644 --- a/inc/SP/Core/Upgrade/Profile.class.php +++ b/inc/SP/Core/Upgrade/Profile.class.php @@ -25,6 +25,7 @@ namespace SP\Core\Upgrade; use SP\Core\Exceptions\SPException; +use SP\Core\TaskFactory; use SP\DataModel\ProfileData; use SP\Storage\DB; use SP\Storage\QueryData; @@ -49,6 +50,10 @@ class Profile $Data = new QueryData(); try { + TaskFactory::$Message->setTask(__FUNCTION__); + TaskFactory::$Message->setMessage(__('Actualizando IDs de perfil')); + TaskFactory::sendTaskMessage(); + DB::beginTransaction(); if ($profileId === 0){ diff --git a/inc/SP/Core/Upgrade/Upgrade.class.php b/inc/SP/Core/Upgrade/Upgrade.class.php index f9f8f773..d689c398 100644 --- a/inc/SP/Core/Upgrade/Upgrade.class.php +++ b/inc/SP/Core/Upgrade/Upgrade.class.php @@ -32,6 +32,7 @@ use SP\Controller\MainActionController; use SP\Core\Exceptions\SPException; use SP\Core\Init; use SP\Core\Session as CoreSession; +use SP\Core\TaskFactory; use SP\Http\Request; use SP\Log\Email; use SP\Log\Log; @@ -109,10 +110,18 @@ class Upgrade */ private static function auxPreDbUpgrade($version) { + if ((int)ConfigDB::getValue('version') >= $version) { + return true; + } + switch ($version) { case 1316011001: + debugLog(__FUNCTION__ . ': ' . $version); + return self::upgradeDB(1300000000); case 1316100601: + debugLog(__FUNCTION__ . ': ' . $version); + return Account::fixAccountsId() && UserUpgrade::fixUsersId(Request::analyze('userid', 0)) @@ -146,6 +155,12 @@ class Upgrade return true; } + TaskFactory::$Message->setTask(__('Actualizar BBDD')); + TaskFactory::$Message->setMessage(sprintf('%s : %s', __('Versión'), $version)); + TaskFactory::sendTaskMessage(); + + debugLog(__FUNCTION__ . ': ' . $version); + $Data = new QueryData(); foreach ($queries as $query) { @@ -221,10 +236,17 @@ class Upgrade $masterPass = Request::analyzeEncrypted('masterkey'); $UserData = User::getItem()->getByLogin(Request::analyze('userlogin')); + if (!is_object($UserData)) { + throw new SPException(SPException::SP_ERROR, __('Error al obtener los datos del usuario', false)); + } + + @session_start(); + CoreSession::setUserData($UserData); + @session_write_close(); + return $dbResult === true - && is_object($UserData) && !empty($masterPass) && Crypt::migrate($masterPass); } @@ -243,12 +265,20 @@ class Upgrade try { switch ($version) { case 12001: + debugLog(__FUNCTION__ . ': ' . $version); + return (ProfileUtil::migrateProfiles() && UserMigrate::migrateUsersGroup()); case 12002: + debugLog(__FUNCTION__ . ': ' . $version); + return UserMigrate::setMigrateUsers(); case 20017010901: + debugLog(__FUNCTION__ . ': ' . $version); + return CustomFieldsUtil::migrateCustomFields() && UserPreferencesUtil::migrate(); case 20017011202: + debugLog(__FUNCTION__ . ': ' . $version); + return UserPreferencesUtil::migrate(); } } catch (SPException $e) { @@ -284,6 +314,8 @@ class Upgrade if (version_compare($version, $upgradeVersion) < 0) { switch ($upgradeVersion) { case 20017011202: + debugLog(__FUNCTION__ . ': ' . $version); + $Config->setSiteTheme('material-blue'); $Config->setConfigVersion($upgradeVersion); Config::saveConfig($Config, false); diff --git a/inc/SP/Core/Upgrade/User.class.php b/inc/SP/Core/Upgrade/User.class.php index 0a492614..2685ddd3 100644 --- a/inc/SP/Core/Upgrade/User.class.php +++ b/inc/SP/Core/Upgrade/User.class.php @@ -27,6 +27,7 @@ namespace SP\Core\Upgrade; use Defuse\Crypto\Exception\CryptoException; use SP\Core\Exceptions\SPException; use SP\Core\OldCrypt; +use SP\Core\TaskFactory; use SP\DataModel\UserLoginData; use SP\Mgmt\Users\UserPass; use SP\Storage\DB; @@ -47,6 +48,10 @@ class User */ public static function fixUsersId($userId) { + TaskFactory::$Message->setTask(__FUNCTION__); + TaskFactory::$Message->setMessage(__('Actualizando IDs de usuarios')); + TaskFactory::sendTaskMessage(); + try { DB::beginTransaction(); diff --git a/inc/SP/Util/Util.class.php b/inc/SP/Util/Util.class.php index be1b748f..6c290d0e 100644 --- a/inc/SP/Util/Util.class.php +++ b/inc/SP/Util/Util.class.php @@ -43,7 +43,7 @@ class Util /** * Generar una clave aleatoria * - * @param int $length Longitud de la clave + * @param int $length Longitud de la clave * @param bool $useNumbers Usar números * @param bool $useSpecial Usar carácteres especiales * @param bool $checKStrength @@ -246,10 +246,10 @@ class Util /** * Obtener datos desde una URL usando CURL * - * @param string $url - * @param array $data + * @param string $url + * @param array $data * @param bool|null $useCookie - * @param bool $weak + * @param bool $weak * @return bool|string * @throws SPException */ @@ -453,8 +453,8 @@ class Util * such as 'false','N','yes','on','off', etc. * * @author Samuel Levy - * @param mixed $in The variable to check - * @param bool $strict If set to false, consider everything that is not false to + * @param mixed $in The variable to check + * @param bool $strict If set to false, consider everything that is not false to * be true. * @return bool The boolean equivalent or null (if strict, and no exact equivalent) */ @@ -528,7 +528,7 @@ class Util /** * Cast an object to another class, keeping the properties, but changing the methods * - * @param string $class Class name + * @param string $class Class name * @param string|object $object * @return mixed * @link http://blog.jasny.net/articles/a-dark-corner-of-php-class-casting/ @@ -584,9 +584,9 @@ class Util /** * Comprobar si un valor existe en un array de objetos * - * @param array $objectArray + * @param array $objectArray * @param string $method - * @param mixed $value + * @param mixed $value * @return bool */ public static function checkInObjectArray(array $objectArray, $method, $value) @@ -660,4 +660,30 @@ class Util return [0, 0]; } + + /** + * Comprueba y devuelve un directorio temporal válido + * + * @return bool|string + */ + public static function getTempDir() + { + $sysTmp = sys_get_temp_dir(); + $appTmp = Init::$SERVERROOT . DIRECTORY_SEPARATOR . 'tmp'; + $file = 'syspass.test'; + + if (file_exists($appTmp . DIRECTORY_SEPARATOR . $file)) { + return $appTmp; + } elseif (file_exists($sysTmp . DIRECTORY_SEPARATOR . $file)) { + return $sysTmp; + } + + if (is_dir($appTmp) || @mkdir($appTmp)) { + if (touch($appTmp . DIRECTORY_SEPARATOR . $file)) { + return $appTmp; + } + } + + return touch($sysTmp . DIRECTORY_SEPARATOR . $file) ? $sysTmp : false; + } } \ No newline at end of file diff --git a/inc/locales/en_US/LC_MESSAGES/messages.mo b/inc/locales/en_US/LC_MESSAGES/messages.mo index 8944cff341707f69c5c9b0876f0da730693c499e..8aaf8d2e0d49698f67e9671849ab4c7e7d0b4348 100644 GIT binary patch delta 25790 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 delta 25536 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 P=\n" "Language-Team: nuxsmin@syspass.org\n" "Language: en_US\n" @@ -69,7 +69,7 @@ msgstr "Invalid file" #: ../../../../ajax/ajax_filesMgmt.php:104 #: ../../../../ajax/ajax_filesMgmt.php:166 #: ../../../../inc/SP/Controller/ItemActionController.class.php:841 -#: ../../../../inc/SP/Core/Upgrade/Upgrade.class.php:352 +#: ../../../../inc/SP/Core/Upgrade/Upgrade.class.php:372 #: ../../../../inc/SP/Mgmt/Files/File.class.php:95 #: ../../../../inc/themes/material-blue/views/config/import.inc:66 #: ../../../../inc/themes/material-blue/views/config/import.inc:69 @@ -155,7 +155,7 @@ msgstr "Account" #: ../../../../inc/SP/Controller/LoginController.class.php:512 #: ../../../../inc/SP/Controller/LoginController.class.php:530 #: ../../../../inc/SP/Controller/LoginController.class.php:538 -#: ../../../../inc/SP/Controller/MainActionController.class.php:118 +#: ../../../../inc/SP/Controller/MainActionController.class.php:116 #: ../../../../inc/SP/Core/Init.class.php:445 #: ../../../../inc/SP/Mgmt/Files/File.class.php:96 #: ../../../../inc/SP/Mgmt/PublicLinks/PublicLink.class.php:75 @@ -278,10 +278,10 @@ msgstr "Error while creating the account" #: ../../../../inc/SP/Account/Account.class.php:321 #: ../../../../inc/SP/Account/Account.class.php:324 -#: ../../../../inc/SP/Account/AccountCrypt.class.php:127 -#: ../../../../inc/SP/Account/AccountCrypt.class.php:237 -#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:137 -#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:256 +#: ../../../../inc/SP/Account/AccountCrypt.class.php:138 +#: ../../../../inc/SP/Account/AccountCrypt.class.php:258 +#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:149 +#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:278 #: ../../../../inc/SP/Api/ApiBase.class.php:200 #: ../../../../inc/SP/Api/ApiBase.class.php:203 #: ../../../../inc/SP/Api/ApiRequest.class.php:147 @@ -321,54 +321,54 @@ msgstr "Error while deleting the account" msgid "Error al actualizar la clave" msgstr "Error while updating the password" -#: ../../../../inc/SP/Account/AccountCrypt.class.php:66 -#: ../../../../inc/SP/Account/AccountCrypt.class.php:102 -#: ../../../../inc/SP/Account/AccountCrypt.class.php:188 -#: ../../../../inc/SP/Account/AccountCrypt.class.php:216 +#: ../../../../inc/SP/Account/AccountCrypt.class.php:68 +#: ../../../../inc/SP/Account/AccountCrypt.class.php:94 +#: ../../../../inc/SP/Account/AccountCrypt.class.php:199 +#: ../../../../inc/SP/Account/AccountCrypt.class.php:218 #: ../../../../inc/SP/Controller/ConfigActionController.class.php:558 #: ../../../../inc/SP/Controller/ConfigActionController.class.php:586 msgid "Actualizar Clave Maestra" msgstr "Update Master Password" -#: ../../../../inc/SP/Account/AccountCrypt.class.php:67 -#: ../../../../inc/SP/Account/AccountCrypt.class.php:189 -#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:70 -#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:198 +#: ../../../../inc/SP/Account/AccountCrypt.class.php:69 +#: ../../../../inc/SP/Account/AccountCrypt.class.php:200 +#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:72 +#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:210 msgid "Inicio" msgstr "Start" -#: ../../../../inc/SP/Account/AccountCrypt.class.php:71 -#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:74 +#: ../../../../inc/SP/Account/AccountCrypt.class.php:73 +#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:76 msgid "Error en el módulo de encriptación" msgstr "Error on the encryption module" -#: ../../../../inc/SP/Account/AccountCrypt.class.php:81 -#: ../../../../inc/SP/Account/AccountCrypt.class.php:196 +#: ../../../../inc/SP/Account/AccountCrypt.class.php:83 +#: ../../../../inc/SP/Account/AccountCrypt.class.php:207 msgid "Error al obtener las claves de las cuentas" msgstr "Error while retrieving the accounts' passwords" -#: ../../../../inc/SP/Account/AccountCrypt.class.php:103 -#: ../../../../inc/SP/Account/AccountCrypt.class.php:144 -#: ../../../../inc/SP/Account/AccountCrypt.class.php:217 -#: ../../../../inc/SP/Account/AccountCrypt.class.php:254 -#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:110 -#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:154 -#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:231 -#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:273 +#: ../../../../inc/SP/Account/AccountCrypt.class.php:113 +#: ../../../../inc/SP/Account/AccountCrypt.class.php:155 +#: ../../../../inc/SP/Account/AccountCrypt.class.php:237 +#: ../../../../inc/SP/Account/AccountCrypt.class.php:275 +#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:121 +#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:166 +#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:252 +#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:295 msgid "Cuentas actualizadas" msgstr "Accounts updated" -#: ../../../../inc/SP/Account/AccountCrypt.class.php:137 -#: ../../../../inc/SP/Account/AccountCrypt.class.php:140 -#: ../../../../inc/SP/Account/AccountCrypt.class.php:247 -#: ../../../../inc/SP/Account/AccountCrypt.class.php:250 +#: ../../../../inc/SP/Account/AccountCrypt.class.php:148 +#: ../../../../inc/SP/Account/AccountCrypt.class.php:151 +#: ../../../../inc/SP/Account/AccountCrypt.class.php:268 +#: ../../../../inc/SP/Account/AccountCrypt.class.php:271 msgid "Fallo al actualizar la clave de la cuenta" msgstr "Error while updating the account's password" -#: ../../../../inc/SP/Account/AccountCrypt.class.php:145 -#: ../../../../inc/SP/Account/AccountCrypt.class.php:255 -#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:155 -#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:274 +#: ../../../../inc/SP/Account/AccountCrypt.class.php:156 +#: ../../../../inc/SP/Account/AccountCrypt.class.php:276 +#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:167 +#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:296 #: ../../../../inc/SP/Controller/ItemActionController.class.php:1077 msgid "Errores" msgstr "Errors" @@ -385,29 +385,29 @@ msgstr "Error while deleting favorite" msgid "Error al actualizar el historial" msgstr "Error while updating history" -#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:69 -#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:109 -#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:197 -#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:230 +#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:71 +#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:102 +#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:209 +#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:233 msgid "Actualizar Clave Maestra (H)" msgstr "Update Master Password (H)" -#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:84 -#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:205 +#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:86 +#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:217 #: ../../../../inc/themes/material-blue/views/accountsearch/index.inc:11 #: ../../../../inc/themes/material-blue/views/eventlog/eventlog.inc:11 msgid "No se encontraron registros" msgstr "No records found" -#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:124 -#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:243 +#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:136 +#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:265 msgid "La clave maestra del registro no coincide" msgstr "The record's master password does not match" -#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:147 -#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:150 -#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:266 -#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:269 +#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:159 +#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:162 +#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:288 +#: ../../../../inc/SP/Account/AccountHistoryCrypt.class.php:291 msgid "Fallo al actualizar la clave del histórico" msgstr "Error on updating the history's master password" @@ -969,8 +969,8 @@ msgid "Modificar configuración" msgstr "Update Configuration" #: ../../../../inc/SP/Config/ConfigDB.class.php:149 -#: ../../../../inc/SP/Core/Upgrade/Upgrade.class.php:325 -#: ../../../../inc/SP/Core/Upgrade/Upgrade.class.php:331 +#: ../../../../inc/SP/Core/Upgrade/Upgrade.class.php:345 +#: ../../../../inc/SP/Core/Upgrade/Upgrade.class.php:351 msgid "Parámetro" msgstr "Parameter" @@ -1115,10 +1115,10 @@ msgid "Conexión correcta" msgstr "Connection successful" #: ../../../../inc/SP/Controller/ChecksController.class.php:138 -#: ../../../../inc/SP/Controller/MainActionController.class.php:117 +#: ../../../../inc/SP/Controller/MainActionController.class.php:115 #: ../../../../inc/SP/Core/Init.class.php:444 -#: ../../../../inc/SP/Core/Upgrade/Upgrade.class.php:139 -#: ../../../../inc/SP/Core/Upgrade/Upgrade.class.php:345 +#: ../../../../inc/SP/Core/Upgrade/Upgrade.class.php:147 +#: ../../../../inc/SP/Core/Upgrade/Upgrade.class.php:365 #: ../../../../inc/themes/material-blue/views/config/info.inc:40 #: ../../../../inc/themes/material-blue/views/itemshow/plugins.inc:25 msgid "Versión" @@ -1307,7 +1307,7 @@ msgid "La clave maestra actual no coincide" msgstr "The current master password does not match" #: ../../../../inc/SP/Controller/ConfigActionController.class.php:518 -#: ../../../../inc/SP/Core/Upgrade/Crypt.class.php:59 +#: ../../../../inc/SP/Core/Upgrade/Crypt.class.php:70 #: ../../../../inc/SP/Import/Import.class.php:89 msgid "No es posible iniciar una transacción" msgstr "Unable to start a transaction" @@ -1325,7 +1325,7 @@ msgid "Errores al actualizar datos de campos personalizados" msgstr "Error while updating the custom fields data" #: ../../../../inc/SP/Controller/ConfigActionController.class.php:548 -#: ../../../../inc/SP/Core/Upgrade/Crypt.class.php:68 +#: ../../../../inc/SP/Core/Upgrade/Crypt.class.php:80 #: ../../../../inc/SP/Import/Import.class.php:95 msgid "No es posible finalizar una transacción" msgstr "Unable to finish a transaction" @@ -2377,7 +2377,7 @@ msgstr "Using temporary password" #: ../../../../inc/SP/Controller/LoginController.class.php:319 #: ../../../../inc/SP/Controller/LoginController.class.php:338 #: ../../../../inc/SP/Controller/LoginController.class.php:340 -#: ../../../../inc/SP/Core/Upgrade/Crypt.class.php:61 +#: ../../../../inc/SP/Core/Upgrade/Crypt.class.php:72 msgid "Clave maestra incorrecta" msgstr "Wrong Master Password" @@ -2410,7 +2410,7 @@ msgid "Autentificación" msgstr "Authentication" #: ../../../../inc/SP/Controller/MainActionController.class.php:60 -#: ../../../../inc/SP/Controller/MainActionController.class.php:115 +#: ../../../../inc/SP/Controller/MainActionController.class.php:113 #: ../../../../inc/SP/Core/Init.class.php:442 msgid "Actualización" msgstr "Update" @@ -2432,7 +2432,7 @@ msgstr "Application successfully updated" msgid "En 5 segundos será redirigido al login" msgstr "You will be redirected to log in within 5 seconds" -#: ../../../../inc/SP/Controller/MainActionController.class.php:116 +#: ../../../../inc/SP/Controller/MainActionController.class.php:114 #: ../../../../inc/SP/Core/Init.class.php:443 ../../../../res/upgrade.php:121 msgid "Actualización de versión realizada." msgstr "Version updating done." @@ -2510,6 +2510,10 @@ msgstr "Link viewed" msgid "Agente" msgstr "Agent" +#: ../../../../inc/SP/Controller/TaskController.class.php:140 +msgid "Esperando actualización de progreso ..." +msgstr "Waiting for progress updating ..." + #: ../../../../inc/SP/Controller/UserPreferencesController.class.php:90 msgid "Preferencias" msgstr "Preferences" @@ -2939,16 +2943,16 @@ msgstr "Template does not contain files" msgid "Error al crear la categoría" msgstr "Error while creating the category" -#: ../../../../inc/SP/Core/Upgrade/Crypt.class.php:104 -#: ../../../../inc/SP/Core/Upgrade/Crypt.class.php:115 +#: ../../../../inc/SP/Core/Upgrade/Crypt.class.php:122 +#: ../../../../inc/SP/Core/Upgrade/Crypt.class.php:133 #: ../../../../inc/SP/Log/Email.class.php:141 #: ../../../../inc/themes/material-blue/inc/Icons.class.php:70 #: ../../../../js/strings.js.php:85 msgid "Aviso" msgstr "Warning" -#: ../../../../inc/SP/Core/Upgrade/Crypt.class.php:104 -#: ../../../../inc/SP/Core/Upgrade/Crypt.class.php:115 +#: ../../../../inc/SP/Core/Upgrade/Crypt.class.php:122 +#: ../../../../inc/SP/Core/Upgrade/Crypt.class.php:133 msgid "" "Se ha regenerado el HASH de clave maestra. No es necesaria ninguna acción." msgstr "" @@ -2959,12 +2963,12 @@ msgstr "" msgid "Error al crear el cliente" msgstr "Error while creating the customer" -#: ../../../../inc/SP/Core/Upgrade/Group.class.php:87 +#: ../../../../inc/SP/Core/Upgrade/Group.class.php:98 #: ../../../../inc/SP/Mgmt/Groups/Group.class.php:67 msgid "Error al crear el grupo" msgstr "Error while creating the group" -#: ../../../../inc/SP/Core/Upgrade/Profile.class.php:84 +#: ../../../../inc/SP/Core/Upgrade/Profile.class.php:92 #: ../../../../inc/SP/Mgmt/Profiles/Profile.class.php:68 msgid "Error al crear perfil" msgstr "Error while creating the profile" @@ -2982,7 +2986,7 @@ msgid "Compruebe el registro de eventos para más detalles" msgstr "Please, check the event log for more details" #: ../../../../inc/SP/Core/Upgrade/Upgrade.class.php:79 -#: ../../../../inc/SP/Core/Upgrade/Upgrade.class.php:156 +#: ../../../../inc/SP/Core/Upgrade/Upgrade.class.php:166 msgid "Error al aplicar la actualización de la Base de Datos" msgstr "Error while updating the database" @@ -2990,42 +2994,48 @@ msgstr "Error while updating the database" msgid "Error al aplicar la actualización de la aplicación" msgstr "Error while applying the application update" -#: ../../../../inc/SP/Core/Upgrade/Upgrade.class.php:138 +#: ../../../../inc/SP/Core/Upgrade/Upgrade.class.php:146 msgid "Actualizar BBDD" msgstr "Update DB" -#: ../../../../inc/SP/Core/Upgrade/Upgrade.class.php:144 +#: ../../../../inc/SP/Core/Upgrade/Upgrade.class.php:152 msgid "No es necesario actualizar la Base de Datos." msgstr "Database update not needed." -#: ../../../../inc/SP/Core/Upgrade/Upgrade.class.php:168 +#: ../../../../inc/SP/Core/Upgrade/Upgrade.class.php:178 msgid "Actualización de la Base de Datos realizada correctamente." msgstr "Database updating was completed successfully." -#: ../../../../inc/SP/Core/Upgrade/Upgrade.class.php:312 +#: ../../../../inc/SP/Core/Upgrade/Upgrade.class.php:235 +#: ../../../../inc/SP/Mgmt/Users/User.class.php:336 +#: ../../../../inc/SP/Mgmt/Users/User.class.php:393 +msgid "Error al obtener los datos del usuario" +msgstr "Error while retrieving the user's data" + +#: ../../../../inc/SP/Core/Upgrade/Upgrade.class.php:332 msgid "Actualizar Configuración" msgstr "Update Configuration" -#: ../../../../inc/SP/Core/Upgrade/Upgrade.class.php:351 +#: ../../../../inc/SP/Core/Upgrade/Upgrade.class.php:371 msgid "Error al actualizar la configuración" msgstr "Error while updating the configuration" -#: ../../../../inc/SP/Core/Upgrade/Upgrade.class.php:478 +#: ../../../../inc/SP/Core/Upgrade/Upgrade.class.php:498 msgid "La aplicación necesita actualizarse" msgstr "The application needs to be updated" -#: ../../../../inc/SP/Core/Upgrade/Upgrade.class.php:478 +#: ../../../../inc/SP/Core/Upgrade/Upgrade.class.php:498 #, php-format msgid "Si es un administrador pulse en el enlace: %s" msgstr "If you are an administrator, click on the link: %s" -#: ../../../../inc/SP/Core/Upgrade/Upgrade.class.php:478 +#: ../../../../inc/SP/Core/Upgrade/Upgrade.class.php:498 #: ../../../../inc/themes/material-blue/inc/Icons.class.php:57 -#: ../../../../inc/themes/material-blue/views/main/upgrade.inc:162 +#: ../../../../inc/themes/material-blue/views/main/upgrade.inc:167 msgid "Actualizar" msgstr "Update" -#: ../../../../inc/SP/Core/Upgrade/User.class.php:149 +#: ../../../../inc/SP/Core/Upgrade/User.class.php:151 #: ../../../../inc/SP/Mgmt/Users/User.class.php:88 msgid "Error al crear el usuario" msgstr "Error while creating the user" @@ -3671,11 +3681,6 @@ msgstr "Error while retrieving the users" msgid "Error al modificar la clave" msgstr "Error while updating the password" -#: ../../../../inc/SP/Mgmt/Users/User.class.php:336 -#: ../../../../inc/SP/Mgmt/Users/User.class.php:393 -msgid "Error al obtener los datos del usuario" -msgstr "Error while retrieving the user's data" - #: ../../../../inc/SP/Mgmt/Users/UserLdap.class.php:108 #: ../../../../inc/themes/material-blue/inc/Icons.class.php:54 msgid "Usuario de LDAP" @@ -4149,6 +4154,11 @@ msgstr "This action will reset all plugin data. Do you want to continue?" msgid "Este proceso puede durar algo de tiempo. Desea continuar?" msgstr "This process could long some time. Do you wish to continue?" +#: ../../../../js/strings.js.php:87 +msgid "" +"Realizando tarea. Por favor, no cierre la ventana/pestaña del navegador." +msgstr "Performing task. Please, do not close the browser window/tab." + #: ../../../../inc/themes/material-blue/views/account/account-editpass.inc:67 #: ../../../../inc/themes/material-blue/views/account/account.inc:135 #: ../../../../inc/themes/material-blue/views/account/account.inc:142 @@ -5885,21 +5895,21 @@ msgstr "" #, php-format msgid "" "Se van a actualizar %s cuentas. Este proceso puede tardar algo de tiempo." -msgstr "It will be updated %s accounts. This process could take a some time." +msgstr "It will be updated %s accounts. This process could take some time." #: ../../../../inc/themes/material-blue/views/main/upgrade.inc:135 msgid "Introducir login de usuario válido" msgstr "Enter a valid user login" -#: ../../../../inc/themes/material-blue/views/main/upgrade.inc:141 +#: ../../../../inc/themes/material-blue/views/main/upgrade.inc:146 msgid "He realizado una copia de seguridad completa de sysPass" msgstr "I've done a full sysPass backup" -#: ../../../../inc/themes/material-blue/views/main/upgrade.inc:154 +#: ../../../../inc/themes/material-blue/views/main/upgrade.inc:159 msgid "Por favor espere mientras el proceso se ejecuta" msgstr "Please, wait while the process is running" -#: ../../../../inc/themes/material-blue/views/main/upgrade.inc:164 +#: ../../../../inc/themes/material-blue/views/main/upgrade.inc:169 msgid "Iniciar Actualización" msgstr "Start Update" @@ -5950,9 +5960,3 @@ msgstr "Search results of '%s'" #: ../../../../inc/themes/material-blue/views/wiki/wikipage.inc:23 msgid "Página" msgstr "Page" - -#~ msgid "Error al eliminar archivo" -#~ msgstr "Error while deleting the file" - -#~ msgid "Error al aplicar la actualización de la Base de Datos." -#~ msgstr "Error while updating database." diff --git a/inc/themes/material-blue/views/config/encryption.inc b/inc/themes/material-blue/views/config/encryption.inc index 61082366..22202c62 100644 --- a/inc/themes/material-blue/views/config/encryption.inc +++ b/inc/themes/material-blue/views/config/encryption.inc @@ -88,17 +88,20 @@

getIconWarning()->getIcon(); ?>
+ class="icon material-icons getIconWarning()->getClass(); ?>">getIconWarning()->getIcon(); ?>
getIconWarning()->getIcon(); ?>
+ class="icon material-icons getIconWarning()->getClass(); ?>">getIconWarning()->getIcon(); ?>
getIconWarning()->getIcon(); ?>
+ class="icon material-icons getIconWarning()->getClass(); ?>">getIconWarning()->getIcon(); ?>
+
getIconWarning()->getIcon(); ?>
+