diff --git a/app/modules/web/Controllers/ConfigImportController.php b/app/modules/web/Controllers/ConfigImportController.php index 34f4a9f5..c6a6758e 100644 --- a/app/modules/web/Controllers/ConfigImportController.php +++ b/app/modules/web/Controllers/ConfigImportController.php @@ -24,9 +24,10 @@ namespace SP\Modules\Web\Controllers; - use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; +use SP\Core\Events\Event; +use SP\Core\Events\EventMessage; use SP\Http\JsonResponse; use SP\Http\Request; use SP\Modules\Web\Controllers\Traits\JsonTrait; @@ -64,6 +65,11 @@ class ConfigImportController extends SimpleControllerBase $importService = $this->dic->get(ImportService::class); $counter = $importService->doImport($importParams, new FileImport($this->router->request()->files()->get('inFile'))); + $this->eventDispatcher->notifyEvent('run.import', new Event($this, + EventMessage::factory() + ->addDetail(__('Cuentas importadas'), $counter)) + ); + if ($counter > 0) { $this->returnJsonResponse(JsonResponse::JSON_SUCCESS, __u('Importación finalizada'), [__u('Revise el registro de eventos para más detalles')]); } else { @@ -72,6 +78,11 @@ class ConfigImportController extends SimpleControllerBase } catch (\Exception $e) { processException($e); + $this->eventDispatcher->notifyEvent('exception', + new Event($e, EventMessage::factory() + ->addDescription($e->getMessage())) + ); + $this->returnJsonResponseException($e); } } diff --git a/lib/SP/Core/Events/Event.php b/lib/SP/Core/Events/Event.php index 67ed0b01..c053d5c2 100644 --- a/lib/SP/Core/Events/Event.php +++ b/lib/SP/Core/Events/Event.php @@ -24,6 +24,7 @@ namespace SP\Core\Events; +use InvalidArgumentException; /** * Class Event @@ -39,18 +40,23 @@ class Event /** * @var array */ - private $data; + private $eventMessage; /** * Event constructor. * * @param object $source - * @param array $data + * @param EventMessage $eventMessage + * @throws InvalidArgumentException */ - public function __construct($source, array $data = []) + public function __construct($source, EventMessage $eventMessage = null) { + if (!is_object($source)) { + throw new InvalidArgumentException(__u('Es necesario un objeto')); + } + $this->source = $source; - $this->data = $data; + $this->eventMessage = $eventMessage; } /** @@ -64,9 +70,9 @@ class Event /** * @return array */ - public function getData() + public function getEventMessage() { - return $this->data; + return $this->eventMessage; } } \ No newline at end of file diff --git a/lib/SP/Core/Events/EventDispatcherBase.php b/lib/SP/Core/Events/EventDispatcherBase.php index 7ba171e0..14786714 100644 --- a/lib/SP/Core/Events/EventDispatcherBase.php +++ b/lib/SP/Core/Events/EventDispatcherBase.php @@ -24,7 +24,6 @@ namespace SP\Core\Events; -use SP\Core\Exceptions\InvalidArgumentException; use SP\Core\Exceptions\InvalidClassException; use SP\Core\Exceptions\SPException; use SplObserver; @@ -101,15 +100,10 @@ abstract class EventDispatcherBase implements EventDispatcherInterface * Notificar un evento * * @param string $eventType - * @param Event $event - * @throws \SP\Core\Exceptions\InvalidArgumentException + * @param Event $event */ public function notifyEvent($eventType, Event $event) { - if (!is_object($event->getSource())) { - throw new InvalidArgumentException(__u('Es necesario un objeto'), SPException::ERROR); - } - foreach ($this->observers as $observer) { if (in_array($eventType, $observer->getEvents(), true)) { // FIXME: update receivers Event diff --git a/lib/SP/Core/Events/EventMessage.php b/lib/SP/Core/Events/EventMessage.php new file mode 100644 index 00000000..83dc9fd9 --- /dev/null +++ b/lib/SP/Core/Events/EventMessage.php @@ -0,0 +1,248 @@ +getDescription($translate)); + } + + /** + * Devuelve la descripción de la acción realizada + * + * @param bool $translate + * @return string + */ + public function getDescription($translate = false) + { + if (count($this->description) === 0) { + return ''; + } + + if (count($this->description) > 1) { + if ($translate === true) { + return implode(PHP_EOL, array_map('__', $this->description)); + } + + return implode(PHP_EOL, $this->description); + } + + return $translate === true ? __($this->description[0]) : $this->description[0]; + } + + /** + * Añadir detalle en formato HTML. Se resalta el texto clave. + * + * @param $key string + * @param $value string + * @return $this + */ + public function addDetailHtml($key, $value) + { + $this->addDetail(Html::strongText($key), $value); + + return $this; + } + + /** + * Establece los detalles de la acción realizada + * + * @param $key string + * @param $value string + * @return $this + */ + public function addDetail($key, $value) + { + if ($value === '' || $key === '') { + return $this; + } + + $this->details[] = [$this->formatString($key), $this->formatString($value)]; + + $this->detailsCounter++; + + return $this; + } + + /** + * Formatear una cadena para guardarla en el registro + * + * @param $string string La cadena a formatear + * @return string + */ + private function formatString($string) + { + return strip_tags($string); + } + + /** + * Establece la descripción de la acción realizada en formato HTML + * + * @param string $description + * @return $this + */ + public function addDescriptionHtml($description = '') + { + $this->addDescription(Html::strongText($description)); + + return $this; + } + + /** + * Establece la descripción de la acción realizada + * + * @param string $description + * @return $this + */ + public function addDescription($description = '') + { + $this->description[] = $this->formatString($description); + + return $this; + } + + /** + * Añadir una línea en blanco a la descripción + */ + public function addDescriptionLine() + { + $this->descriptionCounter++; + + return $this; + } + + /** + * Componer un mensaje en formato texto + * + * @return string + */ + public function composeText() + { + $message[] = $this->getDescription(true); + $message[] = $this->getDetails(true); + + return implode(PHP_EOL, $message); + } + + /** + * Devuelve los detalles de la acción realizada + * + * @param bool $translate + * @return string + */ + public function getDetails($translate = false) + { + if (count($this->details) === 0) { + return ''; + } + + if (count($this->details) > 1) { + if ($translate === true) { + return implode(PHP_EOL, array_map(function ($detail) use ($translate) { + return $this->formatDetail($detail, $translate); + }, $this->details)); + } + + return implode(PHP_EOL, array_map([$this, 'formatDetail'], $this->details)); + } + + return $this->formatDetail($this->details[0], $translate); + } + + /** + * Devolver un detalle formateado + * + * @param array $detail + * @param bool $translate + * @return string + */ + protected function formatDetail(array $detail, $translate = false) + { + if ($translate === true) { + return sprintf('%s : %s', __($detail[0]), __($detail[1])); + } + + return sprintf('%s : %s', $detail[0], $detail[1]); + } + + /** + * Componer un mensaje en formato HTML + * + * @return mixed + */ + public function composeHtml() + { + $message[] = '
'; + $message[] = '

' . nl2br($this->getDescription(true)) . '

'; + $message[] = '

' . nl2br($this->getDetails(true)) . '

'; + $message[] = '
'; + + return implode('', $message); + } + + /** + * Devuelve los detalles en formato HTML + * + * @param bool $translate + * @return string + */ + public function getHtmlDetails($translate = false) + { + return nl2br($this->getDetails($translate)); + } + + /** + * @return int + */ + public function getDescriptionCounter() + { + return $this->descriptionCounter; + } + + /** + * @return int + */ + public function getDetailsCounter() + { + return $this->detailsCounter; + } +} \ No newline at end of file diff --git a/lib/SP/DataModel/CategoryData.php b/lib/SP/DataModel/CategoryData.php index ade659b3..d8102910 100644 --- a/lib/SP/DataModel/CategoryData.php +++ b/lib/SP/DataModel/CategoryData.php @@ -53,15 +53,15 @@ class CategoryData extends DataModelBase implements DataModelInterface /** * categoryData constructor. * - * @param int $category_id - * @param string $category_name - * @param string $category_description + * @param int $id + * @param string $name + * @param string $description */ - public function __construct($category_id = null, $category_name = null, $category_description = null) + public function __construct($id = null, $name = null, $description = null) { - $this->id = $category_id; - $this->name = $category_name; - $this->description = $category_description; + $this->id = $id; + $this->name = $name; + $this->description = $description; } /** diff --git a/lib/SP/DataModel/ClientData.php b/lib/SP/DataModel/ClientData.php index bff8f8e3..89b1dbf1 100644 --- a/lib/SP/DataModel/ClientData.php +++ b/lib/SP/DataModel/ClientData.php @@ -57,15 +57,15 @@ class ClientData extends DataModelBase implements DataModelInterface /** * CustomerData constructor. * - * @param int $customer_id - * @param string $customer_name - * @param string $customer_description + * @param int $id + * @param string $name + * @param string $description */ - public function __construct($customer_id = null, $customer_name = null, $customer_description = null) + public function __construct($id = null, $name = null, $description = null) { - $this->id = $customer_id; - $this->name = $customer_name; - $this->description = $customer_description; + $this->id = $id; + $this->name = $name; + $this->description = $description; } /** diff --git a/lib/SP/Services/Import/CsvImport.php b/lib/SP/Services/Import/CsvImport.php index 072044c1..48538216 100644 --- a/lib/SP/Services/Import/CsvImport.php +++ b/lib/SP/Services/Import/CsvImport.php @@ -24,6 +24,9 @@ namespace SP\Services\Import; +use SP\Core\Events\Event; +use SP\Core\Events\EventMessage; + defined('APP_ROOT') || die(); /** @@ -40,7 +43,12 @@ class CsvImport extends CsvImportBase implements ImportInterface */ public function doImport() { -// $this->LogMessage->addDescription(sprintf(__('Formato detectado: %s'), 'CSV')); + $this->eventDispatcher->notifyEvent('run.import.csv', + new Event($this, + EventMessage::factory() + ->addDescription(sprintf(__('Formato detectado: %s'), 'CSV'))) + ); + $this->fileImport->readFileToArray(); $this->processAccounts(); diff --git a/lib/SP/Services/Import/CsvImportBase.php b/lib/SP/Services/Import/CsvImportBase.php index 716e59aa..770d3ca6 100644 --- a/lib/SP/Services/Import/CsvImportBase.php +++ b/lib/SP/Services/Import/CsvImportBase.php @@ -26,6 +26,9 @@ namespace SP\Services\Import; use SP\Account\AccountRequest; use SP\Bootstrap; +use SP\Core\Events\Event; +use SP\Core\Events\EventDispatcher; +use SP\Core\Events\EventMessage; use SP\DataModel\CategoryData; use SP\DataModel\ClientData; use SP\Services\Account\AccountService; @@ -56,11 +59,15 @@ abstract class CsvImportBase * @var FileImport */ protected $fileImport; + /** + * @var EventDispatcher + */ + protected $eventDispatcher; /** * ImportBase constructor. * - * @param FileImport $fileImport + * @param FileImport $fileImport * @param ImportParams $importParams * @throws \Psr\Container\ContainerExceptionInterface * @throws \Psr\Container\NotFoundExceptionInterface @@ -75,6 +82,7 @@ abstract class CsvImportBase $this->categoryService = $dic->get(CategoryService::class); $this->clientService = $dic->get(ClientService::class); $this->tagService = $dic->get(TagService::class); + $this->eventDispatcher = $dic->get(EventDispatcher::class); } /** @@ -97,6 +105,7 @@ abstract class CsvImportBase * Obtener los datos de las entradas de sysPass y crearlas * * @throws ImportException + * @throws \SP\Core\Exceptions\InvalidArgumentException */ protected function processAccounts() { @@ -140,9 +149,13 @@ abstract class CsvImportBase $this->addAccount($accountRequest); } catch (\Exception $e) { processException($e); -// $this->LogMessage->addDetails(__('Error importando cuenta', false), $accountName); -// $this->LogMessage->addDetails(__('Error procesando línea', false), $line); -// $this->LogMessage->addDetails(__('Error', false), $e->getMessage()); + + $this->eventDispatcher->notifyEvent('exception', + new Event($e, + EventMessage::factory() + ->addDetail(__u('Error importando cuenta'), $accountName) + ->addDetail(__u('Error procesando línea'), $line)) + ); } } } diff --git a/lib/SP/Services/Import/ImportService.php b/lib/SP/Services/Import/ImportService.php index a956f217..a408601e 100644 --- a/lib/SP/Services/Import/ImportService.php +++ b/lib/SP/Services/Import/ImportService.php @@ -49,7 +49,7 @@ class ImportService extends Service * Iniciar la importación de cuentas. * * @param ImportParams $importParams - * @param FileImport $fileImport + * @param FileImport $fileImport * @return int * @throws \Exception * @throws \Psr\Container\ContainerExceptionInterface @@ -78,17 +78,11 @@ class ImportService extends Service } return $counter; -// $LogMessage->addDetails(__('Cuentas importadas'), $Import->getCounter()); } catch (\Exception $e) { if (DbWrapper::rollbackTransaction($db)) { debugLog('Rollback'); } -// $LogMessage->addDescription($e->getMessage()); -// $LogMessage->addDetails(__('Ayuda', false), $e->getHint()); -// $Log->setLogLevel(Log::ERROR); -// $Log->writeLog(); - throw $e; } diff --git a/lib/SP/Services/Import/KeepassImport.php b/lib/SP/Services/Import/KeepassImport.php index a3c8939c..172bf218 100644 --- a/lib/SP/Services/Import/KeepassImport.php +++ b/lib/SP/Services/Import/KeepassImport.php @@ -27,6 +27,8 @@ namespace SP\Services\Import; use DOMElement; use DOMXPath; use SP\Account\AccountRequest; +use SP\Core\Events\Event; +use SP\Core\Events\EventMessage; use SP\DataModel\CategoryData; use SP\DataModel\ClientData; @@ -59,10 +61,21 @@ class KeepassImport extends XmlImportBase implements ImportInterface { $clientId = $this->addClient(new ClientData(null, 'KeePass')); + $this->eventDispatcher->notifyEvent('run.import.keepass', + new Event($this, + EventMessage::factory() + ->addDetail(__('Cliente creado'), 'KeePass')) + ); + foreach ($this->getItems() as $group => $entry) { try { - $categoryData = new CategoryData(null, $group); - $this->addCategory($categoryData); + $categoryId = $this->addCategory(new CategoryData(null, $group)); + + $this->eventDispatcher->notifyEvent('run.import.keepass', + new Event($this, + EventMessage::factory() + ->addDetail(__('Categoría importada'), $group)) + ); if (count($entry) > 0) { foreach ($entry as $account) { @@ -72,10 +85,16 @@ class KeepassImport extends XmlImportBase implements ImportInterface $accountRequest->name = $account['Title']; $accountRequest->url = $account['URL']; $accountRequest->login = $account['UserName']; - $accountRequest->categoryId = $categoryData->getId(); + $accountRequest->categoryId = $categoryId; $accountRequest->clientId = $clientId; $this->addAccount($accountRequest); + + $this->eventDispatcher->notifyEvent('run.import.keepass', + new Event($this, + EventMessage::factory() + ->addDetail(__('Cuenta importada'), $accountRequest->name)) + ); } } } catch (\Exception $e) { diff --git a/lib/SP/Services/Import/SyspassImport.php b/lib/SP/Services/Import/SyspassImport.php index 988d7fa6..e0e1b4c6 100644 --- a/lib/SP/Services/Import/SyspassImport.php +++ b/lib/SP/Services/Import/SyspassImport.php @@ -30,6 +30,8 @@ use SP\Account\AccountRequest; use SP\Config\ConfigDB; use SP\Core\Crypt\Crypt; use SP\Core\Crypt\Hash; +use SP\Core\Events\Event; +use SP\Core\Events\EventMessage; use SP\Core\OldCrypt; use SP\DataModel\CategoryData; use SP\DataModel\ClientData; @@ -167,6 +169,12 @@ class SyspassImport extends XmlImportBase implements ImportInterface $nodeData = $this->xmlDOM->getElementsByTagName('Encrypted')->item(0); $nodeData->parentNode->removeChild($nodeData); } + + $this->eventDispatcher->notifyEvent('run.import.syspass', + new Event($this, + EventMessage::factory() + ->addDescription(__('Datos desencriptados'))) + ); } /** @@ -195,6 +203,12 @@ class SyspassImport extends XmlImportBase implements ImportInterface try { $this->categories[$category->getAttribute('id')] = $this->addCategory($categoryData); + + $this->eventDispatcher->notifyEvent('run.import.syspass', + new Event($this, + EventMessage::factory() + ->addDetail(__('Categoría importada'), $categoryData->getName())) + ); } catch (\Exception $e) { processException($e); } @@ -226,8 +240,13 @@ class SyspassImport extends XmlImportBase implements ImportInterface } try { - $this->clients[$client->getAttribute('id')] = $this->addClient($clientData); + + $this->eventDispatcher->notifyEvent('run.import.syspass', + new Event($this, + EventMessage::factory() + ->addDetail(__('Cliente importado'), $clientData->getName())) + ); } catch (\Exception $e) { processException($e); } @@ -262,6 +281,12 @@ class SyspassImport extends XmlImportBase implements ImportInterface try { $this->clients[$client->getAttribute('id')] = $this->addClient($clientData); + + $this->eventDispatcher->notifyEvent('run.import.syspass', + new Event($this, + EventMessage::factory() + ->addDetail(__('Cliente importado'), $clientData->getName())) + ); } catch (\Exception $e) { processException($e); } @@ -291,6 +316,12 @@ class SyspassImport extends XmlImportBase implements ImportInterface try { $this->tags[$tag->getAttribute('id')] = $this->addTag($tagData); + + $this->eventDispatcher->notifyEvent('run.import.syspass', + new Event($this, + EventMessage::factory() + ->addDetail(__('Etiqueta importada'), $tagData->getName())) + ); } catch (\Exception $e) { processException($e); } @@ -345,6 +376,12 @@ class SyspassImport extends XmlImportBase implements ImportInterface try { $this->addAccount($accountRequest); + + $this->eventDispatcher->notifyEvent('run.import.syspass', + new Event($this, + EventMessage::factory() + ->addDetail(__('Cuenta importada'), $accountRequest->name)) + ); } catch (\Exception $e) { processException($e); } diff --git a/lib/SP/Services/Import/XmlImportBase.php b/lib/SP/Services/Import/XmlImportBase.php index f60fb0d2..385e520d 100644 --- a/lib/SP/Services/Import/XmlImportBase.php +++ b/lib/SP/Services/Import/XmlImportBase.php @@ -25,6 +25,7 @@ namespace SP\Services\Import; use SP\Bootstrap; +use SP\Core\Events\EventDispatcher; use SP\Services\Account\AccountService; use SP\Services\Category\CategoryService; use SP\Services\Client\ClientService; @@ -47,6 +48,10 @@ abstract class XmlImportBase * @var \DOMDocument */ protected $xmlDOM; + /** + * @var EventDispatcher + */ + protected $eventDispatcher; /** * ImportBase constructor. @@ -67,6 +72,7 @@ abstract class XmlImportBase $this->categoryService = $dic->get(CategoryService::class); $this->clientService = $dic->get(ClientService::class); $this->tagService = $dic->get(TagService::class); + $this->eventDispatcher = $dic->get(EventDispatcher::class); } /** @@ -91,18 +97,16 @@ abstract class XmlImportBase ); } - return; - } + if (!is_callable($callback)) { + throw new ImportException(__u('Método inválido'), ImportException::WARNING); + } - if (!is_callable($callback)) { - throw new ImportException(__u('Método inválido'), ImportException::WARNING); - } - - /** @var \DOMElement $nodes */ - foreach ($nodeList as $nodes) { - /** @var \DOMElement $Account */ - foreach ($nodes->getElementsByTagName($childNodeName) as $node) { - $callback($node); + /** @var \DOMElement $nodes */ + foreach ($nodeList as $nodes) { + /** @var \DOMElement $Account */ + foreach ($nodes->getElementsByTagName($childNodeName) as $node) { + $callback($node); + } } } } diff --git a/public/js/app-actions.min.js b/public/js/app-actions.min.js index 61c505fc..30da0117 100644 --- a/public/js/app-actions.min.js +++ b/public/js/app-actions.min.js @@ -1,5 +1,5 @@ var $jscomp={scope:{},findInternal:function(c,e,k){c instanceof String&&(c=String(c));for(var f=c.length,m=0;m