Proyectos de Subversion LeadersLinked - Backend

Rev

Rev 17229 | Rev 17231 | Ir a la última revisión | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 www 1
<?php
2
declare(strict_types=1);
3
 
4
namespace LeadersLinked\Controller;
5
 
6
use Laminas\Mvc\Controller\AbstractActionController;
7
use Laminas\View\Model\JsonModel;
17184 stevensc 8
use Laminas\View\Model\ViewModel;
17216 stevensc 9
use Laminas\Hydrator\ObjectPropertyHydrator;
1 www 10
use LeadersLinked\Library\Functions;
17002 efrain 11
use LeadersLinked\Mapper\MicrolearningTopicMapper;
17101 stevensc 12
use LeadersLinked\Mapper\MicrolearningCapsuleMapper;
17002 efrain 13
use LeadersLinked\Model\MicrolearningTopic;
14
use LeadersLinked\Form\Microlearning\TopicAddForm;
15
use LeadersLinked\Form\Microlearning\TopicEditForm;
16
use LeadersLinked\Library\Storage;
17107 stevensc 17
use LeadersLinked\Model\MicrolearningTopicCapsule;
18
use LeadersLinked\Mapper\MicrolearningTopicCapsuleMapper;
17178 stevensc 19
use LeadersLinked\Mapper\MicrolearningUserProgressMapper;
17187 stevensc 20
use LeadersLinked\Mapper\QueryMapper;
21
use LeadersLinked\Mapper\UserMapper;
22
use LeadersLinked\Mapper\MicrolearningTopicUserMapper;
23
use LeadersLinked\Model\MicrolearningTopicUser;
1 www 24
 
25
class MicrolearningTopicController extends AbstractActionController
26
{
27
    /**
28
     *
16769 efrain 29
     * @var \Laminas\Db\Adapter\AdapterInterface
1 www 30
     */
31
    private $adapter;
32
 
33
    /**
34
     *
16769 efrain 35
     * @var \LeadersLinked\Cache\CacheInterface
1 www 36
     */
16769 efrain 37
    private $cache;
38
 
39
 
40
    /**
41
     *
42
     * @var \Laminas\Log\LoggerInterface
43
     */
1 www 44
    private $logger;
45
 
46
    /**
47
     *
48
     * @var array
49
     */
50
    private $config;
51
 
16769 efrain 52
 
1 www 53
    /**
54
     *
16769 efrain 55
     * @var \Laminas\Mvc\I18n\Translator
56
     */
57
    private $translator;
58
 
59
 
60
    /**
61
     *
62
     * @param \Laminas\Db\Adapter\AdapterInterface $adapter
63
     * @param \LeadersLinked\Cache\CacheInterface $cache
64
     * @param \Laminas\Log\LoggerInterface LoggerInterface $logger
1 www 65
     * @param array $config
16769 efrain 66
     * @param \Laminas\Mvc\I18n\Translator $translator
1 www 67
     */
16769 efrain 68
    public function __construct($adapter, $cache, $logger, $config, $translator)
1 www 69
    {
16769 efrain 70
        $this->adapter      = $adapter;
71
        $this->cache        = $cache;
72
        $this->logger       = $logger;
73
        $this->config       = $config;
74
        $this->translator   = $translator;
1 www 75
    }
76
 
77
    /**
78
     *
17178 stevensc 79
     * Generación del listado de tópicos
1 www 80
     * {@inheritDoc}
81
     * @see \Laminas\Mvc\Controller\AbstractActionController::indexAction()
17178 stevensc 82
     * @return JsonModel
1 www 83
     */
84
    public function indexAction()
85
    {
17178 stevensc 86
        try {
87
            $request = $this->getRequest();
88
 
89
            $currentUserPlugin = $this->plugin('currentUserPlugin');
90
            $currentCompany = $currentUserPlugin->getCompany();
91
            $currentUser    = $currentUserPlugin->getUser();
1 www 92
 
17178 stevensc 93
            if (!$request->isGet()) {
94
                return $this->createErrorResponse('ERROR_METHOD_NOT_ALLOWED');
95
            }
96
 
97
            if(!$currentCompany) {
98
                return $this->createErrorResponse('ERROR_COMPANY_NOT_FOUND');
99
            }
100
 
101
            if(!$currentUser) {
102
                return $this->createErrorResponse('ERROR_USER_NOT_FOUND');
103
            }
1 www 104
 
17178 stevensc 105
            if($this->isJsonRequest($request)) {
106
                return $this->handleJsonRequest($currentUser, $currentCompany);
107
            }
108
 
109
            return $this->handleHtmlRequest($currentCompany);
110
        } catch (\Exception $e) {
111
            $this->logger->err('Error in indexAction: ' . $e->getMessage());
112
            return $this->createErrorResponse('ERROR_INTERNAL_SERVER_ERROR');
113
        }
114
    }
115
 
116
    /**
117
     *
118
     * Agregar un tópico
119
     * {@inheritDoc}
120
     * @see \Laminas\Mvc\Controller\AbstractActionController::addAction()
121
     * @return JsonModel
122
     */
123
    public function addAction()
124
    {
125
        try {
126
            $request    = $this->getRequest();
127
 
128
            $currentUserPlugin  = $this->plugin('currentUserPlugin');
129
            $currentCompany     = $currentUserPlugin->getCompany();
130
            $currentUser        = $currentUserPlugin->getUser();
131
 
132
            if(!$currentCompany) {
133
                return $this->createErrorResponse('ERROR_COMPANY_NOT_FOUND');
134
            }
135
 
136
            if(!$currentUser) {
137
                return $this->createErrorResponse('ERROR_USER_NOT_FOUND');
138
            }
139
 
140
            if(!$request->isPost() && !$request->isGet()) {
141
                return $this->createErrorResponse('ERROR_METHOD_NOT_ALLOWED');
142
            }
143
 
144
            if($request->isGet()) {
145
                $capsuleMapper = MicrolearningCapsuleMapper::getInstance($this->adapter);
146
                $records =  $capsuleMapper->fetchAllByCompanyId($currentCompany->id);
1 www 147
 
17178 stevensc 148
                $capsules = [];
149
 
150
                foreach($records as $record) {
151
                    array_push($capsules, [
17215 stevensc 152
                        'uuid' => $record->uuid,
17178 stevensc 153
                        'name' => $record->name,
154
                    ]);
1 www 155
                }
17178 stevensc 156
 
157
                return new JsonModel([
158
                    'success' => true,
159
                    'data' => ['capsules' => $capsules]
160
                ]);
1 www 161
            }
17178 stevensc 162
 
163
            if($request->isPost()) {
164
                $form = new TopicAddForm($this->adapter, $currentCompany->id);
165
                $dataPost = array_merge($request->getPost()->toArray(), $request->getFiles()->toArray());
1 www 166
 
17178 stevensc 167
                $form->setData($dataPost);
1 www 168
 
17178 stevensc 169
                if(!$form->isValid()) {
170
                    $messages = [];
171
                    $form_messages = (array) $form->getMessages();
172
 
173
                    foreach($form_messages  as $fieldname => $field_messages)
174
                    {
175
                        $messages[$fieldname] = array_values($field_messages);
176
                    }
177
 
178
                    return new JsonModel([
179
                        'success'   => false,
180
                        'data'   => $messages
181
                    ]);
1 www 182
                }
183
 
17178 stevensc 184
                $dataPost = (array) $form->getData();
185
 
186
                $topic = new MicrolearningTopic();
17223 stevensc 187
                $topic->name = $dataPost['name'];
188
                $topic->description = $dataPost['description'];
189
                $topic->order = $dataPost['order'];
190
                $topic->status = $dataPost['status'];
191
                $topic->privacy = $dataPost['privacy'];
192
                $topic->type = $dataPost['type'];
193
                $topic->cost = $dataPost['cost'];
17178 stevensc 194
                $topic->company_id = $currentCompany->id;
195
                $topic->image = '';
196
                $topic->marketplace = '';
17216 stevensc 197
 
17178 stevensc 198
                $topicMapper = MicrolearningTopicMapper::getInstance($this->adapter);
1 www 199
 
17216 stevensc 200
                // First insert the topic to get the ID and UUID
17178 stevensc 201
                if(!$topicMapper->insert($topic)) {
202
                    return $this->createErrorResponse($topicMapper->getError());
203
                }
1 www 204
 
17216 stevensc 205
                // Get the complete topic with ID and UUID
17178 stevensc 206
                $topic = $topicMapper->fetchOne($topic->id);
17002 efrain 207
 
17216 stevensc 208
                // Now process images with the proper UUID
17214 stevensc 209
                $imageResult = $this->processTopicImages($request, $topic);
210
                if (!$imageResult['success']) {
211
                    return $this->createErrorResponse($imageResult['error']);
17178 stevensc 212
                }
1 www 213
 
17216 stevensc 214
                // Update topic with image filenames if processed
215
                if (!empty($imageResult['image_filename']) || !empty($imageResult['marketplace_filename'])) {
216
                    if (!empty($imageResult['image_filename'])) {
217
                        $topic->image = $imageResult['image_filename'];
218
                    }
219
                    if (!empty($imageResult['marketplace_filename'])) {
220
                        $topic->marketplace = $imageResult['marketplace_filename'];
221
                    }
222
 
223
                    if(!$topicMapper->update($topic)) {
224
                        return $this->createErrorResponse($topicMapper->getError());
225
                    }
226
                }
17002 efrain 227
 
17216 stevensc 228
                // Create capsule relations
17224 stevensc 229
                $result = $this->updateTopicCapsuleRelations($topic, $dataPost['capsule_uuid'], $currentCompany->id, $currentUser);
17216 stevensc 230
                if (!$result['success']) {
231
                    return $this->createErrorResponse($result['error']);
17178 stevensc 232
                }
1 www 233
 
17178 stevensc 234
                $this->logger->info('Se agrego el tópico ' . $topic->name, ['user_id' => $currentUser->id, 'ip' => Functions::getUserIP()]);
1 www 235
 
236
                return new JsonModel([
17178 stevensc 237
                    'success'   => true,
238
                    'data'   => 'LABEL_RECORD_ADDED'
1 www 239
                ]);
240
            }
17178 stevensc 241
        } catch (\Exception $e) {
242
            $this->logger->err('Error in addAction: ' . $e->getMessage());
243
            return $this->createErrorResponse('ERROR_INTERNAL_SERVER_ERROR');
1 www 244
        }
245
    }
17178 stevensc 246
 
247
    /**
248
     *
249
     * Borrar un tópico
250
     * {@inheritDoc}
251
     * @see \Laminas\Mvc\Controller\AbstractActionController::deleteAction()
252
     * @return JsonModel
253
     */
254
    public function deleteAction()
255
    {
256
        try {
257
            $request = $this->getRequest();
1 www 258
 
17178 stevensc 259
            $currentUserPlugin  = $this->plugin('currentUserPlugin');
260
            $currentCompany     = $currentUserPlugin->getCompany();
261
            $currentUser        = $currentUserPlugin->getUser();
262
 
263
            $id   = $this->params()->fromRoute('id');
264
 
265
            if(!$currentCompany) {
266
                return $this->createErrorResponse('ERROR_COMPANY_NOT_FOUND');
267
            }
268
 
269
            if(!$currentUser) {
270
                return $this->createErrorResponse('ERROR_USER_NOT_FOUND');
271
            }
272
 
273
            if(!$request->isPost()) {
274
                return $this->createErrorResponse('ERROR_METHOD_NOT_ALLOWED');
275
            }
276
 
277
            $topicMapper = MicrolearningTopicMapper::getInstance($this->adapter);
278
            $topic = $topicMapper->fetchOneByUuid($id);
1 www 279
 
17178 stevensc 280
            if(!$topic) {
281
                return $this->createErrorResponse('ERROR_TOPIC_NOT_FOUND');
282
            }
283
 
284
            if($topic->company_id != $currentCompany->id) {
285
                return $this->createErrorResponse('ERROR_UNAUTHORIZED');
286
            }
16943 efrain 287
 
17178 stevensc 288
            // Eliminar las relaciones con cápsulas primero
289
            $topicCapsuleMapper = MicrolearningTopicCapsuleMapper::getInstance($this->adapter);
290
 
291
            if(!$topicCapsuleMapper->deleteByTopicId($topic->id)) {
292
                return $this->createErrorResponse($topicCapsuleMapper->getError());
293
            }
17127 stevensc 294
 
17178 stevensc 295
            // Eliminar el progreso de los usuarios
296
            $userProgressMapper = MicrolearningUserProgressMapper::getInstance($this->adapter);
1 www 297
 
17178 stevensc 298
            if(!$userProgressMapper->deleteAllTopicProgressByTopicId($topic->id)) {
299
                return $this->createErrorResponse($userProgressMapper->getError());
17133 stevensc 300
            }
17127 stevensc 301
 
17178 stevensc 302
            // Eliminar archivos de almacenamiento
303
            $storage = Storage::getInstance($this->config, $this->adapter);
304
            $target_path = $storage->getPathMicrolearningTopic();
305
 
306
            if($topic->image) {
307
                if(!$storage->deleteFile($target_path, $topic->uuid, $topic->image)) {
308
                    return $this->createErrorResponse('ERROR_DELETING_FILE');
309
                }
310
            }
17127 stevensc 311
 
17178 stevensc 312
            if(!$topicMapper->delete($topic)) {
313
                return $this->createErrorResponse($topicMapper->getError());
17133 stevensc 314
            }
17127 stevensc 315
 
17178 stevensc 316
            if($topic->marketplace) {
317
                if(!$storage->deleteFile($target_path, $topic->uuid, $topic->marketplace)) {
318
                    return $this->createErrorResponse('ERROR_DELETING_FILE');
319
                }
320
            }
321
 
322
            // Registrar la acción en el log
323
            $this->logger->info('Se borro el tópico : ' . $topic->name, [
324
            'user_id' => $currentUser->id,
325
            'ip' => Functions::getUserIP()
326
            ]);
327
 
328
            return new JsonModel([
17133 stevensc 329
                'success' => true,
17178 stevensc 330
                'data' => 'LABEL_RECORD_DELETED'
331
            ]);
332
        } catch (\Exception $e) {
333
            $this->logger->err('Error in deleteAction: ' . $e->getMessage());
334
            return $this->createErrorResponse('ERROR_INTERNAL_SERVER_ERROR');
17133 stevensc 335
        }
17178 stevensc 336
    }
337
 
338
    /**
339
     *
340
     * Editar un tópico
341
     * {@inheritDoc}
342
     * @see \Laminas\Mvc\Controller\AbstractActionController::editAction()
343
     * @return JsonModel
344
     */
345
    public function editAction()
346
    {
347
        try {
348
            $request = $this->getRequest();
17127 stevensc 349
 
17178 stevensc 350
            $currentUserPlugin  = $this->plugin('currentUserPlugin');
351
            $currentCompany     = $currentUserPlugin->getCompany();
352
            $currentUser        = $currentUserPlugin->getUser();
17133 stevensc 353
 
17226 stevensc 354
            $id = $this->params()->fromRoute('id');
17133 stevensc 355
 
17226 stevensc 356
            // Validate basic requirements
17178 stevensc 357
            if(!$currentCompany) {
17226 stevensc 358
                $this->logger->err('editAction: Company not found', ['user_id' => $currentUser ? $currentUser->id : null]);
17178 stevensc 359
                return $this->createErrorResponse('ERROR_COMPANY_NOT_FOUND');
360
            }
361
 
362
            if(!$currentUser) {
17226 stevensc 363
                $this->logger->err('editAction: User not found');
17178 stevensc 364
                return $this->createErrorResponse('ERROR_USER_NOT_FOUND');
365
            }
366
 
17226 stevensc 367
            if(!$id) {
368
                $this->logger->err('editAction: Topic ID not provided', ['user_id' => $currentUser->id]);
369
                return $this->createErrorResponse('ERROR_TOPIC_ID_REQUIRED');
370
            }
371
 
17178 stevensc 372
            if(!$request->isPost() && !$request->isGet()) {
17226 stevensc 373
                $this->logger->err('editAction: Invalid HTTP method', [
374
                    'method' => $request->getMethod(),
375
                    'user_id' => $currentUser->id
376
                ]);
17178 stevensc 377
                return $this->createErrorResponse('ERROR_METHOD_NOT_ALLOWED');
378
            }
379
 
17226 stevensc 380
            // Validate topic exists and belongs to company
381
            $topicMapper = MicrolearningTopicMapper::getInstance($this->adapter);
382
            $topic = $topicMapper->fetchOneByUuid($id);
383
 
384
            if(!$topic) {
385
                $this->logger->err('editAction: Topic not found', [
386
                    'topic_uuid' => $id,
387
                    'user_id' => $currentUser->id
388
                ]);
389
                return $this->createErrorResponse('ERROR_TOPIC_NOT_FOUND');
390
            }
391
 
392
            if($topic->company_id != $currentCompany->id) {
393
                $this->logger->err('editAction: Unauthorized access attempt', [
394
                    'topic_uuid' => $id,
395
                    'topic_company_id' => $topic->company_id,
396
                    'user_company_id' => $currentCompany->id,
397
                    'user_id' => $currentUser->id
398
                ]);
399
                return $this->createErrorResponse('ERROR_UNAUTHORIZED');
400
            }
401
 
17178 stevensc 402
            if($request->isGet()) {
17226 stevensc 403
                return $this->handleEditGetRequest($topic, $currentCompany);
404
            }
17133 stevensc 405
 
17226 stevensc 406
            if($request->isPost()) {
407
                return $this->handleEditPostRequest($request, $topic, $currentCompany, $currentUser);
408
            }
409
 
410
        } catch (\Exception $e) {
411
            $this->logger->err('Error in editAction: ' . $e->getMessage(), [
412
                'exception' => $e->getTraceAsString(),
413
                'topic_id' => $id ?? null,
414
                'user_id' => isset($currentUser) ? $currentUser->id : null
415
            ]);
416
            return $this->createErrorResponse('ERROR_INTERNAL_SERVER_ERROR');
417
        }
418
    }
419
 
420
    /**
421
     * Handle GET request for edit action
422
     * @param \LeadersLinked\Model\MicrolearningTopic $topic
423
     * @param \LeadersLinked\Model\Company $currentCompany
424
     * @return \Laminas\View\Model\JsonModel
425
     */
426
    private function handleEditGetRequest($topic, $currentCompany)
427
    {
428
        try {
429
            $capsuleMapper = MicrolearningCapsuleMapper::getInstance($this->adapter);
430
            $records = $capsuleMapper->fetchAllByCompanyId($currentCompany->id);
431
 
432
            $capsules = [];
433
            $topicCapsuleMapper = MicrolearningTopicCapsuleMapper::getInstance($this->adapter);
434
 
435
            foreach($records as $record) {
436
                try {
17211 stevensc 437
                    $topicCapsule = $topicCapsuleMapper->fetchOneByTopicIdAndCapsuleId($topic->id, $record->id);
438
                    $selected = $topicCapsule ? true : false;
439
 
17178 stevensc 440
                    array_push($capsules, [
17215 stevensc 441
                        'uuid' => $record->uuid,
17178 stevensc 442
                        'name' => $record->name,
17211 stevensc 443
                        'selected' => $selected
17178 stevensc 444
                    ]);
17226 stevensc 445
                } catch (\Exception $e) {
446
                    $this->logger->err('Error processing capsule in editAction GET: ' . $e->getMessage(), [
447
                        'capsule_id' => $record->id,
448
                        'topic_id' => $topic->id
449
                    ]);
450
                    // Continue processing other capsules
451
                    continue;
17178 stevensc 452
                }
17226 stevensc 453
            }
17133 stevensc 454
 
17226 stevensc 455
            $storage = Storage::getInstance($this->config, $this->adapter);
456
            $path = $storage->getPathMicrolearningTopic();
457
 
458
            $data = [
459
                'success' => true,
460
                'data' => [
461
                    'capsules' => $capsules,
462
                    'name' => $topic->name,
463
                    'description' => $topic->description,
464
                    'order' => $topic->order,
465
                    'status' => $topic->status,
466
                    'privacy' => $topic->privacy,
467
                    'type' => $topic->type,
468
                    'cost' => $topic->cost,
469
                    'image'=> $storage->getGenericImage($path, $topic->uuid, $topic->image),
470
                    'marketplace'=> $storage->getGenericImage($path, $topic->uuid, $topic->marketplace)
471
                ]
472
            ];
473
 
474
            return new JsonModel($data);
475
 
476
        } catch (\Exception $e) {
477
            $this->logger->err('Error in handleEditGetRequest: ' . $e->getMessage(), [
478
                'topic_id' => $topic->id,
479
                'topic_uuid' => $topic->uuid
480
            ]);
481
            return $this->createErrorResponse('ERROR_LOADING_TOPIC_DATA');
482
        }
483
    }
484
 
485
    /**
486
     * Handle POST request for edit action
487
     * @param \Laminas\Http\Request $request
488
     * @param \LeadersLinked\Model\MicrolearningTopic $topic
489
     * @param \LeadersLinked\Model\Company $currentCompany
490
     * @param \LeadersLinked\Model\User $currentUser
491
     * @return \Laminas\View\Model\JsonModel
492
     */
493
    private function handleEditPostRequest($request, $topic, $currentCompany, $currentUser)
494
    {
495
        try {
496
            // Validate form data
497
            $form = new TopicEditForm($this->adapter, $currentCompany->id, $currentCompany->internal);
498
            $dataPost = array_merge($request->getPost()->toArray(), $request->getFiles()->toArray());
499
 
500
            $form->setData($dataPost);
501
 
502
            if(!$form->isValid()) {
503
                $messages = [];
504
                $form_messages = (array) $form->getMessages();
505
                foreach($form_messages as $fieldname => $field_messages) {
506
                    $messages[$fieldname] = array_values($field_messages);
507
                }
17178 stevensc 508
 
17226 stevensc 509
                $this->logger->info('editAction: Form validation failed', [
510
                    'topic_uuid' => $topic->uuid,
511
                    'user_id' => $currentUser->id,
512
                    'validation_errors' => $messages
513
                ]);
17178 stevensc 514
 
17226 stevensc 515
                return $this->createErrorResponse($messages);
17133 stevensc 516
            }
517
 
17226 stevensc 518
            $dataPost = (array) $form->getData();
519
 
520
            // Update topic basic data
17228 stevensc 521
            $topic->name = $dataPost['name'];
17229 stevensc 522
            $topic->description = $dataPost['description'];
17226 stevensc 523
            $topic->order = $dataPost['order'];
17230 stevensc 524
            $topic->status = $dataPost['status'];
17226 stevensc 525
            $topic->privacy = $dataPost['privacy'];
17230 stevensc 526
            /* $topic->type = $dataPost['type'];
17227 stevensc 527
            $topic->cost = $dataPost['cost']; */
17226 stevensc 528
 
529
            // Update basic topic data first
530
            $topicMapper = MicrolearningTopicMapper::getInstance($this->adapter);
531
            if(!$topicMapper->update($topic)) {
532
                $this->logger->err('editAction: Failed to update topic basic data', [
533
                    'topic_uuid' => $topic->uuid,
534
                    'user_id' => $currentUser->id,
535
                    'mapper_error' => $topicMapper->getError()
536
                ]);
537
                return $this->createErrorResponse($topicMapper->getError());
538
            }
539
 
540
            $this->logger->info('Se actualizo el tópico ' . $topic->name, [
541
                'user_id' => $currentUser->id,
542
                'ip' => Functions::getUserIP(),
17227 stevensc 543
                'original_name' => $topic->name,
17226 stevensc 544
                'new_name' => $topic->name
545
            ]);
546
 
547
            // Process images if uploaded
548
            $imageResult = $this->processTopicImages($request, $topic);
549
            if (!$imageResult['success']) {
550
                $this->logger->err('editAction: Failed to process topic images', [
551
                    'topic_uuid' => $topic->uuid,
552
                    'user_id' => $currentUser->id,
553
                    'image_error' => $imageResult['error']
554
                ]);
555
                return $this->createErrorResponse($imageResult['error']);
556
            }
557
 
558
            // Update topic with new image filenames if they were processed
559
            if (!empty($imageResult['image_filename']) || !empty($imageResult['marketplace_filename'])) {
560
                if (!empty($imageResult['image_filename'])) {
561
                    $topic->image = $imageResult['image_filename'];
17133 stevensc 562
                }
17226 stevensc 563
                if (!empty($imageResult['marketplace_filename'])) {
564
                    $topic->marketplace = $imageResult['marketplace_filename'];
17178 stevensc 565
                }
17217 stevensc 566
 
17178 stevensc 567
                if(!$topicMapper->update($topic)) {
17226 stevensc 568
                    $this->logger->err('editAction: Failed to update topic with new images', [
569
                        'topic_uuid' => $topic->uuid,
570
                        'user_id' => $currentUser->id,
571
                        'mapper_error' => $topicMapper->getError()
572
                    ]);
17178 stevensc 573
                    return $this->createErrorResponse($topicMapper->getError());
574
                }
17225 stevensc 575
 
17226 stevensc 576
                $this->logger->info('Se actualizo la imagen del tópico ' . $topic->name, [
577
                    'user_id' => $currentUser->id,
578
                    'ip' => Functions::getUserIP(),
579
                    'has_image' => !empty($imageResult['image_filename']),
580
                    'has_marketplace' => !empty($imageResult['marketplace_filename'])
581
                ]);
582
            }
17178 stevensc 583
 
17226 stevensc 584
            // Update capsule relations
585
            $result = $this->updateTopicCapsuleRelations($topic, $dataPost['capsule_uuid'], $currentCompany->id, $currentUser);
586
            if (!$result['success']) {
587
                $this->logger->err('editAction: Failed to update capsule relations', [
588
                    'topic_uuid' => $topic->uuid,
589
                    'user_id' => $currentUser->id,
590
                    'relation_error' => $result['error']
17093 stevensc 591
                ]);
17226 stevensc 592
                return $this->createErrorResponse($result['error']);
17093 stevensc 593
            }
17226 stevensc 594
 
595
            $this->logger->info('Se edito el tópico ' . $topic->name, [
596
                'user_id' => $currentUser->id,
597
                'ip' => Functions::getUserIP(),
598
                'topic_uuid' => $topic->uuid
599
            ]);
600
 
601
            return new JsonModel([
602
                'success' => true,
603
                'data' => 'LABEL_RECORD_UPDATED'
604
            ]);
605
 
17178 stevensc 606
        } catch (\Exception $e) {
17226 stevensc 607
            $this->logger->err('Error in handleEditPostRequest: ' . $e->getMessage(), [
608
                'exception' => $e->getTraceAsString(),
609
                'topic_uuid' => $topic->uuid,
610
                'user_id' => $currentUser->id
611
            ]);
17178 stevensc 612
            return $this->createErrorResponse('ERROR_INTERNAL_SERVER_ERROR');
17093 stevensc 613
        }
1 www 614
    }
17178 stevensc 615
 
17185 stevensc 616
    /**
617
     * Obtener los usuarios de un tópico
618
     * {@inheritDoc}
619
     * @see \Laminas\Mvc\Controller\AbstractActionController::usersAction()
620
     * @return JsonModel
621
     */
622
    public function usersAction()
623
    {
624
        try {
625
            $request = $this->getRequest();
626
 
627
            $currentUserPlugin = $this->plugin('currentUserPlugin');
628
            $currentUser    = $currentUserPlugin->getUser();
629
            $currentCompany = $currentUserPlugin->getCompany();
630
 
631
            if(!$currentCompany) {
632
                return $this->createErrorResponse('ERROR_COMPANY_NOT_FOUND');
633
            }
634
 
635
            if(!$currentUser) {
636
                return $this->createErrorResponse('ERROR_USER_NOT_FOUND');
637
            }
638
 
639
            if(!$request->isGet()) {
640
                return $this->createErrorResponse('ERROR_METHOD_NOT_ALLOWED');
641
            }
642
 
643
            $topic_uuid = $this->params()->fromRoute('topic_uuid');
644
            $type = $this->params()->fromRoute('type');
645
 
646
            $topicMapper = MicrolearningTopicMapper::getInstance($this->adapter);
647
            $topic = $topicMapper->fetchOneByUuid($topic_uuid);
648
            if(!$topic) {
649
                $this->logger->err('Error in usersAction: ' . $topic_uuid);
650
                return $this->createErrorResponse('ERROR_TOPIC_NOT_FOUND');
651
            }
652
 
653
            if($topic->company_id != $currentCompany->id) {
654
                $this->logger->err('Error in usersAction: ' . $topic_uuid);
655
                return $this->createErrorResponse('ERROR_UNAUTHORIZED');
656
            }
657
 
658
            $queryMapper = QueryMapper::getInstance($this->adapter);
659
            $sql = $queryMapper->getSql();
660
            $select = $sql->select();
661
            $select->columns(['access', 'paid_from', 'paid_to', 'added_on']);
662
            $select->from(['tb1' => MicrolearningTopicUserMapper::_TABLE] );
663
            $select->join(['tb2' => UserMapper::_TABLE], 'tb1.user_id = tb2.id', ['uuid', 'first_name', 'last_name', 'email']);
664
            $select->where->equalTo('tb1.company_id', $topic->company_id);
665
            $select->where->equalTo('tb1.topic_id', $topic->id);
666
 
667
            if($type == 'active') {
668
                $now = date('Y-m-d H:i:s');
669
                $select->where->nest->equalTo('access', MicrolearningTopicUser::ACCESS_UNLIMITED)->or->nest()
670
                ->equalTo('access', MicrolearningTopicUser::ACCESS_PAY_PERIOD)
671
                ->and->lessThanOrEqualTo('paid_from', $now)->and->greaterThanOrEqualTo('paid_to', $now )->unnest()->unnest();
672
 
673
            }
674
 
675
            $select->order(['first_name', 'last_name', 'email']);
676
            $records  = $queryMapper->fetchAll($select);
677
 
678
            $items = [];
679
 
680
            foreach($records as $record)
681
            {
682
                switch($record['access'])
683
                {
684
                    case MicrolearningTopicUser::ACCESS_UNLIMITED :
685
                        $details['access'] = 'LABEL_UNLIMIT';
686
                        break;
687
 
688
                    case MicrolearningTopicUser::ACCESS_REVOKE :
689
                        $details['access'] = 'LABEL_REVOKED';
690
                        break;
691
 
692
                    case MicrolearningTopicUser::ACCESS_PAY_PERIOD :
693
                        $dt_paid_from = \DateTime::createFromFormat('Y-m-d', $record['paid_from']);
694
                        $dt_paid_to = \DateTime::createFromFormat('Y-m-d', $record['paid_to']);
695
 
696
                        $details['access'] = 'LABEL_PAY_PERIOD';
697
                        $details['paid_from'] = $dt_paid_from->format('d/m/Y');
698
                        $details['paid_to'] = $dt_paid_to->format('d/m/Y');
699
                        break;
700
 
701
                    case MicrolearningTopicUser::ACCESS_SUPENDED :
702
                        $dt_paid_from = \DateTime::createFromFormat('Y-m-d', $record['paid_from']);
703
                        $dt_paid_to = \DateTime::createFromFormat('Y-m-d', $record['paid_to']);
704
 
705
                        $details['access'] = 'LABEL_SUSPENDED';
706
                        $details['paid_from'] = $dt_paid_from->format('d/m/Y');
707
                        $details['paid_to'] = $dt_paid_to->format('d/m/Y');
708
                        break;
709
 
710
                    case MicrolearningTopicUser::ACCESS_CANCELLED :
711
                        $dt_paid_from = \DateTime::createFromFormat('Y-m-d', $record['paid_from']);
712
                        $dt_paid_to = \DateTime::createFromFormat('Y-m-d', $record['paid_to']);
713
 
714
                        $details['access'] = 'LABEL_CANCELLED';
715
                        $details['paid_from'] = $dt_paid_from->format('d/m/Y');
716
                        $details['paid_to'] = $dt_paid_to->format('d/m/Y');
717
                        break;
718
                }
719
 
720
 
721
                $item = [
722
                    'first_name' => $record['first_name'],
723
                    'last_name' => $record['last_name'],
724
                    'email' => $record['email'],
725
                    'details' => $details,
726
                ];
727
 
728
                array_push($items, $item);
729
            }
730
 
731
            return new JsonModel([
732
                'success' => true,
733
                'data' => [
734
                    'topic' => $topic->name,
735
                    'items' => $items,
736
                ]
737
            ]);
738
        } catch (\Exception $e) {
739
            $this->logger->err('Error in usersAction: ' . $e->getMessage());
740
            return $this->createErrorResponse('ERROR_INTERNAL_SERVER_ERROR');
741
        }
742
    }
743
 
17178 stevensc 744
     /**
745
     * Check if request is JSON
746
     * @param \Laminas\Http\Request $request
747
     * @return bool
1 www 748
     */
17178 stevensc 749
    private function isJsonRequest($request)
1 www 750
    {
17178 stevensc 751
        $headers = $request->getHeaders();
752
        if (!$headers->has('Accept')) {
753
            return false;
17133 stevensc 754
        }
755
 
17178 stevensc 756
        $accept = $headers->get('Accept');
757
        $prioritized = $accept->getPrioritized();
17133 stevensc 758
 
17178 stevensc 759
        foreach ($prioritized as $value) {
760
            if (strpos(trim($value->getRaw()), 'json') !== false) {
761
                return true;
762
            }
1 www 763
        }
764
 
17178 stevensc 765
        return false;
766
    }
1 www 767
 
17178 stevensc 768
    /**
769
     * Handle JSON request for datatable
770
     * @param \LeadersLinked\Model\User $currentUser
771
     * @param \LeadersLinked\Model\Company $currentCompany
772
     * @return \Laminas\View\Model\JsonModel
773
     */
774
    private function handleJsonRequest($currentUser, $currentCompany)
775
    {
776
        try {
777
            $search = $this->params()->fromQuery('search', []);
778
            $search = empty($search['value']) ? '' : Functions::sanitizeFilterString($search['value']);
779
 
780
            $recordsPerPage = intval($this->params()->fromQuery('length', 10), 10);
781
            $page = (intval($this->params()->fromQuery('start', 1), 10) / $recordsPerPage) + 1;
782
 
783
            $order = $this->params()->fromQuery('order', []);
784
            $orderField = empty($order[0]['column']) ? 99 : intval($order[0]['column'], 10);
785
            $orderDirection = empty($order[0]['dir']) ? 'ASC' : strtoupper(Functions::sanitizeFilterString($order[0]['dir']));
786
 
787
            $fields = ['name'];
788
            $orderField = isset($fields[$orderField]) ? $fields[$orderField] : 'name';
789
 
790
            if (!in_array($orderDirection, ['ASC', 'DESC'])) {
791
                $orderDirection = 'ASC';
792
            }
793
 
794
            $acl = $this->getEvent()->getViewModel()->getVariable('acl');
795
            $permissions = $this->getUserPermissions($acl, $currentUser);
796
 
797
            $topicMapper = MicrolearningTopicMapper::getInstance($this->adapter);
798
            $paginator = $topicMapper->fetchAllDataTableByCompanyId(
799
                $currentCompany->id,
800
                $search,
801
                $page,
802
                $recordsPerPage,
803
                $orderField,
804
                $orderDirection
805
            );
806
 
807
            $items = $this->prepareTopicItems($paginator->getCurrentItems(), $currentCompany, $permissions);
808
 
809
            $response = [
810
                'success' => true,
811
                'data' => [
812
                    'link_add' => $permissions['allowAdd'] ? $this->url()->fromRoute('microlearning/content/topics/add') : '',
813
                    'items' => $items,
814
                    'total' => $paginator->getTotalItemCount(),
815
                ]
816
            ];
817
 
818
            return new JsonModel($response);
819
 
820
        } catch (\Exception $e) {
821
            $this->logger->err('Error in handleJsonRequest: ' . $e->getMessage());
822
            return $this->createErrorResponse('ERROR_INTERNAL_SERVER_ERROR');
17133 stevensc 823
        }
17178 stevensc 824
    }
17133 stevensc 825
 
17178 stevensc 826
    /**
827
     * Handle HTML request for view
828
     * @param \LeadersLinked\Model\Company $currentCompany
829
     * @return \Laminas\View\Model\ViewModel
830
     */
831
    private function handleHtmlRequest($currentCompany)
832
    {
833
        $imageSize = $this->config['leaderslinked.image_sizes.microlearning_image_upload'];
834
        $marketplaceSize = $this->config['leaderslinked.image_sizes.marketplace'];
1 www 835
 
17178 stevensc 836
        $formAdd = new TopicAddForm($this->adapter, $currentCompany->id, $currentCompany->internal);
837
        $formEdit = new TopicEditForm($this->adapter, $currentCompany->id, $currentCompany->internal);
17002 efrain 838
 
17178 stevensc 839
        $this->layout()->setTemplate('layout/layout-backend.phtml');
840
        $viewModel = new ViewModel();
841
        $viewModel->setTemplate('leaders-linked/microlearning-topics/index.phtml');
842
        $viewModel->setVariables([
843
            'formAdd' => $formAdd,
844
            'formEdit' => $formEdit,
845
            'company_uuid' => $currentCompany->uuid,
846
            'image_size' => $imageSize,
847
            'marketplace_size' => $marketplaceSize,
17133 stevensc 848
        ]);
1 www 849
 
17178 stevensc 850
        return $viewModel;
851
    }
852
 
853
    /**
854
     * Get user permissions for topic actions
855
     * @param \Laminas\Permissions\Acl\Acl $acl
856
     * @param \LeadersLinked\Model\User $currentUser
857
     * @return array
858
     */
859
    private function getUserPermissions($acl, $currentUser)
860
    {
861
        return [
862
            'allowAdd' => $acl->isAllowed($currentUser->usertype_id, 'microlearning/content/topics/add'),
863
            'allowEdit' => $acl->isAllowed($currentUser->usertype_id, 'microlearning/content/topics/edit'),
864
            'allowDelete' => $acl->isAllowed($currentUser->usertype_id, 'microlearning/content/topics/delete'),
865
            'allowUsers' => $acl->isAllowed($currentUser->usertype_id, 'microlearning/content/topics/users')
866
        ];
867
    }
868
 
869
    /**
870
     * Create error response
871
     * @param string $message
872
     * @return \Laminas\View\Model\JsonModel
873
     */
874
    private function createErrorResponse($message)
875
    {
17133 stevensc 876
        return new JsonModel([
17178 stevensc 877
            'success' => false,
878
            'data' => $message
17133 stevensc 879
        ]);
1 www 880
    }
881
 
17178 stevensc 882
     /**
883
     * Prepare topic items for datatable
884
     * @param array $records
885
     * @param \LeadersLinked\Model\Company $currentCompany
886
     * @param array $permissions
887
     * @return array
888
     */
889
    private function prepareTopicItems($records, $currentCompany, $permissions)
1 www 890
    {
17178 stevensc 891
        $items = [];
892
        $microlearningTopicUserMapper = MicrolearningTopicUserMapper::getInstance($this->adapter);
893
        $storage = Storage::getInstance($this->config, $this->adapter);
894
        $path = $storage->getPathMicrolearningTopic();
17109 stevensc 895
 
17178 stevensc 896
        foreach ($records as $record) {
897
            $totalUsers = $microlearningTopicUserMapper->fetchCountUsersByCompanyIdAndTopicId($currentCompany->id, $record->id);
898
            $totalUsersActive = $microlearningTopicUserMapper->fetchCountUsersActiveByCompanyIdAndTopicId($currentCompany->id, $record->id);
899
 
900
            $status = $this->getTopicStatus($record->status);
901
            $privacy = $this->getTopicPrivacy($record->privacy);
902
            $type = $this->getTopicType($record->type);
903
 
904
            $items[] = [
905
                'name' => $record->name,
906
                'details' => [
907
                    'status' => $status,
908
                    'privacy' => $privacy,
909
                    'type' => $type,
910
                    'cost' => $record->cost,
911
                    'total_users' => $totalUsers,
912
                    'total_users_active' => $totalUsersActive,
913
                ],
914
                'images' => [
915
                    'image' => $storage->getGenericImage($path, $record->uuid, $record->image),
17195 stevensc 916
                    'marketplace' => $storage->getGenericImage($path, $record->uuid, $record->marketplace)
17178 stevensc 917
                ],
918
                'actions' => $this->prepareTopicActions($record, $permissions, $totalUsers, $totalUsersActive)
919
            ];
1 www 920
        }
17178 stevensc 921
 
922
        return $items;
923
    }
924
 
925
        /**
926
     * Get topic status label
927
     * @param int $status
928
     * @return string
929
     */
930
    private function getTopicStatus($status)
931
    {
932
        switch ($status) {
933
            case MicrolearningTopic::STATUS_ACTIVE:
934
                return 'LABEL_ACTIVE';
935
            case MicrolearningTopic::STATUS_INACTIVE:
936
                return 'LABEL_INACTIVE';
937
            default:
938
                return '';
1 www 939
        }
17178 stevensc 940
    }
941
 
942
    /**
943
     * Get topic privacy label
944
     * @param int $privacy
945
     * @return string
946
     */
947
    private function getTopicPrivacy($privacy)
948
    {
949
        switch ($privacy) {
950
            case MicrolearningTopic::PRIVACY_PUBLIC:
951
                return 'LABEL_PUBLIC';
952
            case MicrolearningTopic::PRIVACY_PRIVATE:
953
                return 'LABEL_PRIVATE';
954
            default:
955
                return '';
1 www 956
        }
957
    }
17178 stevensc 958
 
959
    /**
960
     * Get topic type label
961
     * @param int $type
962
     * @return string
963
     */
964
    private function getTopicType($type)
965
    {
966
        switch ($type) {
967
            case MicrolearningTopic::TYPE_FREE:
968
                return 'LABEL_FREE';
969
            case MicrolearningTopic::TYPE_PRIVATE:
970
                return 'LABEL_PRIVATE';
971
            case MicrolearningTopic::TYPE_SELLING:
972
                return 'LABEL_SELLING';
973
            default:
974
                return '';
975
        }
976
    }
977
 
978
     /**
979
     * Prepare topic actions
980
     * @param \LeadersLinked\Model\MicrolearningTopic $record
981
     * @param array $permissions
982
     * @param int $totalUsers
983
     * @param int $totalUsersActive
984
     * @return array
985
     */
986
    private function prepareTopicActions($record, $permissions, $totalUsers, $totalUsersActive)
987
    {
17188 stevensc 988
        $editDeleteParams = ['id' => $record->uuid];
17178 stevensc 989
 
990
        return [
17188 stevensc 991
            'link_edit' => $permissions['allowEdit'] ? $this->url()->fromRoute('microlearning/content/topics/edit', $editDeleteParams) : '',
992
            'link_delete' => $permissions['allowDelete'] ? $this->url()->fromRoute('microlearning/content/topics/delete', $editDeleteParams) : '',
17178 stevensc 993
            'link_total_users' => $permissions['allowUsers'] && $totalUsers ?
994
                $this->url()->fromRoute('microlearning/content/topics/users', [
995
                    'topic_uuid' => $record->uuid,
996
                    'type' => 'all'
997
                ]) : '',
998
            'link_total_users_actives' => $permissions['allowUsers'] && $totalUsersActive ?
999
                $this->url()->fromRoute('microlearning/content/topics/users', [
1000
                    'topic_uuid' => $record->uuid,
1001
                    'type' => 'active'
1002
                ]) : ''
1003
        ];
1004
    }
17214 stevensc 1005
 
1006
    /**
1007
     * Update topic capsule relations
1008
     * @param \LeadersLinked\Model\MicrolearningTopic $topic
1009
     * @param array $capsuleUuids
1010
     * @param int $companyId
17224 stevensc 1011
     * @param \LeadersLinked\Model\User $currentUser
17214 stevensc 1012
     * @return array
1013
     */
17224 stevensc 1014
    private function updateTopicCapsuleRelations($topic, $capsuleUuids, $companyId, $currentUser)
17214 stevensc 1015
    {
1016
        try {
17224 stevensc 1017
            // Ensure capsuleUuids is an array
1018
            if (!is_array($capsuleUuids)) {
1019
                $capsuleUuids = [];
1020
            }
1021
 
17214 stevensc 1022
            $topicCapsuleMapper = MicrolearningTopicCapsuleMapper::getInstance($this->adapter);
1023
            $topicCapsuleMapper->deleteByTopicId($topic->id);
1024
 
1025
            $capsuleMapper = MicrolearningCapsuleMapper::getInstance($this->adapter);
1026
            foreach ($capsuleUuids as $capsuleUuid) {
1027
                $capsule = $capsuleMapper->fetchOneByUuid($capsuleUuid);
1028
 
1029
                if ($capsule) {
1030
                    $topicCapsule = new MicrolearningTopicCapsule();
1031
                    $topicCapsule->topic_id = $topic->id;
1032
                    $topicCapsule->capsule_id = $capsule->id;
1033
                    $topicCapsule->company_id = $companyId;
1034
                    $topicCapsuleMapper->insert($topicCapsule);
1035
                }
1036
            }
17224 stevensc 1037
 
1038
            $this->logger->info('Se actualizo la relacion de cápsulas del tópico ' . $topic->name, ['user_id' => $currentUser->id, 'ip' => Functions::getUserIP()]);
17214 stevensc 1039
            return ['success' => true];
1040
        } catch (\Exception $e) {
17224 stevensc 1041
            $this->logger->err('Error in updateTopicCapsuleRelations: ' . $e->getMessage());
17214 stevensc 1042
            return ['success' => false, 'error' => $e->getMessage()];
1043
        }
1044
    }
1045
 
1046
    /**
1047
     * Process topic images
1048
     * @param \Laminas\Http\Request $request
1049
     * @param \LeadersLinked\Model\MicrolearningTopic $topic
1050
     * @return array
1051
     */
1052
    private function processTopicImages($request, $topic)
1053
    {
1054
        try {
1055
            $storage = Storage::getInstance($this->config, $this->adapter);
1056
            $target_path = $storage->getPathMicrolearningTopic();
1057
            $storage->setFiles($request->getFiles()->toArray());
1058
 
1059
            $result = [
1060
                'success' => true,
1061
                'image_filename' => '',
1062
                'marketplace_filename' => ''
1063
            ];
1064
 
1065
            // Process main image if uploaded
17219 stevensc 1066
            if ($storage->setCurrentFilename('image')) {
17214 stevensc 1067
                $target_size = $this->config['leaderslinked.image_sizes.microlearning_image_size'];
1068
                list($target_width, $target_height) = explode('x', $target_size);
1069
 
1070
                $image_source_filename = $storage->getTmpFilename();
1071
                $image_filename = 'topic-' . uniqid() . '.png';
1072
                $image_target_filename = $storage->composePathToFilename(
1073
                    Storage::TYPE_MICROLEARNING_TOPIC,
1074
                    $topic->uuid,
1075
                    $image_filename
1076
                );
1077
 
1078
                if (!$storage->uploadImageCrop($image_source_filename, $image_target_filename, $target_width, $target_height)) {
1079
                    return ['success' => false, 'error' => 'ERROR_UPLOAD_IMAGE'];
1080
                }
1081
 
1082
                // Delete old image if exists
1083
                if ($topic->image) {
1084
                    $storage->deleteFile($target_path, $topic->uuid, $topic->image);
1085
                }
1086
 
1087
                $result['image_filename'] = $image_filename;
1088
            }
1089
 
1090
            // Process marketplace image if uploaded
1091
            if ($storage->setCurrentFilename('marketplace')) {
1092
                $target_size = $this->config['leaderslinked.image_sizes.microlearning_image_size'];
1093
                list($target_width, $target_height) = explode('x', $target_size);
1094
 
1095
                $marketplace_source_filename = $storage->getTmpFilename();
1096
                $marketplace_filename = 'marketplace-' . uniqid() . '.png';
1097
                $marketplace_target_filename = $storage->composePathToFilename(
1098
                    Storage::TYPE_MICROLEARNING_TOPIC,
1099
                    $topic->uuid,
1100
                    $marketplace_filename
1101
                );
1102
 
1103
                if (!$storage->uploadImageCrop($marketplace_source_filename, $marketplace_target_filename, $target_width, $target_height)) {
1104
                    return ['success' => false, 'error' => 'ERROR_UPLOAD_IMAGE'];
1105
                }
1106
 
1107
                // Delete old marketplace image if exists
1108
                if ($topic->marketplace) {
1109
                    $storage->deleteFile($target_path, $topic->uuid, $topic->marketplace);
1110
                }
1111
 
1112
                $result['marketplace_filename'] = $marketplace_filename;
1113
            }
1114
 
1115
            return $result;
1116
        } catch (\Exception $e) {
1117
            return ['success' => false, 'error' => $e->getMessage()];
1118
        }
1119
    }
1 www 1120
}