Rev 17263 | Rev 17265 | Ir a la última revisión | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |
<?phpdeclare(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 existsif(!$currentUser) {return new JsonModel(['success' => false,'data' => 'ERROR_UNAUTHORIZED']);}// Validate current company existsif(!$currentCompany) {return new JsonModel(['success' => false,'data' => 'ERROR_UNAUTHORIZED']);}// Validate topic UUID was providedif(!$topic_uuid) {return new JsonModel(['success' => false,'data' => 'ERROR_TOPIC_NOT_FOUND']);}// Validate user UUID was providedif(!$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 companyif($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 requestsif($request->isPost()) {// Validate user has unlimited access before revokingif($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 requestsreturn 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 = NombreB = ApellidoC = EmailD = ContraseñaE = EmpresaF = FunciónG = GrupoH = InstituciónI = ProgramaJ = SocioK = SectorL = Tipo de EstudianteM = PaísN = 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']);}}try {$key = md5($currentUser->id . '-' . $topic->uuid . '-' . $topic->uuid);$this->cache->setItem($key, serialize($users));$this->logger->info('Cache operation successful: ' . $key);} 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 existsif(!$topic_uuid) {return new JsonModel(['success' => false,'data' => 'ERROR_TOPIC_UUID_NOT_FOUND']);}// Validate company existsif(!$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 companyif($topic->company_id != $currentCompany->id) {return new JsonModel(['success' => false,'data' => 'ERROR_UNAUTHORIZED']);}// Validate request methodif(!$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 dataif(!$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 createdif(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']);}}}