Proyectos de Subversion LeadersLinked - Backend

Rev

Rev 17225 | Rev 17227 | 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
521
            $originalName = $topic->name;
522
            $topic->name = $dataPost['name'];
523
            $topic->description = $dataPost['description'];
524
            $topic->order = $dataPost['order'];
525
            $topic->status = $dataPost['status'];
526
            $topic->privacy = $dataPost['privacy'];
527
            $topic->type = $dataPost['type'];
528
            $topic->cost = $dataPost['cost'];
529
 
530
            // Update basic topic data first
531
            $topicMapper = MicrolearningTopicMapper::getInstance($this->adapter);
532
            if(!$topicMapper->update($topic)) {
533
                $this->logger->err('editAction: Failed to update topic basic data', [
534
                    'topic_uuid' => $topic->uuid,
535
                    'user_id' => $currentUser->id,
536
                    'mapper_error' => $topicMapper->getError()
537
                ]);
538
                return $this->createErrorResponse($topicMapper->getError());
539
            }
540
 
541
            $this->logger->info('Se actualizo el tópico ' . $topic->name, [
542
                'user_id' => $currentUser->id,
543
                'ip' => Functions::getUserIP(),
544
                'original_name' => $originalName,
545
                'new_name' => $topic->name
546
            ]);
547
 
548
            // Process images if uploaded
549
            $imageResult = $this->processTopicImages($request, $topic);
550
            if (!$imageResult['success']) {
551
                $this->logger->err('editAction: Failed to process topic images', [
552
                    'topic_uuid' => $topic->uuid,
553
                    'user_id' => $currentUser->id,
554
                    'image_error' => $imageResult['error']
555
                ]);
556
                return $this->createErrorResponse($imageResult['error']);
557
            }
558
 
559
            // Update topic with new image filenames if they were processed
560
            if (!empty($imageResult['image_filename']) || !empty($imageResult['marketplace_filename'])) {
561
                if (!empty($imageResult['image_filename'])) {
562
                    $topic->image = $imageResult['image_filename'];
17133 stevensc 563
                }
17226 stevensc 564
                if (!empty($imageResult['marketplace_filename'])) {
565
                    $topic->marketplace = $imageResult['marketplace_filename'];
17178 stevensc 566
                }
17217 stevensc 567
 
17178 stevensc 568
                if(!$topicMapper->update($topic)) {
17226 stevensc 569
                    $this->logger->err('editAction: Failed to update topic with new images', [
570
                        'topic_uuid' => $topic->uuid,
571
                        'user_id' => $currentUser->id,
572
                        'mapper_error' => $topicMapper->getError()
573
                    ]);
17178 stevensc 574
                    return $this->createErrorResponse($topicMapper->getError());
575
                }
17225 stevensc 576
 
17226 stevensc 577
                $this->logger->info('Se actualizo la imagen del tópico ' . $topic->name, [
578
                    'user_id' => $currentUser->id,
579
                    'ip' => Functions::getUserIP(),
580
                    'has_image' => !empty($imageResult['image_filename']),
581
                    'has_marketplace' => !empty($imageResult['marketplace_filename'])
582
                ]);
583
            }
17178 stevensc 584
 
17226 stevensc 585
            // Update capsule relations
586
            $result = $this->updateTopicCapsuleRelations($topic, $dataPost['capsule_uuid'], $currentCompany->id, $currentUser);
587
            if (!$result['success']) {
588
                $this->logger->err('editAction: Failed to update capsule relations', [
589
                    'topic_uuid' => $topic->uuid,
590
                    'user_id' => $currentUser->id,
591
                    'relation_error' => $result['error']
17093 stevensc 592
                ]);
17226 stevensc 593
                return $this->createErrorResponse($result['error']);
17093 stevensc 594
            }
17226 stevensc 595
 
596
            $this->logger->info('Se edito el tópico ' . $topic->name, [
597
                'user_id' => $currentUser->id,
598
                'ip' => Functions::getUserIP(),
599
                'topic_uuid' => $topic->uuid
600
            ]);
601
 
602
            return new JsonModel([
603
                'success' => true,
604
                'data' => 'LABEL_RECORD_UPDATED'
605
            ]);
606
 
17178 stevensc 607
        } catch (\Exception $e) {
17226 stevensc 608
            $this->logger->err('Error in handleEditPostRequest: ' . $e->getMessage(), [
609
                'exception' => $e->getTraceAsString(),
610
                'topic_uuid' => $topic->uuid,
611
                'user_id' => $currentUser->id
612
            ]);
17178 stevensc 613
            return $this->createErrorResponse('ERROR_INTERNAL_SERVER_ERROR');
17093 stevensc 614
        }
1 www 615
    }
17178 stevensc 616
 
17185 stevensc 617
    /**
618
     * Obtener los usuarios de un tópico
619
     * {@inheritDoc}
620
     * @see \Laminas\Mvc\Controller\AbstractActionController::usersAction()
621
     * @return JsonModel
622
     */
623
    public function usersAction()
624
    {
625
        try {
626
            $request = $this->getRequest();
627
 
628
            $currentUserPlugin = $this->plugin('currentUserPlugin');
629
            $currentUser    = $currentUserPlugin->getUser();
630
            $currentCompany = $currentUserPlugin->getCompany();
631
 
632
            if(!$currentCompany) {
633
                return $this->createErrorResponse('ERROR_COMPANY_NOT_FOUND');
634
            }
635
 
636
            if(!$currentUser) {
637
                return $this->createErrorResponse('ERROR_USER_NOT_FOUND');
638
            }
639
 
640
            if(!$request->isGet()) {
641
                return $this->createErrorResponse('ERROR_METHOD_NOT_ALLOWED');
642
            }
643
 
644
            $topic_uuid = $this->params()->fromRoute('topic_uuid');
645
            $type = $this->params()->fromRoute('type');
646
 
647
            $topicMapper = MicrolearningTopicMapper::getInstance($this->adapter);
648
            $topic = $topicMapper->fetchOneByUuid($topic_uuid);
649
            if(!$topic) {
650
                $this->logger->err('Error in usersAction: ' . $topic_uuid);
651
                return $this->createErrorResponse('ERROR_TOPIC_NOT_FOUND');
652
            }
653
 
654
            if($topic->company_id != $currentCompany->id) {
655
                $this->logger->err('Error in usersAction: ' . $topic_uuid);
656
                return $this->createErrorResponse('ERROR_UNAUTHORIZED');
657
            }
658
 
659
            $queryMapper = QueryMapper::getInstance($this->adapter);
660
            $sql = $queryMapper->getSql();
661
            $select = $sql->select();
662
            $select->columns(['access', 'paid_from', 'paid_to', 'added_on']);
663
            $select->from(['tb1' => MicrolearningTopicUserMapper::_TABLE] );
664
            $select->join(['tb2' => UserMapper::_TABLE], 'tb1.user_id = tb2.id', ['uuid', 'first_name', 'last_name', 'email']);
665
            $select->where->equalTo('tb1.company_id', $topic->company_id);
666
            $select->where->equalTo('tb1.topic_id', $topic->id);
667
 
668
            if($type == 'active') {
669
                $now = date('Y-m-d H:i:s');
670
                $select->where->nest->equalTo('access', MicrolearningTopicUser::ACCESS_UNLIMITED)->or->nest()
671
                ->equalTo('access', MicrolearningTopicUser::ACCESS_PAY_PERIOD)
672
                ->and->lessThanOrEqualTo('paid_from', $now)->and->greaterThanOrEqualTo('paid_to', $now )->unnest()->unnest();
673
 
674
            }
675
 
676
            $select->order(['first_name', 'last_name', 'email']);
677
            $records  = $queryMapper->fetchAll($select);
678
 
679
            $items = [];
680
 
681
            foreach($records as $record)
682
            {
683
                switch($record['access'])
684
                {
685
                    case MicrolearningTopicUser::ACCESS_UNLIMITED :
686
                        $details['access'] = 'LABEL_UNLIMIT';
687
                        break;
688
 
689
                    case MicrolearningTopicUser::ACCESS_REVOKE :
690
                        $details['access'] = 'LABEL_REVOKED';
691
                        break;
692
 
693
                    case MicrolearningTopicUser::ACCESS_PAY_PERIOD :
694
                        $dt_paid_from = \DateTime::createFromFormat('Y-m-d', $record['paid_from']);
695
                        $dt_paid_to = \DateTime::createFromFormat('Y-m-d', $record['paid_to']);
696
 
697
                        $details['access'] = 'LABEL_PAY_PERIOD';
698
                        $details['paid_from'] = $dt_paid_from->format('d/m/Y');
699
                        $details['paid_to'] = $dt_paid_to->format('d/m/Y');
700
                        break;
701
 
702
                    case MicrolearningTopicUser::ACCESS_SUPENDED :
703
                        $dt_paid_from = \DateTime::createFromFormat('Y-m-d', $record['paid_from']);
704
                        $dt_paid_to = \DateTime::createFromFormat('Y-m-d', $record['paid_to']);
705
 
706
                        $details['access'] = 'LABEL_SUSPENDED';
707
                        $details['paid_from'] = $dt_paid_from->format('d/m/Y');
708
                        $details['paid_to'] = $dt_paid_to->format('d/m/Y');
709
                        break;
710
 
711
                    case MicrolearningTopicUser::ACCESS_CANCELLED :
712
                        $dt_paid_from = \DateTime::createFromFormat('Y-m-d', $record['paid_from']);
713
                        $dt_paid_to = \DateTime::createFromFormat('Y-m-d', $record['paid_to']);
714
 
715
                        $details['access'] = 'LABEL_CANCELLED';
716
                        $details['paid_from'] = $dt_paid_from->format('d/m/Y');
717
                        $details['paid_to'] = $dt_paid_to->format('d/m/Y');
718
                        break;
719
                }
720
 
721
 
722
                $item = [
723
                    'first_name' => $record['first_name'],
724
                    'last_name' => $record['last_name'],
725
                    'email' => $record['email'],
726
                    'details' => $details,
727
                ];
728
 
729
                array_push($items, $item);
730
            }
731
 
732
            return new JsonModel([
733
                'success' => true,
734
                'data' => [
735
                    'topic' => $topic->name,
736
                    'items' => $items,
737
                ]
738
            ]);
739
        } catch (\Exception $e) {
740
            $this->logger->err('Error in usersAction: ' . $e->getMessage());
741
            return $this->createErrorResponse('ERROR_INTERNAL_SERVER_ERROR');
742
        }
743
    }
744
 
17178 stevensc 745
     /**
746
     * Check if request is JSON
747
     * @param \Laminas\Http\Request $request
748
     * @return bool
1 www 749
     */
17178 stevensc 750
    private function isJsonRequest($request)
1 www 751
    {
17178 stevensc 752
        $headers = $request->getHeaders();
753
        if (!$headers->has('Accept')) {
754
            return false;
17133 stevensc 755
        }
756
 
17178 stevensc 757
        $accept = $headers->get('Accept');
758
        $prioritized = $accept->getPrioritized();
17133 stevensc 759
 
17178 stevensc 760
        foreach ($prioritized as $value) {
761
            if (strpos(trim($value->getRaw()), 'json') !== false) {
762
                return true;
763
            }
1 www 764
        }
765
 
17178 stevensc 766
        return false;
767
    }
1 www 768
 
17178 stevensc 769
    /**
770
     * Handle JSON request for datatable
771
     * @param \LeadersLinked\Model\User $currentUser
772
     * @param \LeadersLinked\Model\Company $currentCompany
773
     * @return \Laminas\View\Model\JsonModel
774
     */
775
    private function handleJsonRequest($currentUser, $currentCompany)
776
    {
777
        try {
778
            $search = $this->params()->fromQuery('search', []);
779
            $search = empty($search['value']) ? '' : Functions::sanitizeFilterString($search['value']);
780
 
781
            $recordsPerPage = intval($this->params()->fromQuery('length', 10), 10);
782
            $page = (intval($this->params()->fromQuery('start', 1), 10) / $recordsPerPage) + 1;
783
 
784
            $order = $this->params()->fromQuery('order', []);
785
            $orderField = empty($order[0]['column']) ? 99 : intval($order[0]['column'], 10);
786
            $orderDirection = empty($order[0]['dir']) ? 'ASC' : strtoupper(Functions::sanitizeFilterString($order[0]['dir']));
787
 
788
            $fields = ['name'];
789
            $orderField = isset($fields[$orderField]) ? $fields[$orderField] : 'name';
790
 
791
            if (!in_array($orderDirection, ['ASC', 'DESC'])) {
792
                $orderDirection = 'ASC';
793
            }
794
 
795
            $acl = $this->getEvent()->getViewModel()->getVariable('acl');
796
            $permissions = $this->getUserPermissions($acl, $currentUser);
797
 
798
            $topicMapper = MicrolearningTopicMapper::getInstance($this->adapter);
799
            $paginator = $topicMapper->fetchAllDataTableByCompanyId(
800
                $currentCompany->id,
801
                $search,
802
                $page,
803
                $recordsPerPage,
804
                $orderField,
805
                $orderDirection
806
            );
807
 
808
            $items = $this->prepareTopicItems($paginator->getCurrentItems(), $currentCompany, $permissions);
809
 
810
            $response = [
811
                'success' => true,
812
                'data' => [
813
                    'link_add' => $permissions['allowAdd'] ? $this->url()->fromRoute('microlearning/content/topics/add') : '',
814
                    'items' => $items,
815
                    'total' => $paginator->getTotalItemCount(),
816
                ]
817
            ];
818
 
819
            return new JsonModel($response);
820
 
821
        } catch (\Exception $e) {
822
            $this->logger->err('Error in handleJsonRequest: ' . $e->getMessage());
823
            return $this->createErrorResponse('ERROR_INTERNAL_SERVER_ERROR');
17133 stevensc 824
        }
17178 stevensc 825
    }
17133 stevensc 826
 
17178 stevensc 827
    /**
828
     * Handle HTML request for view
829
     * @param \LeadersLinked\Model\Company $currentCompany
830
     * @return \Laminas\View\Model\ViewModel
831
     */
832
    private function handleHtmlRequest($currentCompany)
833
    {
834
        $imageSize = $this->config['leaderslinked.image_sizes.microlearning_image_upload'];
835
        $marketplaceSize = $this->config['leaderslinked.image_sizes.marketplace'];
1 www 836
 
17178 stevensc 837
        $formAdd = new TopicAddForm($this->adapter, $currentCompany->id, $currentCompany->internal);
838
        $formEdit = new TopicEditForm($this->adapter, $currentCompany->id, $currentCompany->internal);
17002 efrain 839
 
17178 stevensc 840
        $this->layout()->setTemplate('layout/layout-backend.phtml');
841
        $viewModel = new ViewModel();
842
        $viewModel->setTemplate('leaders-linked/microlearning-topics/index.phtml');
843
        $viewModel->setVariables([
844
            'formAdd' => $formAdd,
845
            'formEdit' => $formEdit,
846
            'company_uuid' => $currentCompany->uuid,
847
            'image_size' => $imageSize,
848
            'marketplace_size' => $marketplaceSize,
17133 stevensc 849
        ]);
1 www 850
 
17178 stevensc 851
        return $viewModel;
852
    }
853
 
854
    /**
855
     * Get user permissions for topic actions
856
     * @param \Laminas\Permissions\Acl\Acl $acl
857
     * @param \LeadersLinked\Model\User $currentUser
858
     * @return array
859
     */
860
    private function getUserPermissions($acl, $currentUser)
861
    {
862
        return [
863
            'allowAdd' => $acl->isAllowed($currentUser->usertype_id, 'microlearning/content/topics/add'),
864
            'allowEdit' => $acl->isAllowed($currentUser->usertype_id, 'microlearning/content/topics/edit'),
865
            'allowDelete' => $acl->isAllowed($currentUser->usertype_id, 'microlearning/content/topics/delete'),
866
            'allowUsers' => $acl->isAllowed($currentUser->usertype_id, 'microlearning/content/topics/users')
867
        ];
868
    }
869
 
870
    /**
871
     * Create error response
872
     * @param string $message
873
     * @return \Laminas\View\Model\JsonModel
874
     */
875
    private function createErrorResponse($message)
876
    {
17133 stevensc 877
        return new JsonModel([
17178 stevensc 878
            'success' => false,
879
            'data' => $message
17133 stevensc 880
        ]);
1 www 881
    }
882
 
17178 stevensc 883
     /**
884
     * Prepare topic items for datatable
885
     * @param array $records
886
     * @param \LeadersLinked\Model\Company $currentCompany
887
     * @param array $permissions
888
     * @return array
889
     */
890
    private function prepareTopicItems($records, $currentCompany, $permissions)
1 www 891
    {
17178 stevensc 892
        $items = [];
893
        $microlearningTopicUserMapper = MicrolearningTopicUserMapper::getInstance($this->adapter);
894
        $storage = Storage::getInstance($this->config, $this->adapter);
895
        $path = $storage->getPathMicrolearningTopic();
17109 stevensc 896
 
17178 stevensc 897
        foreach ($records as $record) {
898
            $totalUsers = $microlearningTopicUserMapper->fetchCountUsersByCompanyIdAndTopicId($currentCompany->id, $record->id);
899
            $totalUsersActive = $microlearningTopicUserMapper->fetchCountUsersActiveByCompanyIdAndTopicId($currentCompany->id, $record->id);
900
 
901
            $status = $this->getTopicStatus($record->status);
902
            $privacy = $this->getTopicPrivacy($record->privacy);
903
            $type = $this->getTopicType($record->type);
904
 
905
            $items[] = [
906
                'name' => $record->name,
907
                'details' => [
908
                    'status' => $status,
909
                    'privacy' => $privacy,
910
                    'type' => $type,
911
                    'cost' => $record->cost,
912
                    'total_users' => $totalUsers,
913
                    'total_users_active' => $totalUsersActive,
914
                ],
915
                'images' => [
916
                    'image' => $storage->getGenericImage($path, $record->uuid, $record->image),
17195 stevensc 917
                    'marketplace' => $storage->getGenericImage($path, $record->uuid, $record->marketplace)
17178 stevensc 918
                ],
919
                'actions' => $this->prepareTopicActions($record, $permissions, $totalUsers, $totalUsersActive)
920
            ];
1 www 921
        }
17178 stevensc 922
 
923
        return $items;
924
    }
925
 
926
        /**
927
     * Get topic status label
928
     * @param int $status
929
     * @return string
930
     */
931
    private function getTopicStatus($status)
932
    {
933
        switch ($status) {
934
            case MicrolearningTopic::STATUS_ACTIVE:
935
                return 'LABEL_ACTIVE';
936
            case MicrolearningTopic::STATUS_INACTIVE:
937
                return 'LABEL_INACTIVE';
938
            default:
939
                return '';
1 www 940
        }
17178 stevensc 941
    }
942
 
943
    /**
944
     * Get topic privacy label
945
     * @param int $privacy
946
     * @return string
947
     */
948
    private function getTopicPrivacy($privacy)
949
    {
950
        switch ($privacy) {
951
            case MicrolearningTopic::PRIVACY_PUBLIC:
952
                return 'LABEL_PUBLIC';
953
            case MicrolearningTopic::PRIVACY_PRIVATE:
954
                return 'LABEL_PRIVATE';
955
            default:
956
                return '';
1 www 957
        }
958
    }
17178 stevensc 959
 
960
    /**
961
     * Get topic type label
962
     * @param int $type
963
     * @return string
964
     */
965
    private function getTopicType($type)
966
    {
967
        switch ($type) {
968
            case MicrolearningTopic::TYPE_FREE:
969
                return 'LABEL_FREE';
970
            case MicrolearningTopic::TYPE_PRIVATE:
971
                return 'LABEL_PRIVATE';
972
            case MicrolearningTopic::TYPE_SELLING:
973
                return 'LABEL_SELLING';
974
            default:
975
                return '';
976
        }
977
    }
978
 
979
     /**
980
     * Prepare topic actions
981
     * @param \LeadersLinked\Model\MicrolearningTopic $record
982
     * @param array $permissions
983
     * @param int $totalUsers
984
     * @param int $totalUsersActive
985
     * @return array
986
     */
987
    private function prepareTopicActions($record, $permissions, $totalUsers, $totalUsersActive)
988
    {
17188 stevensc 989
        $editDeleteParams = ['id' => $record->uuid];
17178 stevensc 990
 
991
        return [
17188 stevensc 992
            'link_edit' => $permissions['allowEdit'] ? $this->url()->fromRoute('microlearning/content/topics/edit', $editDeleteParams) : '',
993
            'link_delete' => $permissions['allowDelete'] ? $this->url()->fromRoute('microlearning/content/topics/delete', $editDeleteParams) : '',
17178 stevensc 994
            'link_total_users' => $permissions['allowUsers'] && $totalUsers ?
995
                $this->url()->fromRoute('microlearning/content/topics/users', [
996
                    'topic_uuid' => $record->uuid,
997
                    'type' => 'all'
998
                ]) : '',
999
            'link_total_users_actives' => $permissions['allowUsers'] && $totalUsersActive ?
1000
                $this->url()->fromRoute('microlearning/content/topics/users', [
1001
                    'topic_uuid' => $record->uuid,
1002
                    'type' => 'active'
1003
                ]) : ''
1004
        ];
1005
    }
17214 stevensc 1006
 
1007
    /**
1008
     * Update topic capsule relations
1009
     * @param \LeadersLinked\Model\MicrolearningTopic $topic
1010
     * @param array $capsuleUuids
1011
     * @param int $companyId
17224 stevensc 1012
     * @param \LeadersLinked\Model\User $currentUser
17214 stevensc 1013
     * @return array
1014
     */
17224 stevensc 1015
    private function updateTopicCapsuleRelations($topic, $capsuleUuids, $companyId, $currentUser)
17214 stevensc 1016
    {
1017
        try {
17224 stevensc 1018
            // Ensure capsuleUuids is an array
1019
            if (!is_array($capsuleUuids)) {
1020
                $capsuleUuids = [];
1021
            }
1022
 
17214 stevensc 1023
            $topicCapsuleMapper = MicrolearningTopicCapsuleMapper::getInstance($this->adapter);
1024
            $topicCapsuleMapper->deleteByTopicId($topic->id);
1025
 
1026
            $capsuleMapper = MicrolearningCapsuleMapper::getInstance($this->adapter);
1027
            foreach ($capsuleUuids as $capsuleUuid) {
1028
                $capsule = $capsuleMapper->fetchOneByUuid($capsuleUuid);
1029
 
1030
                if ($capsule) {
1031
                    $topicCapsule = new MicrolearningTopicCapsule();
1032
                    $topicCapsule->topic_id = $topic->id;
1033
                    $topicCapsule->capsule_id = $capsule->id;
1034
                    $topicCapsule->company_id = $companyId;
1035
                    $topicCapsuleMapper->insert($topicCapsule);
1036
                }
1037
            }
17224 stevensc 1038
 
1039
            $this->logger->info('Se actualizo la relacion de cápsulas del tópico ' . $topic->name, ['user_id' => $currentUser->id, 'ip' => Functions::getUserIP()]);
17214 stevensc 1040
            return ['success' => true];
1041
        } catch (\Exception $e) {
17224 stevensc 1042
            $this->logger->err('Error in updateTopicCapsuleRelations: ' . $e->getMessage());
17214 stevensc 1043
            return ['success' => false, 'error' => $e->getMessage()];
1044
        }
1045
    }
1046
 
1047
    /**
1048
     * Process topic images
1049
     * @param \Laminas\Http\Request $request
1050
     * @param \LeadersLinked\Model\MicrolearningTopic $topic
1051
     * @return array
1052
     */
1053
    private function processTopicImages($request, $topic)
1054
    {
1055
        try {
1056
            $storage = Storage::getInstance($this->config, $this->adapter);
1057
            $target_path = $storage->getPathMicrolearningTopic();
1058
            $storage->setFiles($request->getFiles()->toArray());
1059
 
1060
            $result = [
1061
                'success' => true,
1062
                'image_filename' => '',
1063
                'marketplace_filename' => ''
1064
            ];
1065
 
1066
            // Process main image if uploaded
17219 stevensc 1067
            if ($storage->setCurrentFilename('image')) {
17214 stevensc 1068
                $target_size = $this->config['leaderslinked.image_sizes.microlearning_image_size'];
1069
                list($target_width, $target_height) = explode('x', $target_size);
1070
 
1071
                $image_source_filename = $storage->getTmpFilename();
1072
                $image_filename = 'topic-' . uniqid() . '.png';
1073
                $image_target_filename = $storage->composePathToFilename(
1074
                    Storage::TYPE_MICROLEARNING_TOPIC,
1075
                    $topic->uuid,
1076
                    $image_filename
1077
                );
1078
 
1079
                if (!$storage->uploadImageCrop($image_source_filename, $image_target_filename, $target_width, $target_height)) {
1080
                    return ['success' => false, 'error' => 'ERROR_UPLOAD_IMAGE'];
1081
                }
1082
 
1083
                // Delete old image if exists
1084
                if ($topic->image) {
1085
                    $storage->deleteFile($target_path, $topic->uuid, $topic->image);
1086
                }
1087
 
1088
                $result['image_filename'] = $image_filename;
1089
            }
1090
 
1091
            // Process marketplace image if uploaded
1092
            if ($storage->setCurrentFilename('marketplace')) {
1093
                $target_size = $this->config['leaderslinked.image_sizes.microlearning_image_size'];
1094
                list($target_width, $target_height) = explode('x', $target_size);
1095
 
1096
                $marketplace_source_filename = $storage->getTmpFilename();
1097
                $marketplace_filename = 'marketplace-' . uniqid() . '.png';
1098
                $marketplace_target_filename = $storage->composePathToFilename(
1099
                    Storage::TYPE_MICROLEARNING_TOPIC,
1100
                    $topic->uuid,
1101
                    $marketplace_filename
1102
                );
1103
 
1104
                if (!$storage->uploadImageCrop($marketplace_source_filename, $marketplace_target_filename, $target_width, $target_height)) {
1105
                    return ['success' => false, 'error' => 'ERROR_UPLOAD_IMAGE'];
1106
                }
1107
 
1108
                // Delete old marketplace image if exists
1109
                if ($topic->marketplace) {
1110
                    $storage->deleteFile($target_path, $topic->uuid, $topic->marketplace);
1111
                }
1112
 
1113
                $result['marketplace_filename'] = $marketplace_filename;
1114
            }
1115
 
1116
            return $result;
1117
        } catch (\Exception $e) {
1118
            return ['success' => false, 'error' => $e->getMessage()];
1119
        }
1120
    }
1 www 1121
}