Rev 17268 | Rev 17270 | Ir a la última revisión | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |
<?php
declare(strict_types=1);
namespace LeadersLinked\Controller;
use Laminas\Db\Adapter\AdapterInterface;
use Laminas\Log\LoggerInterface;
use Laminas\Mvc\Controller\AbstractActionController;
use Laminas\View\Model\ViewModel;
use Laminas\View\Model\JsonModel;
use Laminas\Hydrator\ArraySerializableHydrator;
use Laminas\Db\ResultSet\HydratingResultSet;
use Laminas\Paginator\Adapter\DbSelect;
use Laminas\Paginator\Paginator;
use Laminas\Mvc\I18n\Translator;
use LeadersLinked\Mapper\MicrolearningTopicMapper;
use LeadersLinked\Mapper\MicrolearningUserMapper;
use LeadersLinked\Mapper\UserMapper;
use LeadersLinked\Mapper\QueryMapper;
use LeadersLinked\Mapper\ApplicationMapper;
use LeadersLinked\Mapper\PushMapper;
use LeadersLinked\Mapper\PushTemplateMapper;
use LeadersLinked\Mapper\DeviceHistoryMapper;
use LeadersLinked\Mapper\ApplicationVariantMapper;
use LeadersLinked\Mapper\NotificationMapper;
use LeadersLinked\Mapper\NetworkMapper;
use LeadersLinked\Mapper\CompanyMapper;
use LeadersLinked\Mapper\CompanyFollowerMapper;
use LeadersLinked\Mapper\ConnectionMapper;
use LeadersLinked\Mapper\UserPasswordMapper;
use LeadersLinked\Mapper\MicrolearningExtendUserMapper;
use LeadersLinked\Mapper\MicrolearningExtendUserCompanyMapper;
use LeadersLinked\Mapper\MicrolearningExtendUserFunctionMapper;
use LeadersLinked\Mapper\MicrolearningExtendUserGroupMapper;
use LeadersLinked\Mapper\MicrolearningExtendUserInstitutionMapper;
use LeadersLinked\Mapper\MicrolearningExtendUserProgramMapper;
use LeadersLinked\Mapper\MicrolearningExtendUserPartnerMapper;
use LeadersLinked\Mapper\MicrolearningExtendUserSectorMapper;
use LeadersLinked\Mapper\MicrolearningExtendUserStudentTypeMapper;
use LeadersLinked\Mapper\MicrolearningExtendUserCountryMapper;
use LeadersLinked\Model\MicrolearningUser;
use LeadersLinked\Model\Push;
use LeadersLinked\Model\Application;
use LeadersLinked\Model\Notification;
use LeadersLinked\Model\Network;
use LeadersLinked\Model\Connection;
use LeadersLinked\Model\CompanyFollower;
use LeadersLinked\Model\User;
use LeadersLinked\Model\UserType;
use LeadersLinked\Model\UserPassword;
use LeadersLinked\Model\MicrolearningExtendUser;
use LeadersLinked\Model\MicrolearningExtendUserCompany;
use LeadersLinked\Model\MicrolearningExtendUserFunction;
use LeadersLinked\Model\MicrolearningExtendUserGroup;
use LeadersLinked\Model\MicrolearningExtendUserInstitution;
use LeadersLinked\Model\MicrolearningExtendUserProgram;
use LeadersLinked\Model\MicrolearningExtendUserPartner;
use LeadersLinked\Model\MicrolearningExtendUserSector;
use LeadersLinked\Model\MicrolearningExtendUserStudentType;
use LeadersLinked\Model\MicrolearningExtendUserCountry;
use LeadersLinked\Form\Microlearning\TopicUserForm;
use LeadersLinked\Form\Microlearning\PushMicrolearningNotificationForm;
use LeadersLinked\Form\Microlearning\TopicCustomerUploadForm;
use LeadersLinked\Library\Functions;
use LeadersLinked\Cache\CacheInterface;
use LeadersLinked\Cache\CacheImpl;
use PhpOffice\PhpSpreadsheet\IOFactory;
use LeadersLinked\Mapper\MicrolearningTopicUserMapper;
use LeadersLinked\Model\MicrolearningTopicUser;
use LeadersLinked\Library\Storage;
class MicrolearningAccessForStudentsController extends AbstractActionController
{
/**
* @var AdapterInterface
*/
private $adapter;
/**
* @var CacheInterface
*/
private $cache;
/**
* @var LoggerInterface
*/
private $logger;
/**
* @var array
*/
private $config;
/**
* @var Translator
*/
private $translator;
/**
* @param AdapterInterface $adapter
* @param CacheInterface $cache
* @param LoggerInterface $logger
* @param array $config
* @param Translator $translator
*/
public function __construct($adapter, $cache, $logger, $config, $translator)
{
$this->adapter = $adapter;
$this->cache = $cache;
$this->logger = $logger;
$this->config = $config;
$this->translator = $translator;
}
public function indexAction()
{
try {
$currentUserPlugin = $this->plugin('currentUserPlugin');
$currentUser = $currentUserPlugin->getUser();
$currentCompany = $currentUserPlugin->getCompany();
$request = $this->getRequest();
if($request->isGet())
{
$headers = $request->getHeaders();
$isJson = false;
if($headers->has('Accept')) {
$accept = $headers->get('Accept');
$prioritized = $accept->getPrioritized();
foreach($prioritized as $key => $value) {
$raw = trim($value->getRaw());
if(!$isJson) $isJson = strpos($raw, 'json');
}
}
if($isJson) {
try {
$topic_uuid = Functions::sanitizeFilterString($request->getQuery('topic_uuid'));
$data = [
'link_upload' => '',
'items' => [] ,
'total' => 0,
];
if(!$topic_uuid) {
return new JsonModel([
'success' => true,
'data' => $data
]);
}
$topicMapper = MicrolearningTopicMapper::getInstance($this->adapter);
$topic = $topicMapper->fetchOneByUuid($topic_uuid);
if(!$topic) {
return new JsonModel([
'success' => true,
'data' => 'ERROR_TOPIC_NOT_FOUND'
]);
}
if($topic->company_id != $currentCompany->id) {
return new JsonModel([
'success' => true,
'data' => 'ERROR_UNAUTHORIZED'
]);
}
$data['link_upload'] = $this->url()->fromRoute('microlearning/access-for-students/upload',['topic_uuid' => $topic->uuid]);
$data['link_notification'] = $this->url()->fromRoute('microlearning/access-for-students/notification',['topic_uuid' => $topic->uuid]);
$search = $this->params()->fromQuery('search', []);
$search = empty($search['value']) ? '' : Functions::sanitizeFilterString($search['value']);
$page = intval($this->params()->fromQuery('start', 1), 10);
$records_x_page = intval($this->params()->fromQuery('length', 10), 10);
$order = $this->params()->fromQuery('order', []);
$order_field = empty($order[0]['column']) ? 99 : intval($order[0]['column'], 10);
$order_direction = empty($order[0]['dir']) ? 'ASC' : strtoupper(Functions::sanitizeFilterString($order[0]['dir']));
$fields = ['uuid', 'first_name', 'last_name', 'email'];
$order_field = isset($fields[$order_field]) ? $fields[$order_field] : 'first_name';
if(!in_array($order_direction, ['ASC', 'DESC'])) {
$order_direction = 'ASC';
}
$acl = $this->getEvent()->getViewModel()->getVariable('acl');
$allowRevoke = $acl->isAllowed($currentUser->usertype_id, 'microlearning/access-for-students/revoke');
$allowUnlimit = $acl->isAllowed($currentUser->usertype_id, 'microlearning/access-for-students/unlimit');
$allowCancel = $acl->isAllowed($currentUser->usertype_id, 'microlearning/access-for-students/cancel');
$allowReactive = $acl->isAllowed($currentUser->usertype_id, 'microlearning/access-for-students/reactive');
$queryMapper = QueryMapper::getInstance($this->adapter);
$sql = $queryMapper->getSql();
$select = $sql->select();
$select->columns(['access', 'paid_from', 'paid_to', 'added_on', 'updated_on']);
$select->from(['tb1' => MicrolearningTopicUserMapper::_TABLE] );
$select->join(['tb2' => UserMapper::_TABLE], 'tb1.user_id = tb2.id', ['uuid', 'first_name', 'last_name', 'email']);
$select->where->equalTo('tb1.company_id', $topic->company_id);
$select->where->equalTo('tb1.topic_id', $topic->id);
if($search) {
$select->where->nest()
->like('first_name', '%' . $search . '%')
->or->like('last_name', '%' . $search . '%')
->or->like('email', '%' . $search . '%')
->unnest();
}
$select->order($order_field . ' ' . $order_direction);
$hydrator = new ArraySerializableHydrator();
$resultset = new HydratingResultSet($hydrator);
$adapter = new DbSelect($select, $sql, $resultset);
$paginator = new Paginator($adapter);
$paginator->setItemCountPerPage($records_x_page);
$paginator->setCurrentPageNumber($page);
$items = [ ];
$records = $paginator->getCurrentItems();
foreach($records as $record)
{
$params = [
'topic_uuid' => $topic->uuid,
'user_uuid' => $record['uuid']
];
$actions = [];
switch($record['access'])
{
case MicrolearningTopicUser::ACCESS_UNLIMITED :
$actions['link_revoke'] = $allowRevoke ? $this->url()->fromRoute('microlearning/access-for-students/revoke', $params) : '';
$details['access'] = 'LABEL_UNLIMIT';
break;
case MicrolearningTopicUser::ACCESS_REVOKE :
$actions['link_unlimit'] = $allowUnlimit ? $this->url()->fromRoute('microlearning/access-for-students/unlimit', $params) : '';
$details['access'] = 'LABEL_REVOKED';
break;
case MicrolearningTopicUser::ACCESS_PAY_PERIOD :
$actions['link_cancel'] = $allowCancel ? $this->url()->fromRoute('microlearning/access-for-students/cancel', $params) : '';
$dt_paid_from = \DateTime::createFromFormat('Y-m-d', $record['paid_from']);
$dt_paid_to = \DateTime::createFromFormat('Y-m-d', $record['paid_to']);
$details['access'] = 'LABEL_PAY_PERIOD';
$details['paid_from'] = $dt_paid_from->format('d/m/Y');
$details['paid_to'] = $dt_paid_to->format('d/m/Y');
break;
case MicrolearningTopicUser::ACCESS_SUPENDED :
$dt_paid_from = \DateTime::createFromFormat('Y-m-d', $record['paid_from']);
$dt_paid_to = \DateTime::createFromFormat('Y-m-d', $record['paid_to']);
$details['access'] = 'LABEL_SUSPENDED';
$details['paid_from'] = $dt_paid_from->format('d/m/Y');
$details['paid_to'] = $dt_paid_to->format('d/m/Y');
break;
case MicrolearningTopicUser::ACCESS_CANCELLED :
$date = date('Y-m-d');
if($allowCancel && $record['paid_from'] <= $date && $record['paid_to'] >= $date) {
$actions['link_reactive'] = $allowReactive ? $this->url()->fromRoute('microlearning/access-for-students/reactive', $params) : '';
}
$dt_paid_from = \DateTime::createFromFormat('Y-m-d', $record['paid_from']);
$dt_paid_to = \DateTime::createFromFormat('Y-m-d', $record['paid_to']);
$details['access'] = 'LABEL_CANCELLED';
$details['paid_from'] = $dt_paid_from->format('d/m/Y');
$details['paid_to'] = $dt_paid_to->format('d/m/Y');
break;
}
$dt_added_on = \DateTime::createFromFormat('Y-m-d H:i:s', $record['added_on']);
$details['added_on'] = $dt_added_on->format('d/m/Y h:i a');
$dt_updated_on = \DateTime::createFromFormat('Y-m-d H:i:s', $record['updated_on']);
$details['updated_on'] = $dt_updated_on->format('d/m/Y h:i a');
$item = [
'uuid' => $record['uuid'],
'first_name' => $record['first_name'],
'last_name' => $record['last_name'],
'email' => $record['email'],
'details' => $details,
'actions' => $actions
];
array_push($items, $item);
}
$data['items'] = $items;
$data['total'] = $paginator->getTotalItemCount();
return new JsonModel([
'success' => true,
'data' => $data
]);
} catch (\Exception $e) {
$this->logger->err('Error in indexAction: ' . $e->getMessage());
return new JsonModel([
'success' => false,
'data' => 'An error occurred while processing your request'
]);
}
} else {
$topicMapper = MicrolearningTopicMapper::getInstance($this->adapter);
$topics = $topicMapper->fetchAllByCompanyId($currentCompany->id);
if($topics) {
$topic_id = $topics[0]->id;
} else {
$topic_id = 0;
}
$form = new TopicUserForm($this->adapter, $currentCompany->id, $topic_id);
$formPushNotification = new PushMicrolearningNotificationForm($this->adapter, $currentCompany->id);
$formTopicCustomer = new TopicCustomerUploadForm();
$this->layout()->setTemplate('layout/layout-backend');
$viewModel = new ViewModel();
$viewModel->setTemplate('leaders-linked/microlearning-access-for-students/index.phtml');
$viewModel->setVariables([
'form' => $form,
'formPushNotification' => $formPushNotification,
'formTopicCustomer' => $formTopicCustomer
]);
return $viewModel ;
}
} else {
return new JsonModel([
'success' => false,
'data' => 'ERROR_METHOD_NOT_ALLOWED'
]);
}
} catch (\Exception $e) {
$this->logger->err('Fatal error in indexAction: ' . $e->getMessage());
return new JsonModel([
'success' => false,
'data' => $e->getMessage()
]);
}
}
/**
* Revokes unlimited access for a user to a specific microlearning topic
*
* This action handles the revocation of unlimited access privileges for a user.
* It checks various conditions before proceeding with the revocation:
* - Validates current user and company
* - Verifies topic and user existence
* - Ensures proper authorization
* - Confirms the user has unlimited access before revoking
*
* @return JsonModel Returns a JSON response indicating success or failure
*/
public function revokeAction()
{
// Get the current request object
$request = $this->getRequest();
// Get current user and company from plugin
$currentUserPlugin = $this->plugin('currentUserPlugin');
$currentUser = $currentUserPlugin->getUser();
$currentCompany = $currentUserPlugin->getCompany();
// Get topic and user UUIDs from route parameters
$request = $this->getRequest();
$topic_uuid = $this->params()->fromRoute('topic_uuid');
$user_uuid = $this->params()->fromRoute('user_uuid');
// Validate current user exists
if(!$currentUser) {
return new JsonModel([
'success' => false,
'data' => 'ERROR_UNAUTHORIZED'
]);
}
// Validate current company exists
if(!$currentCompany) {
return new JsonModel([
'success' => false,
'data' => 'ERROR_UNAUTHORIZED'
]);
}
// Validate topic UUID was provided
if(!$topic_uuid) {
return new JsonModel([
'success' => false,
'data' => 'ERROR_TOPIC_NOT_FOUND'
]);
}
// Validate user UUID was provided
if(!$user_uuid) {
return new JsonModel([
'success' => false,
'data' => 'ERROR_USER_NOT_FOUND'
]);
}
// Fetch topic by UUID and validate it exists
$topicMapper = MicrolearningTopicMapper::getInstance($this->adapter);
$topic = $topicMapper->fetchOneByUuid($topic_uuid);
if(!$topic) {
return new JsonModel([
'success' => false,
'data' => 'ERROR_TOPIC_NOT_FOUND'
]);
}
// Validate topic belongs to current company
if($topic->company_id != $currentCompany->id) {
return new JsonModel([
'success' => false,
'data' => 'ERROR_UNAUTHORIZED'
]);
}
// Fetch user by UUID and validate it exists
$userMapper = UserMapper::getInstance($this->adapter);
$user = $userMapper->fetchOneByUuid($user_uuid);
if(!$user) {
return new JsonModel([
'success' => false,
'data' => 'ERROR_USER_NOT_FOUND'
]);
}
// Fetch topic-user relationship and validate it exists
$topicUserMapper = MicrolearningTopicUserMapper::getInstance($this->adapter);
$topicUser = $topicUserMapper->fetchOneByUserIdAndTopicId($user->id, $topic->id);
if(!$topicUser) {
return new JsonModel([
'success' => false,
'data' => 'ERROR_UNAUTHORIZED'
]);
}
// Process revocation only for POST requests
if($request->isPost()) {
// Validate user has unlimited access before revoking
if($topicUser->access != MicrolearningTopicUser::ACCESS_UNLIMITED) {
return new JsonModel([
'success' => false,
'data' => 'ERROR_USER_ACCESS_CANNT_BE_REVOKE'
]);
}
// Update access to revoked status
$topicUser->access = MicrolearningTopicUser::ACCESS_REVOKE;
if($topicUserMapper->update($topicUser)) {
// Refresh topic user data after update
$topicUser = $topicUserMapper->fetchOne($topicUser->id);
if($topicUser) {
// Update or create microlearning user record
$microlearningUserMapper = MicrolearningUserMapper::getInstance($this->adapter);
$microlearningUser = $microlearningUserMapper->fetchOneByUserIdAndCompanyId($topicUser->user_id, $topicUser->company_id);
if($microlearningUser) {
// Update existing microlearning user
$microlearningUser->updated_on = $topicUser->updated_on;
$microlearningUserMapper->update($microlearningUser);
} else {
// Create new microlearning user
$microlearningUser = new MicrolearningUser();
$microlearningUser->company_id = $topicUser->company_id;
$microlearningUser->user_id = $topicUser->user_id;
$microlearningUser->added_on = $topicUser->added_on;
$microlearningUser->updated_on = $topicUser->updated_on;
$microlearningUserMapper->insert($microlearningUser);
}
}
return new JsonModel([
'success' => true,
'data' => 'LABEL_USER_ACCESS_HAS_BEEN_REVOKE'
]);
} else {
return new JsonModel([
'success' => false,
'data' => $topicUserMapper->getError()
]);
}
}
// Return error for non-POST requests
return new JsonModel([
'success' => false,
'data' => 'ERROR_METHOD_NOT_ALLOWED'
]);
}
public function unlimitAction()
{
try {
$request = $this->getRequest();
$currentUserPlugin = $this->plugin('currentUserPlugin');
$currentUser = $currentUserPlugin->getUser();
$currentCompany = $currentUserPlugin->getCompany();
$topic_uuid = $this->params()->fromRoute('topic_uuid');
$user_uuid = $this->params()->fromRoute('user_uuid');
if(!$currentUser) {
return new JsonModel([
'success' => false,
'data' => 'ERROR_UNAUTHORIZED'
]);
}
if(!$currentCompany) {
return new JsonModel([
'success' => false,
'data' => 'ERROR_UNAUTHORIZED'
]);
}
if(!$topic_uuid) {
return new JsonModel([
'success' => false,
'data' => 'ERROR_TOPIC_NOT_FOUND'
]);
}
if(!$user_uuid) {
return new JsonModel([
'success' => false,
'data' => 'ERROR_USER_NOT_FOUND'
]);
}
if(!$request->isPost()) {
return new JsonModel([
'success' => false,
'data' => 'ERROR_METHOD_NOT_ALLOWED'
]);
}
$topicMapper = MicrolearningTopicMapper::getInstance($this->adapter);
$topic = $topicMapper->fetchOneByUuid($topic_uuid);
if(!$topic) {
return new JsonModel([
'success' => false,
'data' => 'ERROR_TOPIC_NOT_FOUND'
]);
}
if($topic->company_id != $currentCompany->id) {
return new JsonModel([
'success' => false,
'data' => 'ERROR_UNAUTHORIZED'
]);
}
$userMapper = UserMapper::getInstance($this->adapter);
$user = $userMapper->fetchOneByUuid($user_uuid);
if(!$user) {
return new JsonModel([
'success' => false,
'data' => 'ERROR_USER_NOT_FOUND'
]);
}
$topicUserMapper = MicrolearningTopicUserMapper::getInstance($this->adapter);
$topicUser = $topicUserMapper->fetchOneByUserIdAndTopicId($user->id, $topic->id);
if(!$topicUser) {
return new JsonModel([
'success' => false,
'data' => 'ERROR_UNAUTHORIZED'
]);
}
if($topicUser->access != MicrolearningTopicUser::ACCESS_REVOKE) {
return new JsonModel([
'success' => false,
'data' => 'ERROR_USER_ACCESS_CANNT_BE_UNLIMIT'
]);
}
$topicUser->access = MicrolearningTopicUser::ACCESS_UNLIMITED;
$topicUser->updated_on = date('Y-m-d H:i:s');
try {
$topicUserMapper->update($topicUser);
$microlearningUserMapper = MicrolearningUserMapper::getInstance($this->adapter);
$microlearningUser = $microlearningUserMapper->fetchOneByUserIdAndCompanyId($topicUser->user_id, $topicUser->company_id);
if(!$microlearningUser) {
$microlearningUser = new MicrolearningUser();
$microlearningUser->company_id = $topicUser->company_id;
$microlearningUser->user_id = $topicUser->user_id;
$microlearningUser->added_on = $topicUser->added_on;
$microlearningUser->updated_on = $topicUser->updated_on;
$microlearningUserMapper->insert($microlearningUser);
} else {
$microlearningUser->updated_on = $topicUser->updated_on;
$microlearningUserMapper->update($microlearningUser);
}
} catch (\Exception $e) {
$this->logger->err('Error updating topic user: ' . $e->getMessage());
return new JsonModel([
'success' => false,
'data' => 'ERROR_UPDATING_TOPIC_USER'
]);
}
return new JsonModel([
'success' => true,
'data' => 'LABEL_USER_ACCESS_HAS_BEEN_UNLIMITED'
]);
} catch (\Exception $e) {
$this->logger->err('Fatal error in unlimitAction: ' . $e->getMessage());
return new JsonModel([
'success' => false,
'data' => $e->getMessage()
]);
}
}
public function uploadAction()
{
try {
$request = $this->getRequest();
$currentUserPlugin = $this->plugin('currentUserPlugin');
$currentNetworkPlugin = $this->plugin('currentNetworkPlugin');
$currentUser = $currentUserPlugin->getUser();
$currentCompany = $currentUserPlugin->getCompany();
$currentNetwork = $currentNetworkPlugin->getNetwork();
$topic_uuid = $this->params()->fromRoute('topic_uuid');
if(!$currentUser) {
$this->logger->err('Upload failed: Current user not found');
return new JsonModel([
'success' => false,
'data' => 'ERROR_USER_NOT_FOUND'
]);
}
if(!$currentCompany) {
$this->logger->err('Upload failed: Current company not found');
return new JsonModel([
'success' => false,
'data' => 'ERROR_COMPANY_NOT_FOUND'
]);
}
if(!$currentNetwork) {
$this->logger->err('Upload failed: Current network not found');
return new JsonModel([
'success' => false,
'data' => 'ERROR_NETWORK_NOT_FOUND'
]);
}
if(!$topic_uuid) {
$this->logger->err('Upload failed: Topic UUID not provided');
return new JsonModel([
'success' => false,
'data' => 'ERROR_TOPIC_NOT_FOUND'
]);
}
if(!$request->isPost()) {
$this->logger->err('Upload failed: Request method not POST');
return new JsonModel([
'success' => false,
'data' => 'ERROR_METHOD_NOT_ALLOWED'
]);
}
$topicMapper = MicrolearningTopicMapper::getInstance($this->adapter);
$topic = $topicMapper->fetchOneByUuid($topic_uuid);
if(!$topic) {
$this->logger->err('Upload failed: Topic not found for UUID: ' . $topic_uuid);
return new JsonModel([
'success' => false,
'data' => 'ERROR_TOPIC_NOT_FOUND'
]);
}
if($topic->company_id != $currentCompany->id) {
$this->logger->err('Upload failed: Topic does not belong to current company');
return new JsonModel([
'success' => false,
'data' => 'ERROR_UNAUTHORIZED'
]);
}
$step = Functions::sanitizeFilterString($this->params()->fromPost('step'));
$storage = Storage::getInstance($this->config, $this->adapter);
// Log uploaded files info
$files = $request->getFiles()->toArray();
$this->logger->info('Uploaded files: ' . print_r($files, true));
$storage->setFiles($files);
if(!$storage->setCurrentFilename('file')) {
$this->logger->err('Upload failed: Could not set current filename');
return new JsonModel([
'success' => false,
'data' => 'ERROR_UPLOAD_FILE'
]);
}
if($step == 'validation') {
try {
$userMapper = UserMapper::getInstance($this->adapter);
$topicUserMapper = MicrolearningTopicUserMapper::getInstance($this->adapter);
$tmp_filename = $storage->getTmpFilename();
$filename = $storage->getFilename();
$target_filename = $storage->composePathToFilename(
Storage::TYPE_MICROLEARNING_ACCESS_FOR_STUDENTS,
$topic->uuid,
$filename
);
$this->logger->info('Processing file upload: ' . $target_filename);
if(!$storage->putFile($tmp_filename, $target_filename)) {
$this->logger->err('Upload failed: Could not move file to target location');
return new JsonModel([
'success' => false,
'data' => 'ERROR_UPLOAD_FILE'
]);
}
$count_users = 0;
$users = [];
$count_errors = 0;
$errors = [];
try {
$this->logger->info('Loading spreadsheet: ' . $target_filename);
$spreadsheet = IOFactory::load($target_filename);
$records = $spreadsheet->getActiveSheet()->toArray(null, true, true, true);
} catch (\Exception $e) {
$this->logger->err('Spreadsheet processing failed: ' . $e->getMessage());
return new JsonModel([
'success' => false,
'data' => 'ERROR_UPLOAD_FILE: ' . $e->getMessage()
]);
}
$emails = [];
$row = 0;
foreach($records as $record)
{
/*
A = Nombre
B = Apellido
C = Email
D = Contraseña
E = Empresa
F = Función
G = Grupo
H = Institución
I = Programa
J = Socio
K = Sector
L = Tipo de Estudiante
M = País
N = Es adulto
*/
$row++;
$first_name = Functions::sanitizeFilterString($record['A']);
$last_name = Functions::sanitizeFilterString($record['B']);
$email = trim(filter_var($record['C'], FILTER_SANITIZE_EMAIL));
$password = Functions::sanitizeFilterString($record['D']);
$company = isset($record['E']) ? Functions::sanitizeFilterString($record['E']) : '';
$function = isset($record['F']) ? Functions::sanitizeFilterString($record['F']) : '';
$group = isset($record['G']) ? Functions::sanitizeFilterString($record['G']) : '';
$institution = isset($record['H']) ? Functions::sanitizeFilterString($record['H']) : '';
$program = isset($record['I']) ? Functions::sanitizeFilterString($record['I']) : '';
$partner = isset($record['J']) ? Functions::sanitizeFilterString($record['J']) : '';
$sector = isset($record['K']) ? Functions::sanitizeFilterString($record['K']) : '';
$studentType = isset($record['L']) ? Functions::sanitizeFilterString($record['L']) : '';
$country = isset($record['M']) ? Functions::sanitizeFilterString($record['M']) : '';
$isAdult = isset($record['N']) ? Functions::sanitizeFilterString($record['N']) : '';
if($row == 1) {
continue;
}
//|| empty($password)
if(empty($first_name) || empty($last_name) || !filter_var($email, FILTER_VALIDATE_EMAIL) ) {
continue;
}
if(!in_array($email, $emails)) {
$user = $userMapper->fetchOneByEmail($email);
$assigned_topics = $user ? $topicUserMapper->fetchCountByCompanyIdAndTopicIdAndUserId($topic->company_id, $topic->id, $user->id) : 0;
$count_users++;
array_push($emails, $email);
array_push($users, [
'id' => $count_users,
'first_name' => $first_name,
'last_name' => $last_name,
'password' => $password,
'email' => $email,
'assigned_topics' => $assigned_topics,
'company' => $company,
'function' => $function,
'group' => $group,
'institution' => $institution,
'program' => $program,
'partner' => $partner,
'sector' => $sector,
'studentType' => $studentType,
'country' => $country,
'isAdult' => $isAdult,
]);
} else {
$count_errors++;
array_push($errors,[
'id' => $count_errors,
'first_name' => $first_name,
'last_name' => $last_name,
'password' => $password,
'email' => $email,
'status' => 'DUPLICATE IN EXCEL'
]);
}
}
$this->logger->info('Users: ' . print_r($users, true));
$this->logger->info('Errors: ' . print_r($errors, true));
try {
$this->logger->info('Cache users: ' . print_r($users, true));
$key = md5($currentUser->id . '-' . $topic->uuid . '-' . $topic->uuid);
$this->cache->setItem($key, serialize($users));
} catch (\Exception $e) {
$this->logger->err('Cache operation failed: ' . $e->getMessage());
return new JsonModel([
'success' => false,
'data' => 'ERROR_CACHE_SET_ITEM: ' . $e->getMessage()
]);
}
return new JsonModel([
'success' => true,
'data' => [
'key' => $key,
'topic' => $topic->name,
'items' => [
'ok' => $users,
'error' => $errors,
],
]
]);
} catch (\Exception $e) {
$this->logger->err('Validation step failed: ' . $e->getMessage());
throw $e;
}
}
return new JsonModel([
'success' => false,
'data' => 'ERROR_PARAMETERS_ARE_INVALID'
]);
} catch (\Exception $e) {
$this->logger->err('Fatal error in uploadAction: ' . $e->getMessage() . "\n" . $e->getTraceAsString());
return new JsonModel([
'success' => false,
'data' => 'ERROR_INTERNAL_SERVER_ERROR'
]);
}
}
/**
* Handles sending push notifications to selected users about a microlearning topic
*
* This action processes push notifications for microlearning topics:
* 1. Validates user permissions and input data
* 2. Processes the notification form
* 3. Sends push notifications to selected users' devices
*
* Required parameters:
* - topic_uuid: UUID of the microlearning topic
* - customer_uuids: Array of user UUIDs to receive the notification
* - push_template_id: UUID of the push notification template to use
*
* @return JsonModel Returns JSON response with:
* - success: true/false
* - data: Contains push_to_send count or error message
* @throws \Throwable When an error occurs during processing
*/
public function notificationAction()
{
try {
// Get current request and user context
$request = $this->getRequest();
$currentUserPlugin = $this->plugin('currentUserPlugin');
$currentUser = $currentUserPlugin->getUser();
$currentCompany = $currentUserPlugin->getCompany();
$topic_uuid = $this->params()->fromRoute('topic_uuid');
// Validate topic UUID exists
if(!$topic_uuid) {
return new JsonModel([
'success' => false,
'data' => 'ERROR_TOPIC_UUID_NOT_FOUND'
]);
}
// Validate company exists
if(!$currentCompany) {
return new JsonModel([
'success' => false,
'data' => 'ERROR_COMPANY_NOT_FOUND'
]);
}
// Fetch and validate topic
$topicMapper = MicrolearningTopicMapper::getInstance($this->adapter);
$topic = $topicMapper->fetchOneByUuid($topic_uuid);
if(!$topic) {
return new JsonModel([
'success' => false,
'data' => 'ERROR_TOPIC_NOT_FOUND'
]);
}
// Validate topic belongs to current company
if($topic->company_id != $currentCompany->id) {
return new JsonModel([
'success' => false,
'data' => 'ERROR_UNAUTHORIZED'
]);
}
// Validate request method
if(!$request->isPost()) {
return new JsonModel([
'success' => false,
'data' => 'ERROR_METHOD_NOT_ALLOWED'
]);
}
// Process notification form
$dataPost = $request->getPost()->toArray();
$form = new PushMicrolearningNotificationForm($this->adapter, $currentCompany->id);
$form->setData($dataPost);
// Validate form data
if(!$form->isValid()) {
$messages = [];
$form_messages = (array) $form->getMessages();
foreach($form_messages as $fieldname => $field_messages)
{
$messages[$fieldname] = array_values($field_messages);
}
return new JsonModel([
'success' => false,
'data' => $messages
]);
}
// Validate selected users
$customer_uuids = $this->params()->fromPost('customer_uuids');
if(!$customer_uuids) {
return new JsonModel([
'success' => false,
'data' => 'ERROR_NOT_SELECTED_CUSTOMERS'
]);
}
// Get push template
$push_template_uuid = Functions::sanitizeFilterString($form->get('push_template_id')->getValue());
$pushMapper = PushMapper::getInstance($this->adapter);
$pushTemplateMapper = PushTemplateMapper::getInstance($this->adapter);
$pushTemplate = $pushTemplateMapper->fetchOneByUuid($push_template_uuid);
if(!$pushTemplate) {
return new JsonModel([
'success' => false,
'data' => 'ERROR_PUSH_TEMPLATE_NOT_FOUND'
]);
}
// Initialize push notification process
$applicationMapper = ApplicationMapper::getInstance($this->adapter);
$application = $applicationMapper->fetchOne(Application::TWOGETSKILLS);
$topicUserMapper = MicrolearningTopicUserMapper::getInstance($this->adapter);
$push_to_send = 0;
// Process each selected user
$userMapper = UserMapper::getInstance($this->adapter);
$deviceHistoryMapper = DeviceHistoryMapper::getInstance($this->adapter);
foreach($customer_uuids as $customer_uuid)
{
$user = $userMapper->fetchOneByUuid($customer_uuid);
if(!$user) {
continue;
}
$topicUser = $topicUserMapper->fetchOneByUserIdAndTopicId($user->id, $topic->id);
if(!$topicUser) {
continue;
}
// Get user's latest device
$device = $deviceHistoryMapper->fetchLastDeviceByApplicationIdAndUserId(Application::TWOGETSKILLS, $user->id);
if($device && $device->token) {
$key = $application->key;
if($device->variant_id) {
$applicationVariantMapper = ApplicationVariantMapper::getInstance($this->adapter);
$applicationVariant = $applicationVariantMapper->fetchOneByApplicationIdAndVariantId($device->application_id, $device->variant_id);
if($applicationVariant) {
$key = $applicationVariant->key;
} else {
$applicationVariant = $applicationVariantMapper->fetchOneByApplicationIdAndDefault($device->application_id);
if($applicationVariant) {
$key = $applicationVariant->key;
}
}
}
// Create push notification
$push = new Push();
$push->status = Push::STATUS_PENDING;
$push->data = json_encode([
'server' => [
'key' => $key,
],
'push' => [
'registration_ids' => [
$device->token,
],
'notification' => [
'body' => $pushTemplate->body,
'title' => $pushTemplate->title,
'vibrate' => 1,
'sound' => 1
],
'data' => [
'command' => 'content-refresh',
'new_topics' => '0',
]
]
]);
if($pushMapper->insert($push)) {
$push_to_send = $push_to_send + 1;
}
}
}
// Validate notifications were created
if(0 == $push_to_send) {
return new JsonModel([
'success' => false,
'data' => 'ERROR_NO_USER_DEVICES_WERE_FOUND_TO_SEND_PUSH'
]);
}
return new JsonModel([
'success' => true,
'data' => [
'push_to_send' => $push_to_send,
]
]);
} catch (\Throwable $e) {
$this->logger->error($e->getMessage());
return new JsonModel([
'success' => false,
'data' => 'ERROR_INTERNAL_SERVER_ERROR'
]);
}
}
}