Completed
Pull Request — 1.11.x (#1579)
by José
24:00
created

BuyCoursesPlugin   F

Complexity

Total Complexity 181

Size/Duplication

Total Lines 2159
Duplicated Lines 17.88 %

Coupling/Cohesion

Components 1
Dependencies 16

Importance

Changes 0
Metric Value
dl 386
loc 2159
rs 0.6314
c 0
b 0
f 0
wmc 181
lcom 1
cbo 16

72 Methods

Rating   Name   Duplication   Size   Complexity  
A create() 0 5 2
B __construct() 0 24 1
B install() 0 26 2
A uninstall() 0 23 2
B buyCoursesForGridCatalogVerificator() 0 22 4
A returnBuyCourseButton() 0 13 1
A getSelectedCurrency() 0 11 1
A getCurrencies() 0 7 1
A selectCurrency() 0 16 1
A savePaypalParams() 0 13 1
A getPaypalParams() 0 9 1
A saveTransferAccount() 0 11 1
A getTransferAccounts() 0 7 1
A deleteTransferAccount() 0 7 1
A getCourses() 0 22 1
B getItemByProduct() 0 25 1
A getCoursesForConfiguration() 0 17 3
A getSessionsForConfiguration() 0 15 2
B getUserStatusForSession() 44 44 4
B getCatalogSessionList() 20 50 6
B getUserStatusForCourse() 44 45 4
B getCatalogCourseList() 5 49 6
B getCourseInfo() 5 44 5
B getSessionInfo() 20 69 6
A getItem() 0 11 1
C registerSale() 0 51 7
A getSale() 0 11 1
A getSaleListByPaymentType() 20 20 1
A getCurrency() 0 11 1
A updateSaleStatus() 0 10 1
B completeSale() 0 34 5
A cancelSale() 0 4 1
A getPaymentTypes() 0 8 1
A getSaleListByStatus() 20 20 1
A getSaleStatuses() 0 8 1
A getPayoutStatuses() 0 8 1
A getProductTypes() 0 7 1
C filterSessionList() 48 48 9
C filterCourseList() 47 47 9
C randomText() 0 26 7
A generateReference() 0 7 1
B getSaleListByUser() 0 30 2
B getSaleListByUserId() 27 27 2
B getCourseForConfiguration() 6 25 3
B getSessionForConfiguration() 6 60 5
A getItemBeneficiaries() 12 12 1
A deleteItem() 0 15 2
A registerItem() 0 6 1
A updateItem() 13 13 1
A deleteItemBeneficiaries() 9 9 1
A registerItemBeneficiaries() 10 17 2
A isValidCourse() 0 12 3
A getBeneficiariesBySale() 0 12 1
B getPayouts() 0 44 5
B verifyPaypalAccountByBeneficiary() 0 39 4
B storePayouts() 0 24 2
A setStatusPayouts() 11 11 1
A getPlatformCommission() 0 9 1
A updateCommission() 9 9 1
B storeService() 0 37 2
B updateService() 0 28 2
A deleteService() 0 7 1
B getServices() 0 61 4
A getServiceSaleStatuses() 0 8 1
D getServiceSale() 0 118 11
A cancelServiceSale() 0 5 1
A completeServiceSale() 0 12 2
B getCatalogServiceList() 0 55 6
A updateServiceSaleStatus() 10 10 1
B registerServiceSale() 0 39 3
A saveCulqiParameters() 0 11 1
A getCulqiParams() 0 9 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like BuyCoursesPlugin often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use BuyCoursesPlugin, and based on these observations, apply Extract Interface, too.

1
<?php
2
/* For license terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Entity\Session;
5
use Chamilo\CoreBundle\Entity\Course;
6
7
/**
8
 * Plugin class for the BuyCourses plugin
9
 * @package chamilo.plugin.buycourses
10
 * @author Jose Angel Ruiz <[email protected]>
11
 * @author Imanol Losada <[email protected]>
12
 * @author Alex Aragón <[email protected]>
13
 * @author Angel Fernando Quiroz Campos <[email protected]>
14
 * @author José Loguercio Silva  <[email protected]>
15
 */
16
class BuyCoursesPlugin extends Plugin
17
{
18
    const TABLE_PAYPAL = 'plugin_buycourses_paypal_account';
19
    const TABLE_CURRENCY = 'plugin_buycourses_currency';
20
    const TABLE_ITEM = 'plugin_buycourses_item';
21
    const TABLE_ITEM_BENEFICIARY = 'plugin_buycourses_item_rel_beneficiary';
22
    const TABLE_SALE = 'plugin_buycourses_sale';
23
    const TABLE_TRANSFER = 'plugin_buycourses_transfer';
24
    const TABLE_COMMISSION = 'plugin_buycourses_commission';
25
    const TABLE_PAYPAL_PAYOUTS = 'plugin_buycourses_paypal_payouts';
26
    const TABLE_SERVICES = 'plugin_buycourses_services';
27
    const TABLE_SERVICES_SALE = 'plugin_buycourses_service_sale';
28
    const TABLE_CULQI = 'plugin_buycourses_culqi';
29
    const PRODUCT_TYPE_COURSE = 1;
30
    const PRODUCT_TYPE_SESSION = 2;
31
    const PAYMENT_TYPE_PAYPAL = 1;
32
    const PAYMENT_TYPE_TRANSFER = 2;
33
    const PAYMENT_TYPE_CULQI = 3;
34
    const PAYOUT_STATUS_CANCELED = 2;
35
    const PAYOUT_STATUS_PENDING = 0;
36
    const PAYOUT_STATUS_COMPLETED = 1;
37
    const SALE_STATUS_CANCELED = -1;
38
    const SALE_STATUS_PENDING = 0;
39
    const SALE_STATUS_COMPLETED = 1;
40
    const SERVICE_STATUS_PENDING = 0;
41
    const SERVICE_STATUS_COMPLETED = 1;
42
    const SERVICE_STATUS_CANCELLED = -1;
43
    const SERVICE_TYPE_USER = 1;
44
    const SERVICE_TYPE_COURSE = 2;
45
    const SERVICE_TYPE_SESSION = 3;
46
47
    /**
48
     *
49
     * @return StaticPlugin
50
     */
51
    static function create()
52
    {
53
        static $result = null;
54
        return $result ? $result : $result = new self();
55
    }
56
57
    protected function __construct()
58
    {
59
        parent::__construct(
60
            '1.0',
61
            "
62
                Jose Angel Ruiz - NoSoloRed (original author) <br/>
63
                Francis Gonzales and Yannick Warnier - BeezNest (integration) <br/>
64
                Alex Aragón - BeezNest (Design icons and css styles) <br/>
65
                Imanol Losada - BeezNest (introduction of sessions purchase) <br/>
66
                Angel Fernando Quiroz Campos - BeezNest (cleanup and new reports) <br/>
67
                José Loguercio Silva - BeezNest (Payouts and buy Services)
68
            ",
69
            array(
70
                'show_main_menu_tab' => 'boolean',
71
                'include_sessions' => 'boolean',
72
                'include_services' => 'boolean',
73
                'paypal_enable' => 'boolean',
74
                'transfer_enable' => 'boolean',
75
                'culqi_enable' => 'boolean',
76
                'commissions_enable' => 'boolean',
77
                'unregistered_users_enable' => 'boolean'
78
            )
79
        );
80
    }
81
82
    /**
83
     * This method creates the tables required to this plugin
84
     */
85
    function install()
86
    {
87
        $tablesToBeCompared = array(
88
            self::TABLE_PAYPAL,
89
            self::TABLE_TRANSFER,
90
            self::TABLE_CULQI,
91
            self::TABLE_ITEM_BENEFICIARY,
92
            self::TABLE_ITEM,
93
            self::TABLE_SALE,
94
            self::TABLE_CURRENCY,
95
            self::TABLE_COMMISSION,
96
            self::TABLE_PAYPAL_PAYOUTS,
97
            self::TABLE_SERVICES,
98
            self::TABLE_SERVICES_SALE
99
        );
100
        $em = Database::getManager();
101
        $cn = $em->getConnection();
102
        $sm = $cn->getSchemaManager();
103
        $tables = $sm->tablesExist($tablesToBeCompared);
104
105
        if ($tables) {
106
            return false;
107
        }
108
109
        require_once api_get_path(SYS_PLUGIN_PATH) . 'buycourses/database.php';
110
    }
111
112
    /**
113
     * This method drops the plugin tables
114
     */
115
    function uninstall()
116
    {
117
        $tablesToBeDeleted = array(
118
            self::TABLE_PAYPAL,
119
            self::TABLE_TRANSFER,
120
            self::TABLE_CULQI,
121
            self::TABLE_ITEM_BENEFICIARY,
122
            self::TABLE_ITEM,
123
            self::TABLE_SALE,
124
            self::TABLE_CURRENCY,
125
            self::TABLE_COMMISSION,
126
            self::TABLE_PAYPAL_PAYOUTS,
127
            self::TABLE_SERVICES_SALE,
128
            self::TABLE_SERVICES
129
        );
130
131
        foreach ($tablesToBeDeleted as $tableToBeDeleted) {
132
            $table = Database::get_main_table($tableToBeDeleted);
133
            $sql = "DROP TABLE IF EXISTS $table";
134
            Database::query($sql);
135
        }
136
        $this->manageTab(false);
137
    }
138
139
    /**
140
     * This function verify if the plugin is enable and return the price info for a course or session in the new grid catalog
141
     * for 1.11.x , the main purpose is to show if a course or session is in sale it shows in the main platform course catalog
142
     * so the old buycourses plugin catalog can be deprecated.
143
     * @param int $productId course or session id
144
     * @param int $productType course or session type
145
     * @return mixed bool|string html
146
     */
147
    public function buyCoursesForGridCatalogVerificator($productId, $productType) {
148
        $return = [];
149
        $paypal = $this->get('paypal_enable') === 'true';
150
        $transfer = $this->get('transfer_enable') === 'true';
151
152
        if ($paypal || $transfer) {
153
            $item = $this->getItemByProduct(intval($productId), $productType);
154
            $return['html'] = '<div class="buycourses-price">';
155
            if ($item) {
156
                $return['html'] .= '<span class="label label-primary"><b>'. $item['iso_code'] .' ' . $item['price'] . '</b></span>';
157
                $return['verificator'] = true;
158
            } else {
159
                $return['html'] .= '<span class="label label-primary"><b>'. $this->get_lang('Free'). '</b></span>';
160
                $return['verificator'] = false;
161
            }
162
            $return['html'] .= '</div>';
163
        } else {
164
            return false;
165
        }
166
167
        return $return;
168
    }
169
170
    /**
171
     * Return the buyCourses plugin button to buy the course
172
     * @param int $productId
173
     * @param int $productType
174
     * @return string $html
175
     */
176
    public function returnBuyCourseButton($productId, $productType) {
177
        $url = api_get_path(WEB_PLUGIN_PATH) .
178
            'buycourses/src/process.php?i=' .
179
            intval($productId) .
180
            '&t=' .
181
            $productType
182
        ;
183
184
        $html = ' <a class="btn btn-success btn-sm" title="' . $this->get_lang('Buy') . '" href="' . $url . '">' .
185
            Display::returnFontAwesomeIcon('fa fa-shopping-cart') . '</a>';
186
187
        return $html;
188
    }
189
190
    /**
191
     * Get the currency for sales
192
     * @return array The selected currency. Otherwise return false
193
     */
194
    public function getSelectedCurrency()
195
    {
196
        return Database::select(
197
            '*',
198
            Database::get_main_table(BuyCoursesPlugin::TABLE_CURRENCY),
199
            [
200
                'where' => ['status = ?' => true]
201
            ],
202
            'first'
203
        );
204
    }
205
206
    /**
207
     * Get a list of currencies
208
     * @return array The currencies. Otherwise return false
209
     */
210
    public function getCurrencies()
211
    {
212
        return Database::select(
213
            '*',
214
            Database::get_main_table(BuyCoursesPlugin::TABLE_CURRENCY)
215
        );
216
    }
217
218
    /**
219
     * Save the selected currency
220
     * @param int $selectedId The currency Id
221
     */
222
    public function selectCurrency($selectedId)
223
    {
224
        $currencyTable = Database::get_main_table(
225
            BuyCoursesPlugin::TABLE_CURRENCY
226
        );
227
228
        Database::update(
229
            $currencyTable,
230
            ['status' => 0]
231
        );
232
        Database::update(
233
            $currencyTable,
234
            ['status' => 1],
235
            ['id = ?' => intval($selectedId)]
236
        );
237
    }
238
239
    /**
240
     * Save the PayPal configuration params
241
     * @param array $params
242
     * @return int Rows affected. Otherwise return false
243
     */
244
    public function savePaypalParams($params)
245
    {
246
        return Database::update(
247
            Database::get_main_table(BuyCoursesPlugin::TABLE_PAYPAL),
248
            [
249
                'username' => $params['username'],
250
                'password' => $params['password'],
251
                'signature' => $params['signature'],
252
                'sandbox' => isset($params['sandbox'])
253
            ],
254
            ['id = ?' => 1]
255
        );
256
    }
257
258
    /**
259
     * Gets the stored PayPal params
260
     * @return array
261
     */
262
    public function getPaypalParams()
263
    {
264
        return Database::select(
265
            '*',
266
            Database::get_main_table(BuyCoursesPlugin::TABLE_PAYPAL),
267
            ['id = ?' => 1],
268
            'first'
269
        );
270
    }
271
272
    /**
273
     * Save a transfer account information
274
     * @param array $params The transfer account
275
     * @return int Rows affected. Otherwise return false
276
     */
277
    public function saveTransferAccount($params)
278
    {
279
        return Database::insert(
280
            Database::get_main_table(self::TABLE_TRANSFER),
281
            [
282
                'name' => $params['tname'],
283
                'account' => $params['taccount'],
284
                'swift' => $params['tswift']
285
            ]
286
        );
287
    }
288
289
    /**
290
     * Get a list of transfer accounts
291
     * @return array
292
     */
293
    public function getTransferAccounts()
294
    {
295
        return Database::select(
296
            '*',
297
            Database::get_main_table(self::TABLE_TRANSFER)
298
        );
299
    }
300
301
    /**
302
     * Remove a transfer account
303
     * @param int $id The transfer account ID
304
     * @return int Rows affected. Otherwise return false
305
     */
306
    public function deleteTransferAccount($id)
307
    {
308
        return Database::delete(
309
            Database::get_main_table(self::TABLE_TRANSFER),
310
            ['id = ?' => intval($id)]
311
        );
312
    }
313
314
    /**
315
     * Filter the registered courses for show in plugin catalog
316
     * @return array
317
     */
318
    private function getCourses()
319
    {
320
        $entityManager = Database::getManager();
321
        $query = $entityManager->createQueryBuilder();
322
323
        $courses = $query
324
            ->select('c')
325
            ->from('ChamiloCoreBundle:Course', 'c')
326
            ->leftJoin(
327
                'ChamiloCoreBundle:SessionRelCourse',
328
                'sc',
329
                \Doctrine\ORM\Query\Expr\Join::WITH,
330
                'c = sc.course'
331
            )
332
            ->where(
333
                $query->expr()->isNull('sc.course')
334
            )
335
            ->getQuery()
336
            ->getResult();
337
338
        return $courses;
339
    }
340
341
    /**
342
     * Get the item data
343
     * @param int $productId The item ID
344
     * @param int $itemType The item type
345
     * @return array
346
     */
347
    public function getItemByProduct($productId, $itemType)
348
    {
349
        $buyItemTable = Database::get_main_table(BuyCoursesPlugin::TABLE_ITEM);
350
        $buyCurrencyTable = Database::get_main_table(BuyCoursesPlugin::TABLE_CURRENCY);
351
352
        $fakeItemFrom = "
353
            $buyItemTable i
354
            INNER JOIN $buyCurrencyTable c
355
                ON i.currency_id = c.id
356
        ";
357
358
        return Database::select(
359
            ['i.*', 'c.iso_code'],
360
            $fakeItemFrom,
361
            [
362
                'where' => [
363
                    'i.product_id = ? AND i.product_type = ?' => [
364
                        intval($productId),
365
                        intval($itemType)
366
                    ]
367
                ]
368
            ],
369
            'first'
370
        );
371
    }
372
373
    /**
374
     * List courses details from the configuration page
375
     * @return array
376
     */
377
    public function getCoursesForConfiguration()
378
    {
379
        $courses = $this->getCourses();
380
381
        if (empty($courses)) {
382
            return[];
383
        }
384
385
        $configurationCourses = [];
386
        $currency = $this->getSelectedCurrency();
387
388
        foreach ($courses as $course) {
389
            $configurationCourses[] = $this->getCourseForConfiguration($course, $currency);
390
        }
391
392
        return $configurationCourses;
393
    }
394
395
    /**
396
     * List sessions details from the buy-session table and the session table
397
     * @return array The sessions. Otherwise return false
398
     */
399
    public function getSessionsForConfiguration()
400
    {
401
        $auth = new Auth();
402
        $sessions = $auth->browseSessions();
403
404
        $currency = $this->getSelectedCurrency();
405
406
        $items = [];
407
408
        foreach ($sessions as $session) {
409
            $items[] = $this->getSessionForConfiguration($session, $currency);
410
        }
411
412
        return $items;
413
    }
414
415
    /**
416
     * Get the user status for the session
417
     * @param int $userId The user ID
418
     * @param Session $session The session
419
     * @return string
420
     */
421 View Code Duplication
    private function getUserStatusForSession($userId, Session $session)
422
    {
423
        if (empty($userId)) {
424
            return 'NO';
425
        }
426
427
        $entityManager = Database::getManager();
428
        $scuRepo = $entityManager->getRepository('ChamiloCoreBundle:SessionRelCourseRelUser');
429
430
        $buySaleTable = Database::get_main_table(self::TABLE_SALE);
431
432
        // Check if user bought the course
433
        $sale = Database::select(
434
            'COUNT(1) as qty',
435
            $buySaleTable,
436
            [
437
                'where' => [
438
                    'user_id = ? AND product_type = ? AND product_id = ? AND status = ?' => [
439
                        $userId,
440
                        self::PRODUCT_TYPE_SESSION,
441
                        $session->getId(),
442
                        self::SALE_STATUS_PENDING
443
                    ]
444
                ]
445
            ],
446
            'first'
447
        );
448
449
        if ($sale['qty'] > 0) {
450
            return "TMP";
451
        }
452
453
        // Check if user is already subscribe to session
454
        $userSubscription = $scuRepo->findBy([
455
            'session' => $session,
456
            'user' => $userId
457
        ]);
458
459
        if (!empty($userSubscription)) {
460
            return 'YES';
461
        }
462
463
        return 'NO';
464
    }
465
466
    /**
467
     * Lists current user session details, including each session course details
468
     * @param string $name Optional. The name filter
469
     * @param int $min Optional. The minimum price filter
470
     * @param int $max Optional. The maximum price filter
471
     * @return array
472
     */
473
    public function getCatalogSessionList($name = null, $min = 0, $max = 0)
474
    {
475
        $sessions = $this->filterSessionList($name, $min, $max);
476
477
        $sessionCatalog = array();
478
        // loop through all sessions
479
        foreach ($sessions as $session) {
480
            $sessionCourses = $session->getCourses();
481
482
            if (empty($sessionCourses)) {
483
                continue;
484
            }
485
486
            $item = $this->getItemByProduct($session->getId(), self::PRODUCT_TYPE_SESSION);
487
488
            if (empty($item)) {
489
                continue;
490
            }
491
492
            $sessionData = $this->getSessionInfo($session->getId());
493
            $sessionData['coach'] = $session->getGeneralCoach()->getCompleteName();
494
            $sessionData['enrolled'] = $this->getUserStatusForSession(api_get_user_id(), $session);
495
            $sessionData['courses'] = array();
496
497 View Code Duplication
            foreach ($sessionCourses as $sessionCourse) {
498
                $course = $sessionCourse->getCourse();
499
500
                $sessionCourseData = [
501
                    'title' => $course->getTitle(),
502
                    'coaches' => []
503
                ];
504
505
                $userCourseSubscriptions = $session->getUserCourseSubscriptionsByStatus(
506
                    $course,
507
                    Chamilo\CoreBundle\Entity\Session::COACH
508
                );
509
510
                foreach ($userCourseSubscriptions as $userCourseSubscription) {
511
                    $user = $userCourseSubscription->getUser();
512
                    $sessionCourseData['coaches'][] = $user->getCompleteName();
513
                }
514
515
                $sessionData['courses'][] = $sessionCourseData;
516
            }
517
518
            $sessionCatalog[] = $sessionData;
519
        }
520
521
        return $sessionCatalog;
522
    }
523
524
    /**
525
     * Get the user status for the course
526
     * @param int $userId The user Id
527
     * @param Course $course The course
528
     *
529
     * @return string
530
     */
531 View Code Duplication
    private function getUserStatusForCourse($userId, Course $course)
532
    {
533
        if (empty($userId)) {
534
            
535
            return 'NO';
536
        }
537
538
        $entityManager = Database::getManager();
539
        $cuRepo = $entityManager->getRepository('ChamiloCoreBundle:CourseRelUser');
540
541
        $buySaleTable = Database::get_main_table(self::TABLE_SALE);
542
543
        // Check if user bought the course
544
        $sale = Database::select(
545
            'COUNT(1) as qty',
546
            $buySaleTable,
547
            [
548
                'where' => [
549
                    'user_id = ? AND product_type = ? AND product_id = ? AND status = ?' => [
550
                        $userId,
551
                        self::PRODUCT_TYPE_COURSE,
552
                        $course->getId(),
553
                        self::SALE_STATUS_PENDING
554
                    ]
555
                ]
556
            ],
557
            'first'
558
        );
559
560
        if ($sale['qty'] > 0) {
561
            return "TMP";
562
        }
563
564
        // Check if user is already subscribe to course
565
        $userSubscription = $cuRepo->findBy([
566
            'course' => $course,
567
            'user' => $userId
568
        ]);
569
570
        if (!empty($userSubscription)) {
571
            return 'YES';
572
        }
573
574
        return 'NO';
575
    }
576
577
    /**
578
     * Lists current user course details
579
     * @param string $name Optional. The name filter
580
     * @param int $min Optional. The minimum price filter
581
     * @param int $max Optional. The maximum price filter
582
     * @return array
583
     */
584
    public function getCatalogCourseList($name = null, $min = 0, $max = 0)
585
    {
586
        $courses = $this->filterCourseList($name, $min, $max);
587
588
        if (empty($courses)) {
589
            return [];
590
        }
591
592
        $courseCatalog = [];
593
594
        foreach ($courses as $course) {
595
            $item = $this->getItemByProduct($course->getId(), self::PRODUCT_TYPE_COURSE);
596
597
            if (empty($item)) {
598
                continue;
599
            }
600
601
            $courseItem = [
602
                'id' => $course->getId(),
603
                'title' => $course->getTitle(),
604
                'code' => $course->getCode(),
605
                'course_img' => null,
606
                'price' => $item['price'],
607
                'currency' => $item['iso_code'],
608
                'teachers' => [],
609
                'enrolled' => $this->getUserStatusForCourse(api_get_user_id(), $course)
610
            ];
611
612
            foreach ($course->getTeachers() as $courseUser) {
613
                $teacher = $courseUser->getUser();
614
                $courseItem['teachers'][] = $teacher->getCompleteName();
615
            }
616
617
            //check images
618
            $possiblePath = api_get_path(SYS_COURSE_PATH);
619
            $possiblePath .= $course->getDirectory();
620
            $possiblePath .= '/course-pic.png';
621
622 View Code Duplication
            if (file_exists($possiblePath)) {
623
                $courseItem['course_img'] = api_get_path(WEB_COURSE_PATH)
624
                    . $course->getDirectory()
625
                    . '/course-pic.png';
626
            }
627
628
            $courseCatalog[] = $courseItem;
629
        }
630
631
        return $courseCatalog;
632
    }
633
634
    /**
635
     * Get course info
636
     * @param int $courseId The course ID
637
     * @return array
638
     */
639
    public function getCourseInfo($courseId)
640
    {
641
        $entityManager = Database::getManager();
642
        $course = $entityManager->find('ChamiloCoreBundle:Course', $courseId);
643
644
        if (empty($course)) {
645
            return [];
646
        }
647
648
        $item = $this->getItemByProduct($course->getId(), self::PRODUCT_TYPE_COURSE);
649
650
        if (empty($item)) {
651
            return [];
652
        }
653
654
        $courseInfo = [
655
            'id' => $course->getId(),
656
            'title' => $course->getTitle(),
657
            'code' => $course->getCode(),
658
            'visual_code' => $course->getVisualCode(),
659
            'teachers' => [],
660
            'price' => $item['price'],
661
            'currency' => $item['iso_code'],
662
            'course_img' => null
663
        ];
664
665
        $courseTeachers = $course->getTeachers();
666
667
        foreach ($courseTeachers as $teacher) {
668
            $courseInfo['teachers'][] = $teacher->getUser()->getCompleteName();
669
        }
670
671
        $possiblePath = api_get_path(SYS_COURSE_PATH);
672
        $possiblePath .= $course->getDirectory();
673
        $possiblePath .= '/course-pic.png';
674
675 View Code Duplication
        if (file_exists($possiblePath)) {
676
            $courseInfo['course_img'] = api_get_path(WEB_COURSE_PATH)
677
                . $course->getDirectory()
678
                . '/course-pic.png';
679
        }
680
681
        return $courseInfo;
682
    }
683
684
    /**
685
     * Get session info
686
     * @param array $sessionId The session ID
687
     * @return array
688
     */
689
    public function getSessionInfo($sessionId)
690
    {
691
        $entityManager = Database::getManager();
692
        $session = $entityManager->find('ChamiloCoreBundle:Session', $sessionId);
693
694
        if (empty($session)) {
695
            return [];
696
        }
697
698
        $item = $this->getItemByProduct($session->getId(), self::PRODUCT_TYPE_SESSION);
699
700
        if (empty($item)) {
701
            return [];
702
        }
703
704
        $sessionDates = SessionManager::parseSessionDates([
705
            'display_start_date' => $session->getDisplayStartDate(),
706
            'display_end_date' => $session->getDisplayEndDate(),
707
            'access_start_date' => $session->getAccessStartDate(),
708
            'access_end_date' => $session->getAccessEndDate(),
709
            'coach_access_start_date' => $session->getCoachAccessStartDate(),
710
            'coach_access_end_date' => $session->getCoachAccessEndDate()
711
        ]);
712
713
        $sessionInfo = [
714
            'id' => $session->getId(),
715
            'name' => $session->getName(),
716
            'dates' => $sessionDates,
717
            'courses' => [],
718
            'price' => $item['price'],
719
            'currency' => $item['iso_code'],
720
            'image' => null
721
        ];
722
723
        $fieldValue = new ExtraFieldValue('session');
724
        $sessionImage = $fieldValue->get_values_by_handler_and_field_variable(
725
            $session->getId(),
726
            'image'
727
        );
728
729
        if (!empty($sessionImage)) {
730
            $sessionInfo['image'] = api_get_path(WEB_UPLOAD_PATH) . $sessionImage['value'];
731
        }
732
733
        $sessionCourses = $session->getCourses();
734
735 View Code Duplication
        foreach ($sessionCourses as $sessionCourse) {
736
            $course = $sessionCourse->getCourse();
737
738
            $sessionCourseData = [
739
                'title' => $course->getTitle(),
740
                'coaches' => []
741
            ];
742
743
            $userCourseSubscriptions = $session->getUserCourseSubscriptionsByStatus(
744
                $course,
745
                Chamilo\CoreBundle\Entity\Session::COACH
746
            );
747
748
            foreach ($userCourseSubscriptions as $userCourseSubscription) {
749
                $user = $userCourseSubscription->getUser();
750
                $sessionCourseData['coaches'][] = $user->getCompleteName();
751
            }
752
753
            $sessionInfo['courses'][] = $sessionCourseData;
754
        }
755
756
        return $sessionInfo;
757
    }
758
759
    /**
760
     * Get registered item data
761
     * @param int $itemId The item ID
762
     * @return array
763
     */
764
    public function getItem($itemId)
765
    {
766
        return Database::select(
767
            '*',
768
            Database::get_main_table(self::TABLE_ITEM),
769
            [
770
                'where' => ['id = ?' => intval($itemId)]
771
            ],
772
            'first'
773
        );
774
    }
775
776
    /**
777
     * Register a sale
778
     * @param int $itemId The product ID
779
     * @param int $paymentType The payment type
780
     * @return boolean
781
     */
782
    public function registerSale($itemId, $paymentType)
783
    {
784
        if (!in_array($paymentType, [self::PAYMENT_TYPE_PAYPAL, self::PAYMENT_TYPE_TRANSFER])) {
785
            return false;
786
        }
787
788
        $entityManager = Database::getManager();
789
790
        $item = $this->getItem($itemId);
791
792
        if (empty($item)) {
793
            return false;
794
        }
795
796
        if ($item['product_type'] == self::PRODUCT_TYPE_COURSE) {
797
            $course = $entityManager->find('ChamiloCoreBundle:Course', $item['product_id']);
798
799
            if (empty($course)) {
800
                return false;
801
            }
802
803
            $productName = $course->getTitle();
804
        } elseif ($item['product_type'] == self::PRODUCT_TYPE_SESSION) {
805
            $session = $entityManager->find('ChamiloCoreBundle:Session', $item['product_id']);
806
807
            if (empty($session)) {
808
                return false;
809
            }
810
811
            $productName = $session->getName();
812
        }
813
814
        $values = [
815
            'reference' => $this->generateReference(
816
                api_get_user_id(),
817
                $item['product_type'],
818
                $item['product_id']
819
            ),
820
            'currency_id' => $item['currency_id'],
821
            'date' => api_get_utc_datetime(),
822
            'user_id' => api_get_user_id(),
823
            'product_type' => $item['product_type'],
824
            'product_name' => $productName,
825
            'product_id' => $item['product_id'],
826
            'price' => $item['price'],
827
            'status' => self::SALE_STATUS_PENDING,
828
            'payment_type' => intval($paymentType)
829
        ];
830
831
        return Database::insert(self::TABLE_SALE, $values);
832
    }
833
834
    /**
835
     * Get sale data by ID
836
     * @param int $saleId The sale ID
837
     * @return array
838
     */
839
    public function getSale($saleId)
840
    {
841
        return Database::select(
842
            '*',
843
            Database::get_main_table(self::TABLE_SALE),
844
            [
845
                'where' => ['id = ?' => intval($saleId)]
846
            ],
847
            'first'
848
        );
849
    }
850
851
    /**
852
     * Get a list of sales by the payment type
853
     * @param int $paymentType The payment type to filter (default : Paypal)
854
     * @return array The sale list. Otherwise return false
855
     */
856 View Code Duplication
    public function getSaleListByPaymentType($paymentType = self::PAYMENT_TYPE_PAYPAL)
857
    {
858
        $saleTable = Database::get_main_table(BuyCoursesPlugin::TABLE_SALE);
859
        $currencyTable = Database::get_main_table(BuyCoursesPlugin::TABLE_CURRENCY);
860
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
861
862
        $innerJoins = "
863
            INNER JOIN $currencyTable c ON s.currency_id = c.id
864
            INNER JOIN $userTable u ON s.user_id = u.id
865
        ";
866
867
        return Database::select(
868
            ['c.iso_code', 'u.firstname', 'u.lastname', 's.*'],
869
            "$saleTable s $innerJoins",
870
            [
871
                'where' => ['s.payment_type = ? AND s.status = ?' => [intval($paymentType), self::SALE_STATUS_COMPLETED]],
872
                'order' => 'id DESC'
873
            ]
874
        );
875
    }
876
877
    /**
878
     * Get currency data by ID
879
     * @param int $currencyId The currency ID
880
     * @return array
881
     */
882
    public function getCurrency($currencyId)
883
    {
884
        return Database::select(
885
            '*',
886
            Database::get_main_table(BuyCoursesPlugin::TABLE_CURRENCY),
887
            [
888
                'where' => ['id = ?' => intval($currencyId)]
889
            ],
890
            'first'
891
        );
892
    }
893
894
    /**
895
     * Update the sale status
896
     * @param int $saleId The sale ID
897
     * @param int $newStatus The new status
898
     * @return boolean
899
     */
900
    private function updateSaleStatus($saleId, $newStatus = self::SALE_STATUS_PENDING)
901
    {
902
        $saleTable = Database::get_main_table(self::TABLE_SALE);
903
904
        return Database::update(
905
            $saleTable,
906
            ['status' => intval($newStatus)],
907
            ['id = ?' => intval($saleId)]
908
        );
909
    }
910
911
    /**
912
     * Complete sale process. Update sale status to completed
913
     * @param int $saleId The sale ID
914
     * @return boolean
915
     */
916
    public function completeSale($saleId)
917
    {
918
        $sale = $this->getSale($saleId);
919
920
        if ($sale['status'] == self::SALE_STATUS_COMPLETED) {
921
            return true;
922
        }
923
924
        $saleIsCompleted = false;
925
926
        switch ($sale['product_type']) {
927
            case self::PRODUCT_TYPE_COURSE:
928
                $course = api_get_course_info_by_id($sale['product_id']);
929
930
                $saleIsCompleted = CourseManager::subscribe_user($sale['user_id'], $course['code']);
931
                break;
932
            case self::PRODUCT_TYPE_SESSION:
933
                SessionManager::subscribe_users_to_session(
934
                    $sale['product_id'],
935
                    [$sale['user_id']],
936
                    api_get_session_visibility($sale['product_id']),
937
                    false
938
                );
939
940
                $saleIsCompleted = true;
941
                break;
942
        }
943
944
        if ($saleIsCompleted) {
945
            $this->updateSaleStatus($sale['id'], self::SALE_STATUS_COMPLETED);
946
        }
947
948
        return $saleIsCompleted;
949
    }
950
951
    /**
952
     * Update sale status to canceled
953
     * @param int $saleId The sale ID
954
     */
955
    public function cancelSale($saleId)
956
    {
957
        $this->updateSaleStatus($saleId, self::SALE_STATUS_CANCELED);
958
    }
959
960
    /**
961
     * Get payment types
962
     * @return array
963
     */
964
    public function getPaymentTypes()
965
    {
966
        return [
967
            self::PAYMENT_TYPE_PAYPAL => 'PayPal',
968
            self::PAYMENT_TYPE_TRANSFER => $this->get_lang('BankTransfer'),
969
            self::PAYMENT_TYPE_CULQI => 'Culqi'
970
        ];
971
    }
972
973
    /**
974
     * Get a list of sales by the status
975
     * @param int $status The status to filter
976
     * @return array The sale list. Otherwise return false
977
     */
978 View Code Duplication
    public function getSaleListByStatus($status = self::SALE_STATUS_PENDING)
979
    {
980
        $saleTable = Database::get_main_table(BuyCoursesPlugin::TABLE_SALE);
981
        $currencyTable = Database::get_main_table(BuyCoursesPlugin::TABLE_CURRENCY);
982
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
983
984
        $innerJoins = "
985
            INNER JOIN $currencyTable c ON s.currency_id = c.id
986
            INNER JOIN $userTable u ON s.user_id = u.id
987
        ";
988
989
        return Database::select(
990
            ['c.iso_code', 'u.firstname', 'u.lastname', 's.*'],
991
            "$saleTable s $innerJoins",
992
            [
993
                'where' => ['s.status = ?' => intval($status)],
994
                'order' => 'id DESC'
995
            ]
996
        );
997
    }
998
999
    /**
1000
     * Get the statuses for sales
1001
     * @return array
1002
     */
1003
    public function getSaleStatuses()
1004
    {
1005
        return [
1006
            self::SALE_STATUS_CANCELED => $this->get_lang('SaleStatusCanceled'),
1007
            self::SALE_STATUS_PENDING => $this->get_lang('SaleStatusPending'),
1008
            self::SALE_STATUS_COMPLETED => $this->get_lang('SaleStatusCompleted')
1009
        ];
1010
    }
1011
1012
    /**
1013
     * Get the statuses for Payouts
1014
     * @return array
1015
     */
1016
    public function getPayoutStatuses()
1017
    {
1018
        return [
1019
            self::PAYOUT_STATUS_CANCELED => $this->get_lang('PayoutStatusCanceled'),
1020
            self::PAYOUT_STATUS_PENDING => $this->get_lang('PayoutStatusPending'),
1021
            self::PAYOUT_STATUS_COMPLETED => $this->get_lang('PayoutStatusCompleted')
1022
        ];
1023
    }
1024
1025
    /**
1026
     * Get the list of product types
1027
     * @return array
1028
     */
1029
    public function getProductTypes()
1030
    {
1031
        return [
1032
            self::PRODUCT_TYPE_COURSE => get_lang('Course'),
1033
            self::PRODUCT_TYPE_SESSION => get_lang('Session')
1034
        ];
1035
    }
1036
1037
    /**
1038
     * Search filtered sessions by name, and range of price
1039
     * @param string $name Optional. The name filter
1040
     * @param int $min Optional. The minimun price filter
1041
     * @param int $max Optional. The maximum price filter
1042
     * @return array
1043
     */
1044 View Code Duplication
    private function filterSessionList($name = null, $min = 0, $max = 0)
1045
    {
1046
        if (empty($name) && empty($min) && empty($max)) {
1047
            $auth = new Auth();
1048
            return $auth->browseSessions();
1049
        }
1050
1051
        $itemTable = Database::get_main_table(self::TABLE_ITEM);
1052
        $sessionTable = Database::get_main_table(TABLE_MAIN_SESSION);
1053
1054
        $min = floatval($min);
1055
        $max = floatval($max);
1056
1057
        $innerJoin = "$itemTable i ON s.id = i.product_id";
1058
        $whereConditions = [
1059
            'i.product_type = ? ' => self::PRODUCT_TYPE_SESSION
1060
        ];
1061
1062
        if (!empty($name)) {
1063
            $whereConditions['AND s.name LIKE %?%'] = $name;
1064
        }
1065
1066
        if (!empty($min)) {
1067
            $whereConditions['AND i.price >= ?'] = $min;
1068
        }
1069
1070
        if (!empty($max)) {
1071
            $whereConditions['AND i.price <= ?'] = $max;
1072
        }
1073
1074
        $sessionIds = Database::select(
1075
            's.id',
1076
            "$sessionTable s INNER JOIN $innerJoin",
1077
            ['where' => $whereConditions]
1078
        );
1079
1080
        if (!$sessionIds) {
1081
            return [];
1082
        }
1083
1084
        $sessions = [];
1085
1086
        foreach ($sessionIds as $sessionId) {
1087
            $sessions[] = Database::getManager()->find('ChamiloCoreBundle:Session', $sessionId);
1088
        }
1089
1090
        return $sessions;
1091
    }
1092
1093
    /**
1094
     * Search filtered courses by name, and range of price
1095
     * @param string $name Optional. The name filter
1096
     * @param int $min Optional. The minimun price filter
1097
     * @param int $max Optional. The maximum price filter
1098
     * @return array
1099
     */
1100 View Code Duplication
    private function filterCourseList($name = null, $min = 0, $max = 0)
1101
    {
1102
        if (empty($name) && empty($min) && empty($max)) {
1103
            return $this->getCourses();
1104
        }
1105
1106
        $itemTable = Database::get_main_table(self::TABLE_ITEM);
1107
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
1108
1109
        $min = floatval($min);
1110
        $max = floatval($max);
1111
1112
        $innerJoin = "$itemTable i ON c.id = i.product_id";
1113
        $whereConditions = [
1114
            'i.product_type = ? ' => self::PRODUCT_TYPE_COURSE
1115
        ];
1116
1117
        if (!empty($name)) {
1118
            $whereConditions['AND c.title LIKE %?%'] = $name;
1119
        }
1120
1121
        if (!empty($min)) {
1122
            $whereConditions['AND i.price >= ?'] = $min;
1123
        }
1124
1125
        if (!empty($max)) {
1126
            $whereConditions['AND i.price <= ?'] = $max;
1127
        }
1128
1129
        $courseIds = Database::select(
1130
            'c.id',
1131
            "$courseTable c INNER JOIN $innerJoin",
1132
            ['where' => $whereConditions]
1133
        );
1134
1135
        if (!$courseIds) {
1136
            return [];
1137
        }
1138
1139
        $courses = [];
1140
1141
        foreach ($courseIds as $courseId) {
1142
            $courses[] = Database::getManager()->find('ChamiloCoreBundle:Course', $courseId);
1143
        }
1144
1145
        return $courses;
1146
    }
1147
1148
    /**
1149
     * Generates a random text (used for order references)
1150
     * @param int $length Optional. Length of characters
1151
     * @param boolean $lowercase Optional. Include lowercase characters
1152
     * @param boolean $uppercase Optional. Include uppercase characters
1153
     * @param boolean $numbers Optional. Include numbers
1154
     * @return string
1155
     */
1156
    public static function randomText(
1157
        $length = 6,
1158
        $lowercase = true,
1159
        $uppercase = true,
1160
        $numbers = true
1161
    )
1162
    {
1163
        $salt = $lowercase ? 'abchefghknpqrstuvwxyz' : '';
1164
        $salt .= $uppercase ? 'ACDEFHKNPRSTUVWXYZ' : '';
1165
        $salt .= $numbers ? (strlen($salt) ? '2345679' : '0123456789') : '';
1166
1167
        if (strlen($salt) == 0) {
1168
            return '';
1169
        }
1170
1171
        $str = '';
1172
1173
        srand((double)microtime() * 1000000);
1174
1175
        for ($i = 0; $i < $length; $i++) {
1176
            $numbers = rand(0, strlen($salt) - 1);
1177
            $str .= substr($salt, $numbers, 1);
1178
        }
1179
1180
        return $str;
1181
    }
1182
1183
    /**
1184
     * Generates an order reference
1185
     * @param int $userId The user ID
1186
     * @param int $productType The course/session type
1187
     * @param int $productId The course/session ID
1188
     * @return string
1189
     */
1190
    public function generateReference($userId, $productType, $productId)
1191
    {
1192
        return vsprintf(
1193
            "%d-%d-%d-%s",
1194
            [$userId, $productType, $productId, self::randomText()]
1195
        );
1196
    }
1197
1198
    /**
1199
     * Get a list of sales by the user
1200
     * @param string $term The search term
1201
     * @return array The sale list. Otherwise return false
1202
     */
1203
    public function getSaleListByUser($term)
1204
    {
1205
        $term = trim($term);
1206
1207
        if (empty($term)) {
1208
            return [];
1209
        }
1210
1211
        $saleTable = Database::get_main_table(BuyCoursesPlugin::TABLE_SALE);
1212
        $currencyTable = Database::get_main_table(BuyCoursesPlugin::TABLE_CURRENCY);
1213
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
1214
1215
        $innerJoins = "
1216
            INNER JOIN $currencyTable c ON s.currency_id = c.id
1217
            INNER JOIN $userTable u ON s.user_id = u.id
1218
        ";
1219
1220
        return Database::select(
1221
            ['c.iso_code', 'u.firstname', 'u.lastname', 's.*'],
1222
            "$saleTable s $innerJoins",
1223
            [
1224
                'where' => [
1225
                    'u.username LIKE %?% OR ' => $term,
1226
                    'u.lastname LIKE %?% OR ' => $term,
1227
                    'u.firstname LIKE %?%' => $term
1228
                ],
1229
                'order' => 'id DESC'
1230
            ]
1231
        );
1232
    }
1233
1234
    /**
1235
     * Get a list of sales by the user id
1236
     * @param int $id The user id
1237
     * @return array The sale list. Otherwise return false
1238
     */
1239 View Code Duplication
    public function getSaleListByUserId($id)
1240
    {
1241
1242
        if (empty($id)) {
1243
            return [];
1244
        }
1245
1246
        $saleTable = Database::get_main_table(BuyCoursesPlugin::TABLE_SALE);
1247
        $currencyTable = Database::get_main_table(BuyCoursesPlugin::TABLE_CURRENCY);
1248
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
1249
1250
        $innerJoins = "
1251
            INNER JOIN $currencyTable c ON s.currency_id = c.id
1252
            INNER JOIN $userTable u ON s.user_id = u.id
1253
        ";
1254
1255
        return Database::select(
1256
            ['c.iso_code', 'u.firstname', 'u.lastname', 's.*'],
1257
            "$saleTable s $innerJoins",
1258
            [
1259
                'where' => [
1260
                    'u.id = ? AND s.status = ?' => [intval($id), BuyCoursesPlugin::SALE_STATUS_COMPLETED]
1261
                ],
1262
                'order' => 'id DESC'
1263
            ]
1264
        );
1265
    }
1266
1267
    /**
1268
     * Convert the course info to array with necessary course data for save item
1269
     * @param Course $course
1270
     * @param array $defaultCurrency Optional. Currency data
1271
     * @return array
1272
     */
1273
    public function getCourseForConfiguration(Course $course, $defaultCurrency = null)
1274
    {
1275
        $courseItem = [
1276
            'item_id' => null,
1277
            'course_id' => $course->getId(),
1278
            'course_visual_code' => $course->getVisualCode(),
1279
            'course_code' => $course->getCode(),
1280
            'course_title' => $course->getTitle(),
1281
            'course_visibility' => $course->getVisibility(),
1282
            'visible' => false,
1283
            'currency' =>  empty($defaultCurrency) ? null : $defaultCurrency['iso_code'],
1284
            'price' => 0.00
1285
        ];
1286
1287
        $item = $this->getItemByProduct($course->getId(), self::PRODUCT_TYPE_COURSE);
1288
1289 View Code Duplication
        if ($item !== false) {
1290
            $courseItem['item_id'] = $item['id'];
1291
            $courseItem['visible'] = true;
1292
            $courseItem['currency'] = $item['iso_code'];
1293
            $courseItem['price'] = $item['price'];
1294
        }
1295
1296
        return $courseItem;
1297
    }
1298
1299
    /**
1300
     * Convert the session info to array with necessary session data for save item
1301
     * @param Session $session The session data
1302
     * @param array $defaultCurrency Optional. Currency data
1303
     * @return array
1304
     */
1305
    public function getSessionForConfiguration(Session $session, $defaultCurrency = null)
1306
    {
1307
        $buyItemTable = Database::get_main_table(BuyCoursesPlugin::TABLE_ITEM);
1308
        $buyCurrencyTable = Database::get_main_table(BuyCoursesPlugin::TABLE_CURRENCY);
1309
1310
        $fakeItemFrom = "
1311
            $buyItemTable i
1312
            INNER JOIN $buyCurrencyTable c ON i.currency_id = c.id
1313
        ";
1314
1315
        $sessionItem = [
1316
            'item_id' => null,
1317
            'session_id' => $session->getId(),
1318
            'session_name' => $session->getName(),
1319
            'session_visibility' => $session->getVisibility(),
1320
            'session_display_start_date' => null,
1321
            'session_display_end_date' => null,
1322
            'visible' => false,
1323
            'currency' =>  empty($defaultCurrency) ? null : $defaultCurrency['iso_code'],
1324
            'price' => 0.00
1325
        ];
1326
1327
        $displayStartDate = $session->getDisplayStartDate();
1328
1329
        if (!empty($displayStartDate)) {
1330
            $sessionItem['session_display_start_date'] = api_format_date(
1331
                $session->getDisplayStartDate()->format('Y-m-d h:i:s')
1332
            );
1333
        }
1334
1335
        $displayEndDate = $session->getDisplayEndDate();
1336
1337
        if (!empty($displayEndDate)) {
1338
            $sessionItem['session_display_end_date'] = api_format_date(
1339
                $session->getDisplayEndDate()->format('Y-m-d h:i:s'),
1340
                DATE_TIME_FORMAT_LONG_24H
1341
            );
1342
        }
1343
1344
        $item = Database::select(
1345
            ['i.*', 'c.iso_code'],
1346
            $fakeItemFrom,
1347
            [
1348
                'where' => [
1349
                    'i.product_id = ? AND ' => $session->getId(),
1350
                    'i.product_type = ?' => self::PRODUCT_TYPE_SESSION
1351
                ]
1352
            ],
1353
            'first'
1354
        );
1355
1356 View Code Duplication
        if ($item !== false) {
1357
            $sessionItem['item_id'] = $item['id'];
1358
            $sessionItem['visible'] = true;
1359
            $sessionItem['currency'] = $item['iso_code'];
1360
            $sessionItem['price'] = $item['price'];
1361
        }
1362
1363
        return $sessionItem;
1364
    }
1365
1366
    /**
1367
     * Get all beneficiaries for a item
1368
     * @param int $itemId The item ID
1369
     * @return array The beneficiries. Otherwise return false
1370
     */
1371 View Code Duplication
    public function getItemBeneficiaries($itemId)
1372
    {
1373
        $beneficiaryTable = Database::get_main_table(self::TABLE_ITEM_BENEFICIARY);
1374
1375
        return Database::select(
1376
            '*',
1377
            $beneficiaryTable,
1378
            ['where' => [
1379
                'item_id = ?' => intval($itemId)
1380
            ]]
1381
        );
1382
    }
1383
1384
    /**
1385
     * Delete a item with its beneficiaries
1386
     * @param int $itemId The item ID
1387
     * @return int The number of affected rows. Otherwise return false
1388
     */
1389
    public function deleteItem($itemId)
1390
    {
1391
        $itemTable = Database::get_main_table(BuyCoursesPlugin::TABLE_ITEM);
1392
1393
        $affectedRows = Database::delete(
1394
            $itemTable,
1395
            ['id = ?' => intval($itemId)]
1396
        );
1397
1398
        if (!$affectedRows) {
1399
            return false;
1400
        }
1401
1402
        return $this->deleteItemBeneficiaries($itemId);
1403
    }
1404
1405
    /**
1406
     * Register a item
1407
     * @param array $itemData The item data
1408
     * @return int The item ID. Otherwise return false
1409
     */
1410
    public function registerItem(array $itemData)
1411
    {
1412
        $itemTable = Database::get_main_table(BuyCoursesPlugin::TABLE_ITEM);
1413
1414
        return Database::insert($itemTable, $itemData);
1415
    }
1416
1417
    /**
1418
     * Update the item data by product
1419
     * @param array $itemData The item data to be updated
1420
     * @param int $productId The product ID
1421
     * @param int $productType The type of product
1422
     * @return int The number of affected rows. Otherwise return false
1423
     */
1424 View Code Duplication
    public function updateItem(array $itemData, $productId, $productType)
1425
    {
1426
        $itemTable = Database::get_main_table(BuyCoursesPlugin::TABLE_ITEM);
1427
1428
        return Database::update(
1429
            $itemTable,
1430
            $itemData,
1431
            [
1432
                'product_id = ? AND ' => intval($productId),
1433
                'product_type' => $productType
1434
            ]
1435
        );
1436
    }
1437
1438
    /**
1439
     * Remove all beneficiaries for a item
1440
     * @param int $itemId The user ID
1441
     * @return int The number of affected rows. Otherwise return false
1442
     */
1443 View Code Duplication
    public function deleteItemBeneficiaries($itemId)
1444
    {
1445
        $beneficiaryTable = Database::get_main_table(BuyCoursesPlugin::TABLE_ITEM_BENEFICIARY);
1446
1447
        return Database::delete(
1448
            $beneficiaryTable,
1449
            ['item_id = ?' => intval($itemId)]
1450
        );
1451
    }
1452
1453
    /**
1454
     * Register the beneficiaries users with the sale of item
1455
     * @param int $itemId The item ID
1456
     * @param array $userIds The beneficiary user ID and Teachers commissions if enabled
1457
     */
1458
    public function registerItemBeneficiaries($itemId, array $userIds)
1459
    {
1460
        $beneficiaryTable = Database::get_main_table(BuyCoursesPlugin::TABLE_ITEM_BENEFICIARY);
1461
1462
        $this->deleteItemBeneficiaries($itemId);
1463
1464 View Code Duplication
        foreach ($userIds as $userId => $commissions) {
1465
            Database::insert(
1466
                $beneficiaryTable,
1467
                [
1468
                    'item_id' => intval($itemId),
1469
                    'user_id' => intval($userId),
1470
                    'commissions' => intval($commissions)
1471
                ]
1472
            );
1473
        }
1474
    }
1475
1476
    /**
1477
     * Check if a course is valid for sale
1478
     * @param Course $course The course
1479
     * @return boolean
1480
     */
1481
    public function isValidCourse(Course $course)
1482
    {
1483
        $courses = $this->getCourses();
1484
1485
        foreach ($courses as $_c) {
1486
            if ($_c->getCode() === $course->getCode()) {
1487
                return true;
1488
            }
1489
        }
1490
1491
        return false;
1492
    }
1493
1494
    /**
1495
     * Gets the beneficiaries with commissions and current paypal accounts by sale
1496
     * @param int $saleId The sale ID
1497
     * @return array
1498
     */
1499
    public function getBeneficiariesBySale($saleId)
1500
    {
1501
1502
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
1503
1504
        $beneficiaries = [];
1505
        $sale = $this->getSale($saleId);
1506
        $item = $this->getItemByProduct($sale['product_id'], $sale['product_type']);
1507
        $itemBeneficiaries = $this->getItemBeneficiaries($item['id']);
1508
        return $itemBeneficiaries;
1509
1510
    }
1511
1512
    /**
1513
     * gets all payouts
1514
     * @param int $status - default 0 - pending
1515
     * @param int $payoutId - for get an individual payout if want all then false
1516
     * @return array
1517
     */
1518
    public function getPayouts($status = self::PAYOUT_STATUS_PENDING, $payoutId = false, $userId = false)
1519
    {
1520
        $condition = ($payoutId) ? 'AND p.id = '. intval($payoutId) : '';
1521
        $condition2 = ($userId) ? ' AND p.user_id = ' . intval($userId) : '';
1522
        $typeResult = ($condition) ? 'first' : 'all';
1523
        $payoutsTable = Database::get_main_table(BuyCoursesPlugin::TABLE_PAYPAL_PAYOUTS);
1524
        $saleTable = Database::get_main_table(BuyCoursesPlugin::TABLE_SALE);
1525
        $currencyTable = Database::get_main_table(BuyCoursesPlugin::TABLE_CURRENCY);
1526
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
1527
        $extraFieldTable = Database::get_main_table(TABLE_EXTRA_FIELD);
1528
        $extraFieldValues = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
1529
1530
        $paypalExtraField = Database::select(
1531
            "*",
1532
            $extraFieldTable,
1533
            [
1534
                'where' => ['variable = ?' => 'paypal']
1535
            ],
1536
            'first'
1537
        );
1538
1539
        if (!$paypalExtraField) {
1540
            return false;
1541
        }
1542
1543
        $innerJoins = "
1544
            INNER JOIN $userTable u ON p.user_id = u.id
1545
            INNER JOIN $saleTable s ON s.id = p.sale_id
1546
            INNER JOIN $currencyTable c ON s.currency_id = c.id
1547
            LEFT JOIN  $extraFieldValues efv ON p.user_id = efv.item_id 
1548
            AND field_id = " . intval($paypalExtraField['id']) . "
1549
        ";
1550
1551
        $payouts = Database::select(
1552
            "p.* , u.firstname, u.lastname, efv.value as paypal_account, s.reference as sale_reference, s.price as item_price, c.iso_code",
1553
            "$payoutsTable p $innerJoins",
1554
            [
1555
                'where' => ['p.status = ? '.$condition . ' ' .$condition2 => $status]
1556
            ],
1557
            $typeResult
1558
        );
1559
1560
        return $payouts;
1561
    }
1562
1563
    /**
1564
     * Verify if the beneficiary have a paypal account
1565
     * @param int $userId
1566
     * @return true if the user have a paypal account, false if not
1567
     */
1568
    public function verifyPaypalAccountByBeneficiary($userId)
1569
    {
1570
        $extraFieldTable = Database::get_main_table(TABLE_EXTRA_FIELD);
1571
        $extraFieldValues = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
1572
1573
        $paypalExtraField = Database::select(
1574
            "*",
1575
            $extraFieldTable,
1576
            [
1577
                'where' => ['variable = ?' => 'paypal']
1578
            ],
1579
            'first'
1580
        );
1581
1582
        if (!$paypalExtraField) {
1583
            return false;
1584
        }
1585
1586
        $paypalFieldId = $paypalExtraField['id'];
1587
1588
        $paypalAccount = Database::select(
1589
            "value",
1590
            $extraFieldValues,
1591
            [
1592
                'where' => ['field_id = ? AND item_id = ?' => [intval($paypalFieldId), intval($userId)]]
1593
            ],
1594
            'first'
1595
        );
1596
1597
        if (!$paypalAccount) {
1598
            return false;
1599
        }
1600
1601
        if ($paypalAccount['value'] === '') {
1602
            return false;
1603
        }
1604
1605
        return true;
1606
    }
1607
1608
    /**
1609
     * Register the users payouts
1610
     * @param int $saleId The sale ID
1611
     * @return array
1612
     */
1613
    public function storePayouts($saleId)
1614
    {
1615
        $payoutsTable = Database::get_main_table(BuyCoursesPlugin::TABLE_PAYPAL_PAYOUTS);
1616
        $platformCommission = $this->getPlatformCommission();
1617
1618
        $sale = $this->getSale($saleId);
1619
        $teachersCommission = number_format((floatval($sale['price']) * intval($platformCommission['commission']))/100, 2);
1620
1621
1622
        $beneficiaries = $this->getBeneficiariesBySale($saleId);
1623
        foreach ($beneficiaries as $beneficiary) {
1624
            Database::insert(
1625
                $payoutsTable,
1626
                [
1627
                    'date' => $sale['date'],
1628
                    'payout_date' => getdate(),
1629
                    'sale_id' => intval($saleId),
1630
                    'user_id' => $beneficiary['user_id'],
1631
                    'commission' => number_format((floatval($teachersCommission) * intval($beneficiary['commissions']))/100, 2),
1632
                    'status' => self::PAYOUT_STATUS_PENDING
1633
                ]
1634
            );
1635
        }
1636
    }
1637
1638
    /**
1639
     * Register the users payouts
1640
     * @param int $payoutId The payout ID
1641
     * @param int $status The status to set (-1 to cancel, 0 to pending, 1 to completed)
1642
     * @return array
1643
     */
1644 View Code Duplication
    public function setStatusPayouts($payoutId, $status)
1645
    {
1646
        $payoutsTable = Database::get_main_table(BuyCoursesPlugin::TABLE_PAYPAL_PAYOUTS);
1647
1648
        Database::update(
1649
            $payoutsTable,
1650
            ['status' => intval($status)],
1651
            ['id = ?' => intval($payoutId)]
1652
        );
1653
1654
    }
1655
1656
    /**
1657
     * Gets the stored platform commission params
1658
     * @return array
1659
     */
1660
    public function getPlatformCommission()
1661
    {
1662
        return Database::select(
1663
            '*',
1664
            Database::get_main_table(BuyCoursesPlugin::TABLE_COMMISSION),
1665
            ['id = ?' => 1],
1666
            'first'
1667
        );
1668
    }
1669
1670
    /**
1671
     * Update the platform commission
1672
     * @param int $params platform commission
1673
     * @return int The number of affected rows. Otherwise return false
1674
     */
1675 View Code Duplication
    public function updateCommission($params)
1676
    {
1677
        $commissionTable = Database::get_main_table(BuyCoursesPlugin::TABLE_COMMISSION);
1678
1679
        return Database::update(
1680
            $commissionTable,
1681
            ['commission' => intval($params['commission'])]
1682
        );
1683
    }
1684
1685
    /**
1686
     * Register addicional service
1687
     * @param array params $service
1688
     * @return mixed response
1689
     */
1690
    public function storeService($service)
1691
    {
1692
        $servicesTable = Database::get_main_table(BuyCoursesPlugin::TABLE_SERVICES);
1693
1694
        $return = Database::insert(
1695
            $servicesTable,
1696
            [
1697
                'name' => Security::remove_XSS($service['name']),
1698
                'description' => Security::remove_XSS($service['description']),
1699
                'price' => $service['price'],
1700
                'duration_days' => intval($service['duration_days']),
1701
                'applies_to' => intval($service['applies_to']),
1702
                'owner_id' => intval($service['owner_id']),
1703
                'visibility' => intval($service['visibility']),
1704
                'image' => 'simg.png',
1705
                'video_url' => $service['video_url'],
1706
                'service_information' => $service['service_information']
1707
            ]
1708
        );
1709
1710
        if ($return) {
1711
            $img = str_replace('data:image/png;base64,', '', $service['picture_crop_image_base_64']);
1712
            $img = str_replace(' ', '+', $img);
1713
            $data = base64_decode($img);
1714
            $file = api_get_path(SYS_PLUGIN_PATH).'buycourses/uploads/services/images/simg-'.$return.'.png';
1715
            file_put_contents($file, $data);
1716
1717
            Database::update(
1718
                $servicesTable,
1719
                ['image' => 'simg-'.$return.'.png'],
1720
                ['id = ?' => intval($return)]
1721
            );
1722
            return $return;
1723
        }
1724
1725
        return false;
1726
    }
1727
1728
    /**
1729
     * update a service
1730
     * @param array $service
1731
     * @param integer $id
1732
     * @return mixed response
1733
     */
1734
    public function updateService($service, $id)
1735
    {
1736
        $servicesTable = Database::get_main_table(BuyCoursesPlugin::TABLE_SERVICES);
1737
        if (!empty($service['picture_crop_image_base_64'])) {
1738
            $img = str_replace('data:image/png;base64,', '', $service['picture_crop_image_base_64']);
1739
            $img = str_replace(' ', '+', $img);
1740
            $data = base64_decode($img);
1741
            $file = api_get_path(SYS_PLUGIN_PATH).'buycourses/uploads/services/images/simg-'.$id.'.png';
1742
            file_put_contents($file, $data);
1743
        }
1744
1745
        return Database::update(
1746
            $servicesTable,
1747
            [
1748
                'name' => Security::remove_XSS($service['name']),
1749
                'description' => Security::remove_XSS($service['description']),
1750
                'price' => $service['price'],
1751
                'duration_days' => intval($service['duration_days']),
1752
                'applies_to' => intval($service['applies_to']),
1753
                'owner_id' => intval($service['owner_id']),
1754
                'visibility' => intval($service['visibility']),
1755
                'image' => 'simg-'.$id.'.png',
1756
                'video_url' => $service['video_url'],
1757
                'service_information' => $service['service_information']
1758
            ],
1759
            ['id = ?' => intval($id)]
1760
        );
1761
    }
1762
1763
    /**
1764
     * Remove a service
1765
     * @param int $id The transfer account ID
1766
     * @return int Rows affected. Otherwise return false
1767
     */
1768
    public function deleteService($id)
1769
    {
1770
        return Database::delete(
1771
            Database::get_main_table(self::TABLE_SERVICES),
1772
            ['id = ?' => intval($id)]
1773
        );
1774
    }
1775
1776
    /**
1777
     * List adicional services
1778
     * @param integer $id service id
1779
     * @return array
1780
     */
1781
    public function getServices($id = null)
1782
    {
1783
        $servicesTable = Database::get_main_table(BuyCoursesPlugin::TABLE_SERVICES);
1784
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
1785
1786
        $conditions = null;
1787
        $showData = "all";
1788
1789
        if ($id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $id of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1790
            $conditions = ['WHERE' => ['s.id = ?' => $id]];
1791
            $showData = "first";
1792
        }
1793
1794
        $innerJoins = "INNER JOIN $userTable u ON s.owner_id = u.id";
1795
        $currency = $this->getSelectedCurrency();
1796
        $isoCode = $currency['iso_code'];
1797
        $return = Database::select(
1798
            "s.*, '$isoCode' as currency, u.firstname, u.lastname",
1799
            "$servicesTable s $innerJoins",
1800
            $conditions,
0 ignored issues
show
Bug introduced by
It seems like $conditions defined by null on line 1786 can also be of type null; however, Database::select() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1801
            $showData
1802
        );
1803
1804
        $services = [];
1805
1806
        if ($id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $id of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1807
            $services['id'] = $return['id'];
1808
            $services['name'] = $return['name'];
1809
            $services['description'] = $return['description'];
1810
            $services['price'] = $return['price'];
1811
            $services['currency'] = $return['currency'];
1812
            $services['duration_days'] = $return['duration_days'];
1813
            $services['applies_to'] = $return['applies_to'];
1814
            $services['owner_id'] = $return['owner_id'];
1815
            $services['owner_name'] = api_get_person_name($return['firstname'], $return['lastname']);
1816
            $services['visibility'] = $return['visibility'];
1817
            $services['image'] = $return['image'];
1818
            $services['video_url'] = $return['video_url'];
1819
            $services['service_information'] = $return['service_information'];
1820
1821
            return $services;
1822
        }
1823
1824
        foreach ($return as $index => $service) {
1825
            $services[$index]['id'] = $service['id'];
1826
            $services[$index]['name'] = $service['name'];
1827
            $services[$index]['description'] = $service['description'];
1828
            $services[$index]['price'] = $service['price'];
1829
            $services[$index]['currency'] = $service['currency'];
1830
            $services[$index]['duration_days'] = $service['duration_days'];
1831
            $services[$index]['applies_to'] = $service['applies_to'];
1832
            $services[$index]['owner_id'] = $service['owner_id'];
1833
            $services[$index]['owner_name'] = api_get_person_name($service['firstname'], $service['lastname']);
1834
            $services[$index]['visibility'] = $service['visibility'];
1835
            $services[$index]['image'] = $service['image'];
1836
            $services[$index]['video_url'] = $service['video_url'];
1837
            $services[$index]['service_information'] = $service['service_information'];
1838
        }
1839
1840
        return $services;
1841
    }
1842
1843
    /**
1844
     * Get the statuses for sales
1845
     * @return array
1846
     */
1847
    public function getServiceSaleStatuses()
1848
    {
1849
        return [
1850
            self::SERVICE_STATUS_CANCELLED => $this->get_lang('SaleStatusCancelled'),
1851
            self::SERVICE_STATUS_PENDING => $this->get_lang('SaleStatusPending'),
1852
            self::SERVICE_STATUS_COMPLETED => $this->get_lang('SaleStatusCompleted')
1853
        ];
1854
    }
1855
1856
    /**
1857
     * List services sales
1858
     * @param integer $id service id
1859
     * @param integer $buyerId buyer id
1860
     * @param integer $status status
1861
     * @param integer $nodeType The node Type ( User = 1 , Course = 2 , Session = 3 )
1862
     * @param integer $nodeId the nodeId
1863
     * @param boolean $hot enable hot services
1864
     * @return array
1865
     */
1866
    public function getServiceSale($id = null, $buyerId = null, $status = null, $nodeType = null, $nodeId = null, $hot = false)
1867
    {
1868
        $servicesTable = Database::get_main_table(BuyCoursesPlugin::TABLE_SERVICES);
1869
        $servicesSaleTable = Database::get_main_table(BuyCoursesPlugin::TABLE_SERVICES_SALE);
1870
1871
        $conditions = null;
1872
        $showData = "all";
1873
        $groupBy = "";
1874
1875
        if ($id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $id of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1876
            $conditions = ['WHERE' => ['ss.id = ?' => $id]];
1877
            $showData = "first";
1878
        }
1879
1880
        if ($buyerId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $buyerId of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1881
            $conditions = ['WHERE' => ['ss.buyer_id = ?' => $buyerId], 'ORDER' => 'id ASC'];
1882
        }
1883
1884
        if (is_numeric($status)) {
1885
            $conditions = ['WHERE' => ['ss.status = ?' => $status], 'ORDER' => 'id ASC'];
1886
        }
1887
1888
        if ($id && $buyerId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $id of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
Bug Best Practice introduced by
The expression $buyerId of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1889
            $conditions = ['WHERE' => ['ss.id = ? AND ss.buyer_id = ?' => [$id, $buyerId]], 'ORDER' => 'id ASC'];
1890
        }
1891
1892
        if ($nodeType && $nodeId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $nodeType of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
Bug Best Practice introduced by
The expression $nodeId of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1893
            $conditions = ['WHERE' => ['ss.node_type = ? AND ss.node_id = ?' => [$nodeType, $nodeId]], 'ORDER' => 'id ASC'];
1894
        }
1895
1896
        if ($hot) {
1897
            $hot = "count(ss.service_id) as hot, ";
1898
            $conditions = ['ORDER' => 'hot DESC', 'LIMIT' => '6'];
1899
            $groupBy = "GROUP BY ss.service_id";
1900
            "clean_teacher_files.php";
1901
        }
1902
1903
        $innerJoins = "INNER JOIN $servicesTable s ON ss.service_id = s.id $groupBy";
1904
        $currency = $this->getSelectedCurrency();
1905
        $isoCode = $currency['iso_code'];
1906
        $return = Database::select(
1907
            "ss.*, s.name, s.description, s.price as service_price, s.duration_days, s.applies_to, s.owner_id, s.visibility, s.image, $hot '$isoCode' as currency",
1908
            "$servicesSaleTable ss $innerJoins",
1909
            $conditions,
0 ignored issues
show
Bug introduced by
It seems like $conditions defined by null on line 1871 can also be of type null; however, Database::select() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1910
            $showData
1911
        );
1912
1913
        $servicesSale = [];
1914
1915
        if ($id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $id of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1916
1917
            $owner = api_get_user_info($return['owner_id']);
1918
            $buyer = api_get_user_info($return['buyer_id']);
1919
1920
            $servicesSale['id'] = $return['id'];
1921
            $servicesSale['service']['id'] = $return['service_id'];
1922
            $servicesSale['service']['name'] = $return['name'];
1923
            $servicesSale['service']['description'] = $return['description'];
1924
            $servicesSale['service']['price'] = $return['service_price'];
1925
            $servicesSale['service']['duration_days'] = $return['duration_days'];
1926
            $servicesSale['service']['applies_to'] = $return['applies_to'];
1927
            $servicesSale['service']['owner']['id'] = $return['owner_id'];
1928
            $servicesSale['service']['owner']['name'] = api_get_person_name($owner['firstname'], $owner['lastname']);
1929
            $servicesSale['service']['visibility'] = $return['visibility'];
1930
            $servicesSale['service']['image'] = $return['image'];
1931
            $servicesSale['reference'] = $return['reference'];
1932
            $servicesSale['currency_id'] = $return['currency_id'];
1933
            $servicesSale['currency'] = $return['currency'];
1934
            $servicesSale['price'] = $return['price'];
1935
            $servicesSale['node_type'] = $return['node_type'];
1936
            $servicesSale['node_id'] = $return['node_id'];
1937
            $servicesSale['buyer']['id'] = $buyer['user_id'];
1938
            $servicesSale['buyer']['name'] = api_get_person_name($buyer['firstname'], $buyer['lastname']);
1939
            $servicesSale['buyer']['username'] = $buyer['username'];
1940
            $servicesSale['buy_date'] = $return['buy_date'];
1941
            $servicesSale['date_start'] = $return['date_start'];
1942
            $servicesSale['date_end'] = $return['date_end'];
1943
            $servicesSale['status'] = $return['status'];
1944
            $servicesSale['payment_type'] = $return['payment_type'];
1945
1946
            return $servicesSale;
1947
        }
1948
1949
1950
        foreach ($return as $index => $service) {
1951
1952
            $owner = api_get_user_info($service['owner_id']);
1953
            $buyer = api_get_user_info($service['buyer_id']);
1954
1955
            $servicesSale[$index]['id'] = $service['id'];
1956
            $servicesSale[$index]['service']['id'] = $service['service_id'];
1957
            $servicesSale[$index]['service']['name'] = $service['name'];
1958
            $servicesSale[$index]['service']['description'] = $service['description'];
1959
            $servicesSale[$index]['service']['price'] = $service['service_price'];
1960
            $servicesSale[$index]['service']['duration_days'] = $service['duration_days'];
1961
            $servicesSale[$index]['service']['applies_to'] = $service['applies_to'];
1962
            $servicesSale[$index]['service']['owner']['id'] = $service['owner_id'];
1963
            $servicesSale[$index]['service']['owner']['name'] = api_get_person_name($owner['firstname'], $owner['lastname']);
1964
            $servicesSale[$index]['service']['visibility'] = $service['visibility'];
1965
            $servicesSale[$index]['service']['image'] = $service['image'];
1966
            $servicesSale[$index]['reference'] = $service['reference'];
1967
            $servicesSale[$index]['currency_id'] = $service['currency_id'];
1968
            $servicesSale[$index]['currency'] = $service['currency'];
1969
            $servicesSale[$index]['price'] = $service['price'];
1970
            $servicesSale[$index]['node_type'] = $service['node_type'];
1971
            $servicesSale[$index]['node_id'] = $service['node_id'];
1972
            $servicesSale[$index]['buyer']['id'] = $service['buyer_id'];
1973
            $servicesSale[$index]['buyer']['name'] = api_get_person_name($buyer['firstname'], $buyer['lastname']);
1974
            $servicesSale[$index]['buyer']['username'] = $buyer['username'];
1975
            $servicesSale[$index]['buy_date'] = $service['buy_date'];
1976
            $servicesSale[$index]['date_start'] = $service['date_start'];
1977
            $servicesSale[$index]['date_end'] = $service['date_end'];
1978
            $servicesSale[$index]['status'] = $service['status'];
1979
            $servicesSale[$index]['payment_type'] = $service['payment_type'];
1980
        }
1981
1982
        return $servicesSale;
1983
    }
1984
1985
    /**
1986
     * Update service sale status to cancelled
1987
     * @param int $serviceSaleId The sale ID
1988
     * @return boolean
1989
     */
1990
    public function cancelServiceSale($serviceSaleId)
1991
    {
1992
        $this->updateServiceSaleStatus($serviceSaleId, self::SERVICE_STATUS_CANCELLED);
1993
        return true;
1994
    }
1995
1996
    /**
1997
     * Complete service sale process. Update service sale status to completed
1998
     * @param int $serviceSaleId The service sale ID
1999
     * @return boolean
2000
     */
2001
    public function completeServiceSale($serviceSaleId)
2002
    {
2003
        $serviceSale = $this->getServiceSale($serviceSaleId);
2004
2005
        if ($serviceSale['status'] == self::SERVICE_STATUS_COMPLETED) {
2006
            return true;
2007
        }
2008
2009
        $this->updateServiceSaleStatus($serviceSaleId, self::SERVICE_STATUS_COMPLETED);
2010
2011
        return true;
2012
    }
2013
2014
    /**
2015
     * Lists current service details
2016
     * @param string $name Optional. The name filter
2017
     * @param int $min Optional. The minimum price filter
2018
     * @param int $max Optional. The maximum price filter
2019
     * @param mixed $appliesTo Optional.
2020
     * @return array
2021
     */
2022
    public function getCatalogServiceList($name = null, $min = 0, $max = 0, $appliesTo = '')
2023
    {
2024
        $servicesTable = Database::get_main_table(BuyCoursesPlugin::TABLE_SERVICES);
2025
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
2026
2027
        $whereConditions = [
2028
            's.id <> ? ' => 0
2029
        ];
2030
2031
        if (!empty($name)) {
2032
            $whereConditions['AND s.name LIKE %?%'] = $name;
2033
        }
2034
2035
        if (!empty($min)) {
2036
            $whereConditions['AND s.price >= ?'] = $min;
2037
        }
2038
2039
        if (!empty($max)) {
2040
            $whereConditions['AND s.price <= ?'] = $max;
2041
        }
2042
2043
        if (!$appliesTo == '') {
2044
            $whereConditions['AND s.applies_to = ?'] = $appliesTo;
2045
        }
2046
2047
        $innerJoins = "INNER JOIN $userTable u ON s.owner_id = u.id";
2048
        $currency = $this->getSelectedCurrency();
2049
        $isoCode = $currency['iso_code'];
2050
        $return = Database::select(
2051
            "s.*, '$isoCode' as currency, u.firstname, u.lastname",
2052
            "$servicesTable s $innerJoins",
2053
            ['WHERE' => $whereConditions]
2054
        );
2055
2056
        $services = [];
2057
2058
        foreach ($return as $index => $service) {
2059
            $services[$index]['id'] = $service['id'];
2060
            $services[$index]['name'] = $service['name'];
2061
            $services[$index]['description'] = $service['description'];
2062
            $services[$index]['price'] = $service['price'];
2063
            $services[$index]['currency'] = $service['currency'];
2064
            $services[$index]['duration_days'] = $service['duration_days'];
2065
            $services[$index]['applies_to'] = $service['applies_to'];
2066
            $services[$index]['owner_id'] = $service['owner_id'];
2067
            $services[$index]['owner_name'] = api_get_person_name($service['firstname'], $service['lastname']);
2068
            $services[$index]['visibility'] = $service['visibility'];
2069
            $services[$index]['image'] = api_get_path(WEB_PLUGIN_PATH).'buycourses/uploads/services/images/'.$service['image'];
2070
            $services[$index]['video_url'] = $service['video_url'];
2071
            $services[$index]['service_information'] = $service['service_information'];
2072
        }
2073
2074
        return $services;
2075
2076
    }
2077
2078
    /**
2079
     * Update the service sale status
2080
     * @param int $serviceSaleId The service sale ID
2081
     * @param int $newStatus The new status
2082
     * @return boolean
2083
     */
2084 View Code Duplication
    private function updateServiceSaleStatus($serviceSaleId, $newStatus = self::SERVICE_STATUS_PENDING)
2085
    {
2086
        $serviceSaleTable = Database::get_main_table(self::TABLE_SERVICES_SALE);
2087
2088
        return Database::update(
2089
            $serviceSaleTable,
2090
            ['status' => intval($newStatus)],
2091
            ['id = ?' => intval($serviceSaleId)]
2092
        );
2093
    }
2094
2095
    /**
2096
     * Register a Service sale
2097
     * @param int $serviceId The service ID
2098
     * @param int $paymentType The payment type
2099
     * @param int $infoSelect The ID for Service Type
2100
     * @param int $trial trial mode
2101
     * @return boolean
2102
     */
2103
    public function registerServiceSale($serviceId, $paymentType, $infoSelect, $trial = null)
2104
    {
2105
        if (!in_array($paymentType, [self::PAYMENT_TYPE_PAYPAL, self::PAYMENT_TYPE_TRANSFER, self::PAYMENT_TYPE_CULQI])) {
2106
            return false;
2107
        }
2108
2109
        $userId = api_get_user_id();
2110
2111
        $service = $this->getServices($serviceId);
2112
2113
        if (empty($service)) {
2114
            return false;
2115
        }
2116
2117
        $currency = $this->getSelectedCurrency();
2118
2119
        $values = [
2120
            'service_id' => $serviceId,
2121
            'reference' => $this->generateReference(
2122
                $userId,
2123
                $service['applies_to'],
2124
                $infoSelect
2125
            ),
2126
            'currency_id' => $currency['id'],
2127
            'price' => $service['price'],
2128
            'node_type' => $service['applies_to'],
2129
            'node_id' => intval($infoSelect),
2130
            'buyer_id' => $userId,
2131
            'buy_date' => api_get_utc_datetime(),
2132
            'date_start' => api_get_utc_datetime(),
2133
            'date_end' => date_format(date_add(date_create(api_get_utc_datetime()), date_interval_create_from_date_string($service['duration_days'].' days')), 'Y-m-d H:i:s'),
2134
            'status' => self::SERVICE_STATUS_PENDING,
2135
            'payment_type' => intval($paymentType)
2136
        ];
2137
2138
        $returnedServiceSaleId = Database::insert(self::TABLE_SERVICES_SALE, $values);
2139
2140
        return $returnedServiceSaleId;
2141
    }
2142
2143
    /**
2144
     * Save Culqi configuration params
2145
     * @param array $params
2146
     * @return int Rows affected. Otherwise return false
2147
     */
2148
    public function saveCulqiParameters($params)
2149
    {
2150
        return Database::update(
2151
            Database::get_main_table(BuyCoursesPlugin::TABLE_CULQI),
2152
            [
2153
                'commerce_code' => $params['commerce_code'],
2154
                'api_key' => $params['api_key']
2155
            ],
2156
            ['id = ?' => 1]
2157
        );
2158
    }
2159
2160
    /**
2161
     * Gets the stored Culqi params
2162
     * @return array
2163
     */
2164
    public function getCulqiParams()
2165
    {
2166
        return Database::select(
2167
            '*',
2168
            Database::get_main_table(BuyCoursesPlugin::TABLE_CULQI),
2169
            ['id = ?' => 1],
2170
            'first'
2171
        );
2172
    }
2173
2174
}
2175