Proyectos de Subversion LeadersLinked - Backend

Rev

Rev 17268 | Rev 17270 | 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\Db\Adapter\AdapterInterface;
17152 stevensc 7
use Laminas\Log\LoggerInterface;
1 www 8
use Laminas\Mvc\Controller\AbstractActionController;
9
use Laminas\View\Model\ViewModel;
10
use Laminas\View\Model\JsonModel;
17154 stevensc 11
use Laminas\Hydrator\ArraySerializableHydrator;
12
use Laminas\Db\ResultSet\HydratingResultSet;
13
use Laminas\Paginator\Adapter\DbSelect;
14
use Laminas\Paginator\Paginator;
15
use Laminas\Mvc\I18n\Translator;
17002 efrain 16
use LeadersLinked\Mapper\MicrolearningTopicMapper;
17154 stevensc 17
use LeadersLinked\Mapper\MicrolearningUserMapper;
1 www 18
use LeadersLinked\Mapper\UserMapper;
17154 stevensc 19
use LeadersLinked\Mapper\QueryMapper;
1 www 20
use LeadersLinked\Mapper\ApplicationMapper;
21
use LeadersLinked\Mapper\PushMapper;
22
use LeadersLinked\Mapper\PushTemplateMapper;
23
use LeadersLinked\Mapper\DeviceHistoryMapper;
17154 stevensc 24
use LeadersLinked\Mapper\ApplicationVariantMapper;
25
use LeadersLinked\Mapper\NotificationMapper;
26
use LeadersLinked\Mapper\NetworkMapper;
27
use LeadersLinked\Mapper\CompanyMapper;
28
use LeadersLinked\Mapper\CompanyFollowerMapper;
29
use LeadersLinked\Mapper\ConnectionMapper;
1 www 30
use LeadersLinked\Mapper\UserPasswordMapper;
17002 efrain 31
use LeadersLinked\Mapper\MicrolearningExtendUserMapper;
32
use LeadersLinked\Mapper\MicrolearningExtendUserCompanyMapper;
33
use LeadersLinked\Mapper\MicrolearningExtendUserFunctionMapper;
34
use LeadersLinked\Mapper\MicrolearningExtendUserGroupMapper;
35
use LeadersLinked\Mapper\MicrolearningExtendUserInstitutionMapper;
36
use LeadersLinked\Mapper\MicrolearningExtendUserProgramMapper;
37
use LeadersLinked\Mapper\MicrolearningExtendUserPartnerMapper;
38
use LeadersLinked\Mapper\MicrolearningExtendUserSectorMapper;
39
use LeadersLinked\Mapper\MicrolearningExtendUserStudentTypeMapper;
17154 stevensc 40
use LeadersLinked\Mapper\MicrolearningExtendUserCountryMapper;
41
use LeadersLinked\Model\MicrolearningUser;
42
use LeadersLinked\Model\Push;
43
use LeadersLinked\Model\Application;
44
use LeadersLinked\Model\Notification;
45
use LeadersLinked\Model\Network;
46
use LeadersLinked\Model\Connection;
47
use LeadersLinked\Model\CompanyFollower;
48
use LeadersLinked\Model\User;
49
use LeadersLinked\Model\UserType;
50
use LeadersLinked\Model\UserPassword;
17002 efrain 51
use LeadersLinked\Model\MicrolearningExtendUser;
52
use LeadersLinked\Model\MicrolearningExtendUserCompany;
53
use LeadersLinked\Model\MicrolearningExtendUserFunction;
54
use LeadersLinked\Model\MicrolearningExtendUserGroup;
55
use LeadersLinked\Model\MicrolearningExtendUserInstitution;
56
use LeadersLinked\Model\MicrolearningExtendUserProgram;
57
use LeadersLinked\Model\MicrolearningExtendUserPartner;
58
use LeadersLinked\Model\MicrolearningExtendUserSector;
59
use LeadersLinked\Model\MicrolearningExtendUserStudentType;
60
use LeadersLinked\Model\MicrolearningExtendUserCountry;
17248 stevensc 61
use LeadersLinked\Form\Microlearning\TopicUserForm;
17154 stevensc 62
use LeadersLinked\Form\Microlearning\PushMicrolearningNotificationForm;
17248 stevensc 63
use LeadersLinked\Form\Microlearning\TopicCustomerUploadForm;
16766 efrain 64
use LeadersLinked\Library\Functions;
16768 efrain 65
use LeadersLinked\Cache\CacheInterface;
66
use LeadersLinked\Cache\CacheImpl;
17154 stevensc 67
use PhpOffice\PhpSpreadsheet\IOFactory;
17248 stevensc 68
use LeadersLinked\Mapper\MicrolearningTopicUserMapper;
69
use LeadersLinked\Model\MicrolearningTopicUser;
17260 stevensc 70
use LeadersLinked\Library\Storage;
1 www 71
 
72
class MicrolearningAccessForStudentsController extends AbstractActionController
73
{
74
    /**
17152 stevensc 75
     * @var AdapterInterface
1 www 76
     */
77
    private $adapter;
78
 
79
    /**
17152 stevensc 80
     * @var CacheInterface
1 www 81
     */
16769 efrain 82
    private $cache;
83
 
84
    /**
17152 stevensc 85
     * @var LoggerInterface
16769 efrain 86
     */
1 www 87
    private $logger;
88
 
89
    /**
90
     * @var array
91
     */
92
    private $config;
93
 
94
    /**
17152 stevensc 95
     * @var Translator
16768 efrain 96
     */
16769 efrain 97
    private $translator;
16768 efrain 98
 
99
    /**
17152 stevensc 100
     * @param AdapterInterface $adapter
101
     * @param CacheInterface $cache
102
     * @param LoggerInterface $logger
1 www 103
     * @param array $config
17152 stevensc 104
     * @param Translator $translator
1 www 105
     */
16769 efrain 106
    public function __construct($adapter, $cache, $logger, $config, $translator)
1 www 107
    {
17152 stevensc 108
        $this->adapter = $adapter;
109
        $this->cache = $cache;
110
        $this->logger = $logger;
111
        $this->config = $config;
112
        $this->translator = $translator;
1 www 113
    }
114
 
115
    public function indexAction()
116
    {
17154 stevensc 117
        try {
118
            $currentUserPlugin = $this->plugin('currentUserPlugin');
119
            $currentUser = $currentUserPlugin->getUser();
120
            $currentCompany = $currentUserPlugin->getCompany();
1 www 121
 
17154 stevensc 122
            $request = $this->getRequest();
1 www 123
 
17154 stevensc 124
            if($request->isGet())
125
            {
126
                $headers  = $request->getHeaders();
127
                $isJson = false;
1 www 128
 
17154 stevensc 129
                if($headers->has('Accept')) {
130
                    $accept = $headers->get('Accept');
131
                    $prioritized = $accept->getPrioritized();
1 www 132
 
17154 stevensc 133
                    foreach($prioritized as $key => $value) {
134
                        $raw = trim($value->getRaw());
135
                        if(!$isJson) $isJson = strpos($raw, 'json');
1 www 136
                    }
137
                }
138
 
17154 stevensc 139
                if($isJson) {
140
                    try {
17178 stevensc 141
                        $topic_uuid     = Functions::sanitizeFilterString($request->getQuery('topic_uuid'));
17154 stevensc 142
 
143
                        $data = [
144
                            'link_upload' => '',
145
                            'items' => [] ,
146
                            'total' => 0,
147
                        ];
148
 
149
                        if(!$topic_uuid) {
150
                            return new JsonModel([
151
                                'success' => true,
152
                                'data' => $data
153
                            ]);
154
                        }
155
 
156
                        $topicMapper = MicrolearningTopicMapper::getInstance($this->adapter);
17178 stevensc 157
                        $topic = $topicMapper->fetchOneByUuid($topic_uuid);
17154 stevensc 158
 
159
                        if(!$topic) {
160
                            return new JsonModel([
161
                                'success' => true,
162
                                'data' => 'ERROR_TOPIC_NOT_FOUND'
163
                            ]);
164
                        }
165
 
166
                        if($topic->company_id != $currentCompany->id) {
167
                            return new JsonModel([
168
                                'success' => true,
169
                                'data' => 'ERROR_UNAUTHORIZED'
170
                            ]);
171
                        }
17156 stevensc 172
 
17248 stevensc 173
                        $data['link_upload'] = $this->url()->fromRoute('microlearning/access-for-students/upload',['topic_uuid' => $topic->uuid]);
174
                        $data['link_notification'] = $this->url()->fromRoute('microlearning/access-for-students/notification',['topic_uuid' => $topic->uuid]);
17154 stevensc 175
 
176
                        $search = $this->params()->fromQuery('search', []);
177
                        $search = empty($search['value']) ? '' :  Functions::sanitizeFilterString($search['value']);
178
 
179
                        $page               = intval($this->params()->fromQuery('start', 1), 10);
180
                        $records_x_page     = intval($this->params()->fromQuery('length', 10), 10);
181
                        $order =  $this->params()->fromQuery('order', []);
182
                        $order_field        = empty($order[0]['column']) ? 99 :  intval($order[0]['column'], 10);
183
                        $order_direction    = empty($order[0]['dir']) ? 'ASC' : strtoupper(Functions::sanitizeFilterString($order[0]['dir']));
184
 
185
                        $fields =  ['uuid', 'first_name', 'last_name', 'email'];
186
                        $order_field = isset($fields[$order_field]) ? $fields[$order_field] : 'first_name';
187
 
188
                        if(!in_array($order_direction, ['ASC', 'DESC'])) {
189
                            $order_direction = 'ASC';
190
                        }
191
 
192
                        $acl = $this->getEvent()->getViewModel()->getVariable('acl');
193
                        $allowRevoke = $acl->isAllowed($currentUser->usertype_id, 'microlearning/access-for-students/revoke');
194
                        $allowUnlimit = $acl->isAllowed($currentUser->usertype_id, 'microlearning/access-for-students/unlimit');
195
                        $allowCancel = $acl->isAllowed($currentUser->usertype_id, 'microlearning/access-for-students/cancel');
196
                        $allowReactive = $acl->isAllowed($currentUser->usertype_id, 'microlearning/access-for-students/reactive');
197
 
198
                        $queryMapper = QueryMapper::getInstance($this->adapter);
199
                        $sql = $queryMapper->getSql();
200
                        $select = $sql->select();
201
                        $select->columns(['access', 'paid_from', 'paid_to', 'added_on', 'updated_on']);
17248 stevensc 202
                        $select->from(['tb1' => MicrolearningTopicUserMapper::_TABLE] );
17154 stevensc 203
                        $select->join(['tb2' => UserMapper::_TABLE], 'tb1.user_id = tb2.id', ['uuid', 'first_name', 'last_name', 'email']);
17248 stevensc 204
                        $select->where->equalTo('tb1.company_id', $topic->company_id);
17161 stevensc 205
                        $select->where->equalTo('tb1.topic_id', $topic->id);
17154 stevensc 206
 
207
                        if($search) {
208
                            $select->where->nest()
209
                            ->like('first_name', '%' . $search . '%')
210
                            ->or->like('last_name', '%' . $search . '%')
211
                            ->or->like('email', '%' . $search . '%')
212
                            ->unnest();
1 www 213
 
17154 stevensc 214
                        }
215
 
216
 
217
                        $select->order($order_field . ' ' . $order_direction);
218
 
219
                        $hydrator   = new ArraySerializableHydrator();
220
                        $resultset  = new HydratingResultSet($hydrator);
221
 
222
                        $adapter = new DbSelect($select, $sql, $resultset);
223
                        $paginator = new Paginator($adapter);
224
                        $paginator->setItemCountPerPage($records_x_page);
225
                        $paginator->setCurrentPageNumber($page);
226
 
227
 
228
                        $items = [ ];
229
                        $records = $paginator->getCurrentItems();
230
                        foreach($records as $record)
231
                        {
232
                            $params = [
233
                                'topic_uuid' => $topic->uuid,
234
                                'user_uuid' => $record['uuid']
235
                            ];
1 www 236
 
17154 stevensc 237
                            $actions = [];
1 www 238
 
17154 stevensc 239
                            switch($record['access'])
240
                            {
17248 stevensc 241
                                case MicrolearningTopicUser::ACCESS_UNLIMITED :
17154 stevensc 242
                                    $actions['link_revoke'] = $allowRevoke ? $this->url()->fromRoute('microlearning/access-for-students/revoke', $params) : '';
243
                                    $details['access'] = 'LABEL_UNLIMIT';
244
                                    break;
245
 
17248 stevensc 246
                                case MicrolearningTopicUser::ACCESS_REVOKE :
17154 stevensc 247
                                    $actions['link_unlimit'] = $allowUnlimit ? $this->url()->fromRoute('microlearning/access-for-students/unlimit', $params) : '';
248
                                    $details['access'] = 'LABEL_REVOKED';
249
                                    break;
250
 
17248 stevensc 251
                                case MicrolearningTopicUser::ACCESS_PAY_PERIOD :
17154 stevensc 252
                                    $actions['link_cancel'] = $allowCancel ? $this->url()->fromRoute('microlearning/access-for-students/cancel', $params) : '';
253
 
254
                                    $dt_paid_from = \DateTime::createFromFormat('Y-m-d', $record['paid_from']);
255
                                    $dt_paid_to = \DateTime::createFromFormat('Y-m-d', $record['paid_to']);
256
 
257
                                    $details['access'] = 'LABEL_PAY_PERIOD';
258
                                    $details['paid_from'] = $dt_paid_from->format('d/m/Y');
259
                                    $details['paid_to'] = $dt_paid_to->format('d/m/Y');
260
                                    break;
261
 
17248 stevensc 262
                                case MicrolearningTopicUser::ACCESS_SUPENDED :
17154 stevensc 263
                                    $dt_paid_from = \DateTime::createFromFormat('Y-m-d', $record['paid_from']);
264
                                    $dt_paid_to = \DateTime::createFromFormat('Y-m-d', $record['paid_to']);
265
 
266
                                    $details['access'] = 'LABEL_SUSPENDED';
267
                                    $details['paid_from'] = $dt_paid_from->format('d/m/Y');
268
                                    $details['paid_to'] = $dt_paid_to->format('d/m/Y');
269
                                    break;
270
 
17248 stevensc 271
                                case MicrolearningTopicUser::ACCESS_CANCELLED :
17154 stevensc 272
 
273
                                    $date = date('Y-m-d');
274
                                    if($allowCancel && $record['paid_from'] <= $date && $record['paid_to'] >= $date) {
275
                                        $actions['link_reactive'] = $allowReactive ? $this->url()->fromRoute('microlearning/access-for-students/reactive', $params) : '';
276
                                    }
277
 
278
                                    $dt_paid_from = \DateTime::createFromFormat('Y-m-d', $record['paid_from']);
279
                                    $dt_paid_to = \DateTime::createFromFormat('Y-m-d', $record['paid_to']);
280
 
281
                                    $details['access'] = 'LABEL_CANCELLED';
282
                                    $details['paid_from'] = $dt_paid_from->format('d/m/Y');
283
                                    $details['paid_to'] = $dt_paid_to->format('d/m/Y');
284
                                    break;
285
 
1 www 286
                            }
287
 
17154 stevensc 288
                            $dt_added_on = \DateTime::createFromFormat('Y-m-d H:i:s', $record['added_on']);
289
                            $details['added_on'] = $dt_added_on->format('d/m/Y h:i a');
1 www 290
 
17154 stevensc 291
                            $dt_updated_on = \DateTime::createFromFormat('Y-m-d H:i:s', $record['updated_on']);
292
                            $details['updated_on'] = $dt_updated_on->format('d/m/Y h:i a');
1 www 293
 
17154 stevensc 294
                            $item = [
295
                                'uuid' => $record['uuid'],
296
                                'first_name' => $record['first_name'],
297
                                'last_name' => $record['last_name'],
298
                                'email' => $record['email'],
299
                                'details' => $details,
300
                                'actions' => $actions
301
                            ];
17249 stevensc 302
 
17154 stevensc 303
                            array_push($items, $item);
304
                        }
305
 
306
                        $data['items'] = $items;
307
                        $data['total'] = $paginator->getTotalItemCount();
308
 
309
                        return new JsonModel([
310
                            'success' => true,
311
                            'data' => $data
312
                        ]);
313
                    } catch (\Exception $e) {
314
                        $this->logger->err('Error in indexAction: ' . $e->getMessage());
315
                        return new JsonModel([
316
                            'success' => false,
317
                            'data' => 'An error occurred while processing your request'
318
                        ]);
1 www 319
                    }
17154 stevensc 320
                } else {
321
                    $topicMapper = MicrolearningTopicMapper::getInstance($this->adapter);
322
                    $topics = $topicMapper->fetchAllByCompanyId($currentCompany->id);
1 www 323
 
17154 stevensc 324
                    if($topics) {
325
                        $topic_id = $topics[0]->id;
326
                    }  else {
327
                        $topic_id = 0;
328
                    }
1 www 329
 
17248 stevensc 330
                    $form = new TopicUserForm($this->adapter, $currentCompany->id, $topic_id);
17154 stevensc 331
                    $formPushNotification = new PushMicrolearningNotificationForm($this->adapter, $currentCompany->id);
17248 stevensc 332
                    $formTopicCustomer = new TopicCustomerUploadForm();
1 www 333
 
17154 stevensc 334
                    $this->layout()->setTemplate('layout/layout-backend');
335
                    $viewModel = new ViewModel();
336
                    $viewModel->setTemplate('leaders-linked/microlearning-access-for-students/index.phtml');
337
                    $viewModel->setVariables([
338
                        'form' => $form,
339
                        'formPushNotification' => $formPushNotification,
17248 stevensc 340
                        'formTopicCustomer' => $formTopicCustomer
17154 stevensc 341
                    ]);
1 www 342
 
17154 stevensc 343
                    return $viewModel ;
1 www 344
                }
345
 
17154 stevensc 346
            } else {
1 www 347
                return new JsonModel([
17154 stevensc 348
                    'success' => false,
349
                    'data' => 'ERROR_METHOD_NOT_ALLOWED'
1 www 350
                ]);
351
            }
17154 stevensc 352
        } catch (\Exception $e) {
353
            $this->logger->err('Fatal error in indexAction: ' . $e->getMessage());
17155 stevensc 354
            return new JsonModel([
355
                'success' => false,
356
                'data' => $e->getMessage()
357
            ]);
1 www 358
        }
359
    }
17248 stevensc 360
 
361
    /**
362
     * Revokes unlimited access for a user to a specific microlearning topic
363
     *
364
     * This action handles the revocation of unlimited access privileges for a user.
365
     * It checks various conditions before proceeding with the revocation:
366
     * - Validates current user and company
367
     * - Verifies topic and user existence
368
     * - Ensures proper authorization
369
     * - Confirms the user has unlimited access before revoking
370
     *
371
     * @return JsonModel Returns a JSON response indicating success or failure
372
     */
1 www 373
    public function revokeAction()
374
    {
17248 stevensc 375
        // Get the current request object
1 www 376
        $request = $this->getRequest();
377
 
17248 stevensc 378
        // Get current user and company from plugin
1 www 379
        $currentUserPlugin = $this->plugin('currentUserPlugin');
380
        $currentUser    = $currentUserPlugin->getUser();
381
        $currentCompany = $currentUserPlugin->getCompany();
382
 
17248 stevensc 383
        // Get topic and user UUIDs from route parameters
1 www 384
        $request    = $this->getRequest();
385
        $topic_uuid     = $this->params()->fromRoute('topic_uuid');
386
        $user_uuid   = $this->params()->fromRoute('user_uuid');
17248 stevensc 387
 
388
        // Validate current user exists
389
        if(!$currentUser) {
1 www 390
            return new JsonModel([
391
                'success'   => false,
17248 stevensc 392
                'data'   => 'ERROR_UNAUTHORIZED'
1 www 393
            ]);
394
        }
17248 stevensc 395
 
396
        // Validate current company exists
397
        if(!$currentCompany) {
1 www 398
            return new JsonModel([
399
                'success'   => false,
400
                'data'   => 'ERROR_UNAUTHORIZED'
401
            ]);
402
        }
17248 stevensc 403
 
404
        // Validate topic UUID was provided
405
        if(!$topic_uuid) {
406
            return new JsonModel([
407
                'success'   => false,
408
                'data'   => 'ERROR_TOPIC_NOT_FOUND'
409
            ]);
410
        }
411
 
412
        // Validate user UUID was provided
413
        if(!$user_uuid) {
414
            return new JsonModel([
415
                'success'   => false,
416
                'data'   => 'ERROR_USER_NOT_FOUND'
417
            ]);
418
        }
1 www 419
 
17248 stevensc 420
        // Fetch topic by UUID and validate it exists
421
        $topicMapper = MicrolearningTopicMapper::getInstance($this->adapter);
422
        $topic = $topicMapper->fetchOneByUuid($topic_uuid);
423
        if(!$topic) {
1 www 424
            return new JsonModel([
425
                'success'   => false,
17248 stevensc 426
                'data'   => 'ERROR_TOPIC_NOT_FOUND'
1 www 427
            ]);
428
        }
429
 
17248 stevensc 430
        // Validate topic belongs to current company
431
        if($topic->company_id != $currentCompany->id) {
1 www 432
            return new JsonModel([
433
                'success'   => false,
434
                'data'   => 'ERROR_UNAUTHORIZED'
435
            ]);
436
        }
437
 
17248 stevensc 438
        // Fetch user by UUID and validate it exists
1 www 439
        $userMapper = UserMapper::getInstance($this->adapter);
440
        $user = $userMapper->fetchOneByUuid($user_uuid);
441
 
442
        if(!$user) {
443
            return new JsonModel([
444
                'success'   => false,
445
                'data'   => 'ERROR_USER_NOT_FOUND'
446
            ]);
447
        }
448
 
17248 stevensc 449
        // Fetch topic-user relationship and validate it exists
450
        $topicUserMapper = MicrolearningTopicUserMapper::getInstance($this->adapter);
451
        $topicUser = $topicUserMapper->fetchOneByUserIdAndTopicId($user->id, $topic->id);
452
        if(!$topicUser) {
1 www 453
            return new JsonModel([
454
                'success'   => false,
455
                'data'   => 'ERROR_UNAUTHORIZED'
456
            ]);
457
        }
458
 
17248 stevensc 459
        // Process revocation only for POST requests
1 www 460
        if($request->isPost()) {
17248 stevensc 461
            // Validate user has unlimited access before revoking
462
            if($topicUser->access != MicrolearningTopicUser::ACCESS_UNLIMITED) {
1 www 463
                return new JsonModel([
464
                    'success'   => false,
465
                    'data'   => 'ERROR_USER_ACCESS_CANNT_BE_REVOKE'
466
                ]);
467
            }
17248 stevensc 468
 
469
            // Update access to revoked status
470
            $topicUser->access = MicrolearningTopicUser::ACCESS_REVOKE;
471
            if($topicUserMapper->update($topicUser)) {
1 www 472
 
17248 stevensc 473
                // Refresh topic user data after update
474
                $topicUser = $topicUserMapper->fetchOne($topicUser->id);
475
                if($topicUser) {
476
                    // Update or create microlearning user record
17002 efrain 477
                    $microlearningUserMapper = MicrolearningUserMapper::getInstance($this->adapter);
17248 stevensc 478
                    $microlearningUser = $microlearningUserMapper->fetchOneByUserIdAndCompanyId($topicUser->user_id, $topicUser->company_id);
17002 efrain 479
                    if($microlearningUser) {
17248 stevensc 480
                        // Update existing microlearning user
481
                        $microlearningUser->updated_on = $topicUser->updated_on;
17002 efrain 482
                        $microlearningUserMapper->update($microlearningUser);
1 www 483
 
484
                    } else {
17248 stevensc 485
                        // Create new microlearning user
17002 efrain 486
                        $microlearningUser = new MicrolearningUser();
17248 stevensc 487
                        $microlearningUser->company_id = $topicUser->company_id;
488
                        $microlearningUser->user_id = $topicUser->user_id;
489
                        $microlearningUser->added_on = $topicUser->added_on;
490
                        $microlearningUser->updated_on = $topicUser->updated_on;
1 www 491
 
17002 efrain 492
                        $microlearningUserMapper->insert($microlearningUser);
1 www 493
                    }
494
                }
495
 
496
                return new JsonModel([
497
                    'success' => true,
498
                    'data' => 'LABEL_USER_ACCESS_HAS_BEEN_REVOKE'
499
                ]);
500
 
501
            } else {
502
 
503
                return new JsonModel([
504
                    'success'   => false,
17248 stevensc 505
                    'data'      => $topicUserMapper->getError()
1 www 506
                ]);
507
            }
508
        }
509
 
17248 stevensc 510
        // Return error for non-POST requests
1 www 511
        return new JsonModel([
512
            'success' => false,
513
            'data' => 'ERROR_METHOD_NOT_ALLOWED'
514
        ]);
515
    }
516
 
517
    public function unlimitAction()
518
    {
17248 stevensc 519
        try {
520
            $request    = $this->getRequest();
521
 
522
            $currentUserPlugin = $this->plugin('currentUserPlugin');
523
            $currentUser    = $currentUserPlugin->getUser();
524
            $currentCompany = $currentUserPlugin->getCompany();
525
 
526
            $topic_uuid     = $this->params()->fromRoute('topic_uuid');
527
            $user_uuid   = $this->params()->fromRoute('user_uuid');
528
 
529
            if(!$currentUser) {
530
                return new JsonModel([
531
                    'success'   => false,
532
                    'data'   => 'ERROR_UNAUTHORIZED'
533
                ]);
534
            }
535
 
536
            if(!$currentCompany) {
537
                return new JsonModel([
538
                    'success'   => false,
539
                    'data'   => 'ERROR_UNAUTHORIZED'
540
                ]);
541
            }
542
 
543
            if(!$topic_uuid) {
544
                return new JsonModel([
545
                    'success'   => false,
546
                    'data'   => 'ERROR_TOPIC_NOT_FOUND'
547
                ]);
548
            }
549
 
550
            if(!$user_uuid) {
551
                return new JsonModel([
552
                    'success'   => false,
553
                    'data'   => 'ERROR_USER_NOT_FOUND'
554
                ]);
555
            }
556
 
557
            if(!$request->isPost()) {
558
                return new JsonModel([
559
                    'success'   => false,
560
                    'data'   => 'ERROR_METHOD_NOT_ALLOWED'
561
                ]);
562
            }
563
 
564
            $topicMapper = MicrolearningTopicMapper::getInstance($this->adapter);
565
            $topic = $topicMapper->fetchOneByUuid($topic_uuid);
566
            if(!$topic) {
567
                return new JsonModel([
568
                    'success'   => false,
569
                    'data'   => 'ERROR_TOPIC_NOT_FOUND'
570
                ]);
571
            }
1 www 572
 
17248 stevensc 573
            if($topic->company_id != $currentCompany->id) {
574
                return new JsonModel([
575
                    'success'   => false,
576
                    'data'   => 'ERROR_UNAUTHORIZED'
577
                ]);
578
            }
1 www 579
 
17248 stevensc 580
            $userMapper = UserMapper::getInstance($this->adapter);
581
            $user = $userMapper->fetchOneByUuid($user_uuid);
1 www 582
 
17248 stevensc 583
            if(!$user) {
1 www 584
                return new JsonModel([
585
                    'success'   => false,
17248 stevensc 586
                    'data'   => 'ERROR_USER_NOT_FOUND'
1 www 587
                ]);
588
            }
17248 stevensc 589
 
590
            $topicUserMapper = MicrolearningTopicUserMapper::getInstance($this->adapter);
591
            $topicUser = $topicUserMapper->fetchOneByUserIdAndTopicId($user->id, $topic->id);
592
            if(!$topicUser) {
1 www 593
                return new JsonModel([
17248 stevensc 594
                    'success'   => false,
595
                    'data'   => 'ERROR_UNAUTHORIZED'
1 www 596
                ]);
17248 stevensc 597
            }
598
 
599
            if($topicUser->access != MicrolearningTopicUser::ACCESS_REVOKE) {
1 www 600
                return new JsonModel([
601
                    'success'   => false,
17248 stevensc 602
                    'data'   => 'ERROR_USER_ACCESS_CANNT_BE_UNLIMIT'
1 www 603
                ]);
604
            }
17248 stevensc 605
 
606
            $topicUser->access = MicrolearningTopicUser::ACCESS_UNLIMITED;
607
            $topicUser->updated_on = date('Y-m-d H:i:s');
608
 
609
            try {
610
                $topicUserMapper->update($topicUser);
611
 
612
                $microlearningUserMapper = MicrolearningUserMapper::getInstance($this->adapter);
613
                $microlearningUser = $microlearningUserMapper->fetchOneByUserIdAndCompanyId($topicUser->user_id, $topicUser->company_id);
614
 
615
                if(!$microlearningUser) {
616
                    $microlearningUser = new MicrolearningUser();
617
                    $microlearningUser->company_id = $topicUser->company_id;
618
                    $microlearningUser->user_id = $topicUser->user_id;
619
                    $microlearningUser->added_on = $topicUser->added_on;
620
                    $microlearningUser->updated_on = $topicUser->updated_on;
621
                    $microlearningUserMapper->insert($microlearningUser);
622
                } else {
623
                    $microlearningUser->updated_on = $topicUser->updated_on;
624
                    $microlearningUserMapper->update($microlearningUser);
625
                }
626
            } catch (\Exception $e) {
627
                $this->logger->err('Error updating topic user: ' . $e->getMessage());
628
                return new JsonModel([
629
                    'success'   => false,
630
                    'data'      => 'ERROR_UPDATING_TOPIC_USER'
631
                ]);
632
            }
633
 
634
            return new JsonModel([
635
                'success' => true,
636
                'data' => 'LABEL_USER_ACCESS_HAS_BEEN_UNLIMITED'
637
            ]);
638
        } catch (\Exception $e) {
639
            $this->logger->err('Fatal error in unlimitAction: ' . $e->getMessage());
640
            return new JsonModel([
641
                'success' => false,
642
                'data' => $e->getMessage()
643
            ]);
1 www 644
        }
645
    }
646
 
647
    public function uploadAction()
648
    {
17262 stevensc 649
        try {
650
            $request = $this->getRequest();
651
 
652
            $currentUserPlugin = $this->plugin('currentUserPlugin');
653
            $currentNetworkPlugin = $this->plugin('currentNetworkPlugin');
17261 stevensc 654
 
17262 stevensc 655
            $currentUser    = $currentUserPlugin->getUser();
656
            $currentCompany = $currentUserPlugin->getCompany();
657
            $currentNetwork = $currentNetworkPlugin->getNetwork();
658
 
659
            $topic_uuid     = $this->params()->fromRoute('topic_uuid');
17261 stevensc 660
 
17262 stevensc 661
            if(!$currentUser) {
662
                $this->logger->err('Upload failed: Current user not found');
663
                return new JsonModel([
664
                    'success'   => false,
665
                    'data'   => 'ERROR_USER_NOT_FOUND'
666
                ]);
667
            }
17261 stevensc 668
 
17262 stevensc 669
            if(!$currentCompany) {
670
                $this->logger->err('Upload failed: Current company not found');
671
                return new JsonModel([
672
                    'success'   => false,
673
                    'data'   => 'ERROR_COMPANY_NOT_FOUND'
674
                ]);
675
            }
17261 stevensc 676
 
17262 stevensc 677
            if(!$currentNetwork) {
678
                $this->logger->err('Upload failed: Current network not found');
679
                return new JsonModel([
680
                    'success'   => false,
681
                    'data'   => 'ERROR_NETWORK_NOT_FOUND'
682
                ]);
683
            }
17261 stevensc 684
 
17262 stevensc 685
            if(!$topic_uuid) {
686
                $this->logger->err('Upload failed: Topic UUID not provided');
687
                return new JsonModel([
688
                    'success'   => false,
689
                    'data'   => 'ERROR_TOPIC_NOT_FOUND'
690
                ]);
691
            }
17261 stevensc 692
 
17262 stevensc 693
            if(!$request->isPost()) {
694
                $this->logger->err('Upload failed: Request method not POST');
695
                return new JsonModel([
696
                    'success'   => false,
697
                    'data'   => 'ERROR_METHOD_NOT_ALLOWED'
698
                ]);
699
            }
1 www 700
 
17262 stevensc 701
            $topicMapper = MicrolearningTopicMapper::getInstance($this->adapter);
702
            $topic = $topicMapper->fetchOneByUuid($topic_uuid);
17261 stevensc 703
 
17262 stevensc 704
            if(!$topic) {
705
                $this->logger->err('Upload failed: Topic not found for UUID: ' . $topic_uuid);
17261 stevensc 706
                return new JsonModel([
17262 stevensc 707
                    'success'   => false,
708
                    'data'   => 'ERROR_TOPIC_NOT_FOUND'
17261 stevensc 709
                ]);
710
            }
711
 
17262 stevensc 712
            if($topic->company_id != $currentCompany->id) {
713
                $this->logger->err('Upload failed: Topic does not belong to current company');
714
                return new JsonModel([
715
                    'success'   => false,
716
                    'data'   => 'ERROR_UNAUTHORIZED'
717
                ]);
718
            }
17261 stevensc 719
 
17262 stevensc 720
            $step = Functions::sanitizeFilterString($this->params()->fromPost('step'));
721
 
722
            $storage = Storage::getInstance($this->config, $this->adapter);
17261 stevensc 723
 
17262 stevensc 724
            // Log uploaded files info
725
            $files = $request->getFiles()->toArray();
726
            $this->logger->info('Uploaded files: ' . print_r($files, true));
727
 
728
            $storage->setFiles($files);
729
 
730
            if(!$storage->setCurrentFilename('file')) {
731
                $this->logger->err('Upload failed: Could not set current filename');
17261 stevensc 732
                return new JsonModel([
733
                    'success' => false,
17262 stevensc 734
                    'data' => 'ERROR_UPLOAD_FILE'
17261 stevensc 735
                ]);
736
            }
1 www 737
 
17262 stevensc 738
            if($step == 'validation') {
739
                try {
740
                    $userMapper = UserMapper::getInstance($this->adapter);
741
                    $topicUserMapper = MicrolearningTopicUserMapper::getInstance($this->adapter);
742
 
743
                    $tmp_filename = $storage->getTmpFilename();
744
                    $filename = $storage->getFilename();
745
                    $target_filename = $storage->composePathToFilename(
746
                        Storage::TYPE_MICROLEARNING_ACCESS_FOR_STUDENTS,
747
                        $topic->uuid,
748
                        $filename
749
                    );
1 www 750
 
17262 stevensc 751
                    $this->logger->info('Processing file upload: ' . $target_filename);
752
 
753
                    if(!$storage->putFile($tmp_filename, $target_filename)) {
754
                        $this->logger->err('Upload failed: Could not move file to target location');
755
                        return new JsonModel([
756
                            'success' => false,
757
                            'data' => 'ERROR_UPLOAD_FILE'
758
                        ]);
759
                    }
760
 
761
                    $count_users = 0;
762
                    $users = [];
763
                    $count_errors = 0;
764
                    $errors = [];
765
 
766
                    try {
767
                        $this->logger->info('Loading spreadsheet: ' . $target_filename);
768
                        $spreadsheet = IOFactory::load($target_filename);
769
                        $records = $spreadsheet->getActiveSheet()->toArray(null, true, true, true);
770
                    } catch (\Exception $e) {
771
                        $this->logger->err('Spreadsheet processing failed: ' . $e->getMessage());
772
                        return new JsonModel([
773
                            'success' => false,
774
                            'data' => 'ERROR_UPLOAD_FILE: ' . $e->getMessage()
775
                        ]);
776
                    }
777
 
778
                    $emails = [];
779
                    $row = 0;
780
 
781
                    foreach($records as $record)
782
                    {
783
                        /*
784
                        A = Nombre
785
                        B = Apellido
786
                        C = Email
787
                        D = Contraseña
788
                        E = Empresa
789
                        F = Función
790
                        G = Grupo
791
                        H = Institución
792
                        I = Programa
793
                        J = Socio
794
                        K = Sector
795
                        L = Tipo de Estudiante
796
                        M = País
797
                        N = Es adulto
798
                        */
799
                        $row++;
800
 
801
                        $first_name = Functions::sanitizeFilterString($record['A']);
802
                        $last_name = Functions::sanitizeFilterString($record['B']);
803
                        $email = trim(filter_var($record['C'], FILTER_SANITIZE_EMAIL));
804
                        $password = Functions::sanitizeFilterString($record['D']);
805
 
806
                        $company =  isset($record['E']) ? Functions::sanitizeFilterString($record['E']) : '';
807
                        $function = isset($record['F']) ? Functions::sanitizeFilterString($record['F']) : '';
808
                        $group = isset($record['G']) ? Functions::sanitizeFilterString($record['G']) : '';
809
                        $institution = isset($record['H']) ? Functions::sanitizeFilterString($record['H']) : '';
810
                        $program = isset($record['I']) ? Functions::sanitizeFilterString($record['I']) : '';
811
                        $partner = isset($record['J']) ? Functions::sanitizeFilterString($record['J']) : '';
812
                        $sector = isset($record['K']) ? Functions::sanitizeFilterString($record['K']) : '';
813
                        $studentType = isset($record['L']) ? Functions::sanitizeFilterString($record['L']) : '';
814
                        $country =  isset($record['M']) ? Functions::sanitizeFilterString($record['M']) : '';
815
                        $isAdult = isset($record['N']) ? Functions::sanitizeFilterString($record['N']) : '';
816
 
817
                        if($row == 1) {
818
                            continue;
819
                        }
820
 
821
                        //||  empty($password)
822
                        if(empty($first_name) || empty($last_name) || !filter_var($email, FILTER_VALIDATE_EMAIL) ) {
823
                            continue;
824
                        }
825
 
826
                        if(!in_array($email, $emails)) {
827
                            $user = $userMapper->fetchOneByEmail($email);
828
                            $assigned_topics = $user ? $topicUserMapper->fetchCountByCompanyIdAndTopicIdAndUserId($topic->company_id, $topic->id, $user->id) : 0;
829
                            $count_users++;
830
 
831
                            array_push($emails, $email);
832
                            array_push($users, [
833
                                'id' => $count_users,
834
                                'first_name' => $first_name,
835
                                'last_name' => $last_name,
836
                                'password'  => $password,
837
                                'email' => $email,
838
                                'assigned_topics' => $assigned_topics,
839
                                'company' => $company,
840
                                'function' => $function,
841
                                'group' => $group,
842
                                'institution' => $institution,
843
                                'program' => $program,
844
                                'partner' => $partner,
845
                                'sector' => $sector,
846
                                'studentType' => $studentType,
847
                                'country' => $country,
848
                                'isAdult' => $isAdult,
849
                            ]);
850
                        } else {
851
                            $count_errors++;
852
                            array_push($errors,[
853
                                'id' => $count_errors,
854
                                'first_name' => $first_name,
855
                                'last_name' => $last_name,
856
                                'password'  => $password,
857
                                'email' => $email,
858
                                'status' => 'DUPLICATE IN EXCEL'
859
                            ]);
860
                        }
861
                    }
862
 
17269 stevensc 863
                    $this->logger->info('Users: ' . print_r($users, true));
864
                    $this->logger->info('Errors: ' . print_r($errors, true));
865
 
17268 stevensc 866
                    try {
17269 stevensc 867
                        $this->logger->info('Cache users: ' . print_r($users, true));
17268 stevensc 868
                        $key = md5($currentUser->id . '-' . $topic->uuid . '-' . $topic->uuid);
869
                        $this->cache->setItem($key, serialize($users));
870
                    } catch (\Exception $e) {
871
                        $this->logger->err('Cache operation failed: ' . $e->getMessage());
872
                        return new JsonModel([
873
                            'success' => false,
874
                            'data' => 'ERROR_CACHE_SET_ITEM: ' . $e->getMessage()
875
                        ]);
876
                    }
17267 stevensc 877
 
17262 stevensc 878
                    return new JsonModel([
879
                        'success' => true,
880
                        'data' => [
17268 stevensc 881
                            'key' => $key,
17262 stevensc 882
                            'topic' => $topic->name,
883
                            'items' => [
884
                                'ok' => $users,
885
                                'error' => $errors,
886
                            ],
887
                        ]
17261 stevensc 888
                    ]);
17262 stevensc 889
                } catch (\Exception $e) {
890
                    $this->logger->err('Validation step failed: ' . $e->getMessage());
891
                    throw $e;
17261 stevensc 892
                }
893
            }
17262 stevensc 894
 
17261 stevensc 895
            return new JsonModel([
17262 stevensc 896
                'success' => false,
897
                'data' => 'ERROR_PARAMETERS_ARE_INVALID'
17261 stevensc 898
            ]);
17262 stevensc 899
 
900
        } catch (\Exception $e) {
901
            $this->logger->err('Fatal error in uploadAction: ' . $e->getMessage() . "\n" . $e->getTraceAsString());
17263 stevensc 902
            return new JsonModel([
17269 stevensc 903
                'success' => false,
904
                'data' => 'ERROR_INTERNAL_SERVER_ERROR'
905
            ]);
1 www 906
        }
907
    }
908
 
17248 stevensc 909
    /**
910
     * Handles sending push notifications to selected users about a microlearning topic
911
     *
912
     * This action processes push notifications for microlearning topics:
913
     * 1. Validates user permissions and input data
914
     * 2. Processes the notification form
915
     * 3. Sends push notifications to selected users' devices
916
     *
917
     * Required parameters:
918
     * - topic_uuid: UUID of the microlearning topic
919
     * - customer_uuids: Array of user UUIDs to receive the notification
920
     * - push_template_id: UUID of the push notification template to use
921
     *
922
     * @return JsonModel Returns JSON response with:
923
     *                   - success: true/false
924
     *                   - data: Contains push_to_send count or error message
925
     * @throws \Throwable When an error occurs during processing
926
     */
1 www 927
    public function notificationAction()
928
    {
17248 stevensc 929
        try {
930
            // Get current request and user context
931
            $request = $this->getRequest();
1 www 932
 
17248 stevensc 933
            $currentUserPlugin = $this->plugin('currentUserPlugin');
934
            $currentUser    = $currentUserPlugin->getUser();
935
            $currentCompany = $currentUserPlugin->getCompany();
936
 
937
            $topic_uuid     = $this->params()->fromRoute('topic_uuid');
938
 
939
            // Validate topic UUID exists
940
            if(!$topic_uuid) {
941
                return new JsonModel([
942
                    'success'   => false,
943
                    'data'   => 'ERROR_TOPIC_UUID_NOT_FOUND'
944
                ]);
945
            }
946
 
947
            // Validate company exists
948
            if(!$currentCompany) {
949
                return new JsonModel([
950
                    'success'   => false,
951
                    'data'   => 'ERROR_COMPANY_NOT_FOUND'
952
                ]);
953
            }
954
 
955
            // Fetch and validate topic
956
            $topicMapper = MicrolearningTopicMapper::getInstance($this->adapter);
957
            $topic = $topicMapper->fetchOneByUuid($topic_uuid);
958
            if(!$topic) {
959
                return new JsonModel([
960
                    'success'   => false,
961
                    'data'   => 'ERROR_TOPIC_NOT_FOUND'
962
                ]);
963
            }
1 www 964
 
17248 stevensc 965
            // Validate topic belongs to current company
966
            if($topic->company_id != $currentCompany->id) {
967
                return new JsonModel([
968
                    'success'   => false,
969
                    'data'   => 'ERROR_UNAUTHORIZED'
970
                ]);
971
            }
972
 
973
            // Validate request method
974
            if(!$request->isPost()) {
975
                return new JsonModel([
976
                    'success' => false,
977
                    'data' => 'ERROR_METHOD_NOT_ALLOWED'
978
                ]);
979
            }
980
 
981
            // Process notification form
1 www 982
            $dataPost = $request->getPost()->toArray();
15390 efrain 983
            $form = new PushMicrolearningNotificationForm($this->adapter, $currentCompany->id);
1 www 984
 
985
            $form->setData($dataPost);
986
 
17248 stevensc 987
            // Validate form data
988
            if(!$form->isValid()) {
989
                $messages = [];
990
                $form_messages = (array) $form->getMessages();
991
                foreach($form_messages  as $fieldname => $field_messages)
992
                {
993
                    $messages[$fieldname] = array_values($field_messages);
1 www 994
                }
995
 
17248 stevensc 996
                return new JsonModel([
997
                    'success'   => false,
998
                    'data'      => $messages
999
                ]);
1000
            }
1001
 
1002
            // Validate selected users
1003
            $customer_uuids = $this->params()->fromPost('customer_uuids');
1004
            if(!$customer_uuids) {
1005
                return new JsonModel([
1006
                    'success' => false,
1007
                    'data' => 'ERROR_NOT_SELECTED_CUSTOMERS'
1008
                ]);
1 www 1009
 
17248 stevensc 1010
            }
1011
 
1012
            // Get push template
1013
            $push_template_uuid = Functions::sanitizeFilterString($form->get('push_template_id')->getValue());
1014
            $pushMapper = PushMapper::getInstance($this->adapter);
1015
            $pushTemplateMapper = PushTemplateMapper::getInstance($this->adapter);
1016
            $pushTemplate = $pushTemplateMapper->fetchOneByUuid($push_template_uuid);
1017
 
1018
            if(!$pushTemplate) {
1019
                return new JsonModel([
1020
                    'success' => false,
1021
                    'data' => 'ERROR_PUSH_TEMPLATE_NOT_FOUND'
1022
                ]);
1023
            }
1024
 
1025
            // Initialize push notification process
1026
            $applicationMapper = ApplicationMapper::getInstance($this->adapter);
1027
            $application = $applicationMapper->fetchOne(Application::TWOGETSKILLS);
1028
 
1029
            $topicUserMapper = MicrolearningTopicUserMapper::getInstance($this->adapter);
1030
 
1031
            $push_to_send = 0;
1032
 
1033
            // Process each selected user
1034
            $userMapper = UserMapper::getInstance($this->adapter);
1035
            $deviceHistoryMapper = DeviceHistoryMapper::getInstance($this->adapter);
1036
            foreach($customer_uuids as $customer_uuid)
1037
            {
1038
                $user = $userMapper->fetchOneByUuid($customer_uuid);
1039
                if(!$user) {
1040
                    continue;
1 www 1041
                }
1042
 
17248 stevensc 1043
                $topicUser = $topicUserMapper->fetchOneByUserIdAndTopicId($user->id, $topic->id);
1044
                if(!$topicUser) {
1045
                    continue;
1046
                }
1 www 1047
 
17248 stevensc 1048
                // Get user's latest device
1049
                $device = $deviceHistoryMapper->fetchLastDeviceByApplicationIdAndUserId(Application::TWOGETSKILLS, $user->id);
1 www 1050
 
17248 stevensc 1051
                if($device && $device->token) {
1 www 1052
 
17248 stevensc 1053
                    $key = $application->key;
1054
                    if($device->variant_id) {
13755 efrain 1055
 
17248 stevensc 1056
                        $applicationVariantMapper = ApplicationVariantMapper::getInstance($this->adapter);
1057
                        $applicationVariant = $applicationVariantMapper->fetchOneByApplicationIdAndVariantId($device->application_id, $device->variant_id);
1058
                        if($applicationVariant) {
1059
                            $key = $applicationVariant->key;
1060
                        } else {
1061
                            $applicationVariant = $applicationVariantMapper->fetchOneByApplicationIdAndDefault($device->application_id);
13755 efrain 1062
                            if($applicationVariant) {
1063
                                $key = $applicationVariant->key;
1064
                            }
1065
                        }
1066
 
17248 stevensc 1067
                    }
1068
 
1069
                    // Create push notification
1070
                    $push = new Push();
1071
                    $push->status = Push::STATUS_PENDING;
1072
                    $push->data = json_encode([
1073
                        'server' => [
1074
                            'key' => $key,
1075
                        ],
1076
                        'push' => [
1077
                            'registration_ids'   => [
1078
                                $device->token,
1 www 1079
                            ],
17248 stevensc 1080
                            'notification' => [
1081
                                'body' =>  $pushTemplate->body,
1082
                                'title' => $pushTemplate->title,
1083
                                'vibrate' => 1,
1084
                                'sound' =>  1
1085
                            ],
1086
                            'data' => [
1087
                                'command' => 'content-refresh',
1088
                                'new_topics' => '0',
1 www 1089
                            ]
17248 stevensc 1090
                        ]
1091
                    ]);
1092
 
1093
                    if($pushMapper->insert($push)) {
1094
                        $push_to_send = $push_to_send + 1;
1 www 1095
                    }
17248 stevensc 1096
 
1 www 1097
                }
17248 stevensc 1098
            }
1099
 
1100
            // Validate notifications were created
1101
            if(0 == $push_to_send) {
1 www 1102
                return new JsonModel([
17248 stevensc 1103
                    'success' => false,
1104
                    'data' => 'ERROR_NO_USER_DEVICES_WERE_FOUND_TO_SEND_PUSH'
1 www 1105
                ]);
1106
            }
17248 stevensc 1107
 
1108
            return new JsonModel([
1109
                'success' => true,
1110
                'data' => [
1111
                    'push_to_send' => $push_to_send,
1112
                ]
1113
            ]);
1114
        } catch (\Throwable $e) {
1115
            $this->logger->error($e->getMessage());
1116
            return new JsonModel([
1117
                'success' => false,
1118
                'data' => 'ERROR_INTERNAL_SERVER_ERROR'
1119
            ]);
1 www 1120
        }
17248 stevensc 1121
    }
1 www 1122
}