Completed
Push — sf/last-boss ( 1627bd...06d0f3 )
by Kiyotaka
14:25 queued 06:50
created

OwnerStoreController::postRequestApi()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 2
dl 0
loc 16
rs 9.7333
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of EC-CUBE
5
 *
6
 * Copyright(c) LOCKON CO.,LTD. All Rights Reserved.
7
 *
8
 * http://www.lockon.co.jp/
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Eccube\Controller\Admin\Store;
15
16
use Eccube\Controller\AbstractController;
17
use Eccube\Entity\BaseInfo;
18
use Eccube\Entity\Master\PageMax;
19
use Eccube\Entity\Plugin;
20
use Eccube\Form\Type\Admin\SearchPluginApiType;
21
use Eccube\Repository\BaseInfoRepository;
22
use Eccube\Repository\PluginRepository;
23
use Eccube\Service\Composer\ComposerApiService;
24
use Eccube\Service\Composer\ComposerProcessService;
25
use Eccube\Service\Composer\ComposerServiceInterface;
26
use Eccube\Service\PluginApiService;
27
use Eccube\Service\PluginService;
28
use Eccube\Service\SystemService;
29
use Eccube\Util\FormUtil;
30
use Knp\Component\Pager\Paginator;
31
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
32
use Symfony\Component\HttpFoundation\RedirectResponse;
33
use Symfony\Component\HttpFoundation\Request;
34
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
35
use Symfony\Component\Routing\Annotation\Route;
36
37
/**
38
 * @Route("/%eccube_admin_route%/store/plugin/api")
39
 */
40
class OwnerStoreController extends AbstractController
41
{
42
    /**
43
     * @var PluginRepository
44
     */
45
    protected $pluginRepository;
46
47
    /**
48
     * @var PluginService
49
     */
50
    protected $pluginService;
51
52
    /**
53
     * @var ComposerServiceInterface
54
     */
55
    protected $composerService;
56
57
    /**
58
     * @var SystemService
59
     */
60
    protected $systemService;
61
62
    /**
63
     * @var PluginApiService
64
     */
65
    protected $pluginApiService;
66
67
    private static $vendorName = 'ec-cube';
68
69
    /** @var BaseInfo */
70
    private $BaseInfo;
71
72
    /**
73
     * OwnerStoreController constructor.
74
     *
75
     * @param PluginRepository $pluginRepository
76
     * @param PluginService $pluginService
77
     * @param ComposerProcessService $composerProcessService
78
     * @param ComposerApiService $composerApiService
79
     * @param SystemService $systemService
80
     * @param PluginApiService $pluginApiService
81
     * @param BaseInfoRepository $baseInfoRepository
82
     *
83
     * @throws \Doctrine\ORM\NoResultException
84
     * @throws \Doctrine\ORM\NonUniqueResultException
85
     */
86
    public function __construct(
87
        PluginRepository $pluginRepository,
88
        PluginService $pluginService,
89
        ComposerProcessService $composerProcessService,
90
        ComposerApiService $composerApiService,
91
        SystemService $systemService,
92
        PluginApiService $pluginApiService,
93
        BaseInfoRepository $baseInfoRepository
94
    ) {
95
        $this->pluginRepository = $pluginRepository;
96
        $this->pluginService = $pluginService;
97
        $this->systemService = $systemService;
98
        $this->pluginApiService = $pluginApiService;
99
        $this->BaseInfo = $baseInfoRepository->get();
100
101
        // TODO: Check the flow of the composer service below
102
        $memoryLimit = $this->systemService->getMemoryLimit();
103
        if ($memoryLimit == -1 or $memoryLimit >= $this->eccubeConfig['eccube_composer_memory_limit']) {
104
            $this->composerService = $composerApiService;
105
        } else {
106
            $this->composerService = $composerProcessService;
107
        }
108
    }
109
110
    /**
111
     * Owner's Store Plugin Installation Screen - Search function
112
     *
113
     * @Route("/search", name="admin_store_plugin_owners_search")
114
     * @Route("/search/page/{page_no}", name="admin_store_plugin_owners_search_page", requirements={"page_no" = "\d+"})
115
     * @Template("@admin/Store/plugin_search.twig")
116
     *
117
     * @param Request     $request
118
     * @param int $page_no
119
     * @param Paginator $paginator
120
     *
121
     * @return array
122
     */
123
    public function search(Request $request, $page_no = null, Paginator $paginator)
124
    {
125
        if (empty($this->BaseInfo->getAuthenticationKey())) {
126
            $this->addWarning('認証キーを設定してください。', 'admin');
127
128
            return $this->redirectToRoute('admin_store_authentication_setting');
129
        }
130
131
        // Acquire downloadable plug-in information from owners store
132
        $items = [];
133
        $message = '';
134
        $total = 0;
135
        $category = [];
136
137
        list($json,) = $this->pluginApiService->getCategory();
138
        if (!empty($json)) {
139
            $data = json_decode($json, true);
140
            $category = array_column($data, 'name', 'id');
141
        }
142
143
        // build form with master data
144
        $builder = $this->formFactory
145
            ->createBuilder(SearchPluginApiType::class, null, ['category' => $category]);
146
        $searchForm = $builder->getForm();
147
148
        $searchForm->handleRequest($request);
149
        $searchData = $searchForm->getData();
150
        if ($searchForm->isSubmitted()) {
151
            if ($searchForm->isValid()) {
152
                $page_no = 1;
153
                $searchData = $searchForm->getData();
154
                $this->session->set('eccube.admin.plugin_api.search', FormUtil::getViewData($searchForm));
155
                $this->session->set('eccube.admin.plugin_api.search.page_no', $page_no);
156
            }
157
        } else {
158
            // quick search
159
            if (is_numeric($categoryId = $request->get('category_id')) && array_key_exists($categoryId, $category)) {
160
                $searchForm['category_id']->setData($categoryId);
161
            }
162
            // reset page count
163
            $this->session->set('eccube.admin.plugin_api.search.page_count', $this->eccubeConfig->get('eccube_default_page_count'));
164 View Code Duplication
            if (null !== $page_no || $request->get('resume')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
165
                if ($page_no) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $page_no 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...
166
                    $this->session->set('eccube.admin.plugin_api.search.page_no', (int) $page_no);
167
                } else {
168
                    $page_no = $this->session->get('eccube.admin.plugin_api.search.page_no', 1);
169
                }
170
                $viewData = $this->session->get('eccube.admin.plugin_api.search', []);
171
                $searchData = FormUtil::submitAndGetData($searchForm, $viewData);
172
            } else {
173
                $page_no = 1;
174
                // submit default value
175
                $viewData = FormUtil::getViewData($searchForm);
176
                $searchData = FormUtil::submitAndGetData($searchForm, $viewData);
177
                $this->session->set('eccube.admin.plugin_api.search', $searchData);
178
                $this->session->set('eccube.admin.plugin_api.search.page_no', $page_no);
179
            }
180
        }
181
182
        // set page count
183
        $pageCount = $this->session->get('eccube.admin.plugin_api.search.page_count', $this->eccubeConfig->get('eccube_default_page_count'));
184
        if (($PageMax = $searchForm['page_count']->getData()) instanceof PageMax) {
185
            $pageCount = $PageMax->getId();
186
            $this->session->set('eccube.admin.plugin_api.search.page_count', $pageCount);
187
        }
188
189
        // Owner's store communication
190
        $searchData['page_no'] = $page_no;
191
        $searchData['page_count'] = $pageCount;
192
        list($json, $info) = $this->pluginApiService->getPlugins($searchData);
193
        if (empty($json)) {
194
            $message = $this->pluginApiService->getResponseErrorMessage($info);
195
        } else {
196
            $data = json_decode($json, true);
197
            $total = $data['total'];
198
            if (isset($data['plugins']) && count($data['plugins']) > 0) {
199
                // Check plugin installed
200
                $pluginInstalled = $this->pluginRepository->findAll();
201
                // Update_status 1 : not install/purchased 、2 : Installed、 3 : Update、4 : not purchased
202
                foreach ($data['plugins'] as $item) {
203
                    // Not install/purchased
204
                    $item['update_status'] = 1;
205
                    /** @var Plugin $plugin */
206
                    foreach ($pluginInstalled as $plugin) {
207
                        if ($plugin->getSource() == $item['id']) {
208
                            // Installed
209
                            $item['update_status'] = 2;
210
                            if ($this->pluginService->isUpdate($plugin->getVersion(), $item['version'])) {
211
                                // Need update
212
                                $item['update_status'] = 3;
213
                            }
214
                        }
215
                    }
216
                    if ($item['purchased'] == false && (isset($item['purchase_required']) && $item['purchase_required'] == true)) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $item['purchase_required'] of type integer to the boolean true. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
217
                        // Not purchased with paid items
218
                        $item['update_status'] = 4;
219
                    }
220
221
                    $item = $this->pluginService->buildInfo($item);
222
                    $items[] = $item;
223
                }
224
225
                // Todo: news api will remove this?
226
                // Promotion item
227
//                $i = 0;
228
//                foreach ($items as $item) {
229
//                    if ($item['promotion'] == 1) {
230
//                        $promotionItems[] = $item;
231
//                        unset($items[$i]);
232
//                    }
233
//                    $i++;
234
//                }
235
            } else {
236
                $message = trans('admin.store.plugin.search.not_found');
237
            }
238
        }
239
240
        // The usage is set because `$items` are already paged.
241
        // virtual paging
242
        $pagination = $paginator->paginate($items, 1, $pageCount);
243
        $pagination->setTotalItemCount($total);
244
        $pagination->setCurrentPageNumber($page_no);
245
        $pagination->setItemNumberPerPage($pageCount);
246
247
        return [
248
            'pagination' => $pagination,
249
            'total' => $total,
250
            'searchForm' => $searchForm->createView(),
251
            'page_no' => $page_no,
252
            'message' => $message,
253
            'Categories' => $category,
254
        ];
255
    }
256
257
    /**
258
     * Do confirm page
259
     *
260
     * @Route("/install/{id}/confirm", requirements={"id" = "\d+"}, name="admin_store_plugin_install_confirm")
261
     * @Template("@admin/Store/plugin_confirm.twig")
262
     *
263
     * @param Request $request
264
     *
265
     * @return array
266
     *
267
     * @throws \Eccube\Exception\PluginException
268
     */
269
    public function doConfirm(Request $request, $id)
270
    {
271
        list($json) = $this->pluginApiService->getPlugin($id);
272
        $item = [];
273
        if ($json) {
274
            $data = json_decode($json, true);
275
            $item = $this->pluginService->buildInfo($data);
276
        }
277
278
        if (empty($item)) {
279
            throw new NotFoundHttpException();
280
        }
281
282
        // Todo: need define item's dependency mechanism
283
        $requires = $this->pluginService->getPluginRequired($item);
284
285
        return [
286
            'item' => $item,
287
            'requires' => $requires,
288
            'is_update' => $request->get('is_update', false),
289
        ];
290
    }
291
292
    /**
293
     * Api Install plugin by composer connect with package repo
294
     *
295
     * @Route("/install", name="admin_store_plugin_api_install", methods={"POST"})
296
     *
297
     * @param Request $request
298
     *
299
     * @return \Symfony\Component\HttpFoundation\JsonResponse
300
     */
301
    public function apiInstall(Request $request)
302
    {
303
        $this->isTokenValid();
304
305
        $pluginCode = $request->get('pluginCode');
306
307
        $log = null;
308
        try {
309
            $log = $this->composerService->execRequire('ec-cube/'.$pluginCode);
310
311
            return $this->json(['success' => true, 'log' => $log]);
312
        } catch (\Exception $e) {
313
            $log = $e->getMessage();
314
            log_error($e);
315
        }
316
317
        return $this->json(['success' => false, 'log' => $log], 500);
318
    }
319
320
    /**
321
     * Do confirm page
322
     *
323
     * @Route("/delete/{id}/confirm", requirements={"id" = "\d+"}, name="admin_store_plugin_delete_confirm")
324
     * @Template("@admin/Store/plugin_confirm_uninstall.twig")
325
     *
326
     * @param Plugin $Plugin
327
     *
328
     * @return array|RedirectResponse
329
     */
330
    public function deleteConfirm(Plugin $Plugin)
331
    {
332
        // Owner's store communication
333
        $url = $this->eccubeConfig['eccube_package_api_url'].'/search/packages.json';
334
        list($json,) = $this->getRequestApi($url);
0 ignored issues
show
Deprecated Code introduced by
The method Eccube\Controller\Admin\...roller::getRequestApi() has been deprecated with message: since release, please preference PluginApiService

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
335
        $data = json_decode($json, true);
336
        $items = $data['item'];
337
338
        // The plugin depends on it
339
        $pluginCode = $Plugin->getCode();
340
        $otherDepend = $this->pluginService->findDependentPlugin($pluginCode);
341
342
        if (!empty($otherDepend)) {
343
            $DependPlugin = $this->pluginRepository->findOneBy(['code' => $otherDepend[0]]);
344
            $dependName = $otherDepend[0];
345
            if ($DependPlugin) {
346
                $dependName = $DependPlugin->getName();
347
            }
348
            $message = trans('admin.plugin.uninstall.depend', ['%name%' => $Plugin->getName(), '%depend_name%' => $dependName]);
349
            $this->addError($message, 'admin');
350
351
            return $this->redirectToRoute('admin_store_plugin');
352
        }
353
354
        // Check plugin in api
355
        $pluginSource = $Plugin->getSource();
356
        $index = array_search($pluginSource, array_column($items, 'product_id'));
357
        if ($index === false) {
358
            throw new NotFoundHttpException();
359
        }
360
361
        // Build info
362
        $plugin = $this->pluginService->buildInfo($items);
363
        $plugin['id'] = $Plugin->getId();
364
365
        return [
366
            'item' => $plugin,
367
        ];
368
    }
369
370
    /**
371
     * New ways to remove plugin: using composer command
372
     *
373
     * @Route("/delete/{id}/uninstall", requirements={"id" = "\d+"}, name="admin_store_plugin_api_uninstall", methods={"DELETE"})
374
     *
375
     * @param Plugin $Plugin
376
     *
377
     * @return \Symfony\Component\HttpFoundation\JsonResponse
378
     */
379
    public function apiUninstall(Plugin $Plugin)
380
    {
381
        $this->isTokenValid();
382
383
        if ($Plugin->isEnabled()) {
384
            return $this->json(['success' => false, 'message' => trans('admin.plugin.uninstall.error.not_disable')], 400);
385
        }
386
387
        $pluginCode = $Plugin->getCode();
388
        $otherDepend = $this->pluginService->findDependentPlugin($pluginCode);
389
390
        if (!empty($otherDepend)) {
391
            $DependPlugin = $this->pluginRepository->findOneBy(['code' => $otherDepend[0]]);
392
            $dependName = $otherDepend[0];
393
            if ($DependPlugin) {
394
                $dependName = $DependPlugin->getName();
395
            }
396
            $message = trans('admin.plugin.uninstall.depend', ['%name%' => $Plugin->getName(), '%depend_name%' => $dependName]);
397
398
            return $this->json(['success' => false, 'message' => $message], 400);
399
        }
400
401
        $pluginCode = $Plugin->getCode();
402
        $packageName = self::$vendorName.'/'.$pluginCode;
403
        try {
404
            $log = $this->composerService->execRemove($packageName);
405
406
            return $this->json(['success' => false, 'log' => $log]);
407
        } catch (\Exception $e) {
408
            log_error($e);
409
410
            return $this->json(['success' => false, 'log' => $e->getMessage()], 500);
411
        }
412
    }
413
414
    /**
415
     * オーナーズブラグインインストール、アップデート
416
     *
417
     * @Route("/upgrade", name="admin_store_plugin_api_upgrade", methods={"POST"})
418
     *
419
     * @param Request $request
420
     *
421
     * @return \Symfony\Component\HttpFoundation\JsonResponse
422
     */
423
    public function apiUpgrade(Request $request)
424
    {
425
        $this->isTokenValid();
426
427
        $pluginCode = $request->get('pluginCode');
428
        $version = $request->get('version');
429
430
        $log = null;
431
        try {
432
            $log = $this->composerService->execRequire('ec-cube/'.$pluginCode.':'.$version);
433
434
            return $this->json(['success' => true, 'log' => $log]);
435
        } catch (\Exception $e) {
436
            $log = $e->getMessage();
437
            log_error($e);
438
        }
439
440
        return $this->json(['success' => false, 'log' => $log], 500);
441
    }
442
443
    /**
444
     * オーナーズブラグインインストール、スキーマ更新
445
     *
446
     * @Route("/schema_update", name="admin_store_plugin_api_schema_update", methods={"POST"})
447
     *
448
     * @param Request $request
449
     *
450
     * @return \Symfony\Component\HttpFoundation\JsonResponse
451
     */
452 View Code Duplication
    public function apiSchemaUpdate(Request $request)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
453
    {
454
        $this->isTokenValid();
455
456
        $pluginCode = $request->get('pluginCode');
457
458
        try {
459
            $Plugin = $this->pluginRepository->findByCode($pluginCode);
460
461
            if (!$Plugin) {
462
                throw new NotFoundHttpException();
463
            }
464
465
            $config = $this->pluginService->readConfig($this->pluginService->calcPluginDir($Plugin->getCode()));
466
467
            ob_start();
468
            $this->pluginService->generateProxyAndUpdateSchema($Plugin, $config);
469
            $log = ob_get_clean();
470
            ob_end_flush();
471
472
            return $this->json(['success' => true, 'log' => $log]);
473
        } catch (\Exception $e) {
474
            $log = $e->getMessage();
475
            log_error($e);
476
477
            return $this->json(['success' => false, 'log' => $log], 500);
478
        }
479
    }
480
481
    /**
482
     * オーナーズブラグインインストール、更新処理
483
     *
484
     * @Route("/update", name="admin_store_plugin_api_update", methods={"POST"})
485
     *
486
     * @param Request $request
487
     *
488
     * @return \Symfony\Component\HttpFoundation\JsonResponse
489
     */
490 View Code Duplication
    public function apiUpdate(Request $request)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
491
    {
492
        $this->isTokenValid();
493
494
        $pluginCode = $request->get('pluginCode');
495
496
        $log = null;
497
        try {
498
            $Plugin = $this->pluginRepository->findByCode($pluginCode);
499
            if (!$Plugin) {
500
                throw new NotFoundHttpException();
501
            }
502
503
            $config = $this->pluginService->readConfig($this->pluginService->calcPluginDir($Plugin->getCode()));
504
            ob_start();
505
            $this->pluginService->updatePlugin($Plugin, $config);
506
            $log = ob_get_clean();
507
            ob_end_flush();
508
509
            return $this->json(['success' => true, 'log' => $log]);
510
        } catch (\Exception $e) {
511
            $log = $e->getMessage();
512
            log_error($e);
513
        }
514
515
        return $this->json(['success' => false, 'log' => $log], 500);
516
    }
517
518
    /**
519
     * Do confirm update page
520
     *
521
     * @Route("/upgrade/{id}/confirm", requirements={"id" = "\d+"}, name="admin_store_plugin_update_confirm")
522
     * @Template("@admin/Store/plugin_confirm.twig")
523
     *
524
     * @param Plugin $Plugin
525
     *
526
     * @return array
527
     */
528
    public function doUpdateConfirm(Plugin $Plugin)
529
    {
530
        list($json) = $this->pluginApiService->getPlugin($Plugin->getSource());
531
        $item = [];
532
        if ($json) {
533
            $data = json_decode($json, true);
534
            $item = $this->pluginService->buildInfo($data);
535
        }
536
537
        if (empty($item)) {
538
            throw new NotFoundHttpException();
539
        }
540
//
541
//        // Todo: need define item's dependency mechanism
542
//        $requires = $this->pluginService->getPluginRequired($item);
543
544
        return [
545
            'item' => $item,
546
            'requires' => [],
547
            'is_update' => true,
548
            'Plugin' => $Plugin,
549
        ];
550
    }
551
552
    /**
553
     * API request processing
554
     *
555
     * @param string $url
556
     *
557
     * @return array
558
     *
559
     * @deprecated since release, please preference PluginApiService
560
     */
561
    private function getRequestApi($url)
562
    {
563
        $curl = curl_init($url);
564
565
        // Option array
566
        $options = [
567
            // HEADER
568
            CURLOPT_HTTPGET => true,
569
            CURLOPT_SSL_VERIFYPEER => false,
570
            CURLOPT_RETURNTRANSFER => true,
571
            CURLOPT_FAILONERROR => true,
572
            CURLOPT_CAINFO => \Composer\CaBundle\CaBundle::getSystemCaRootBundlePath(),
573
        ];
574
575
        // Set option value
576
        curl_setopt_array($curl, $options);
577
        $result = curl_exec($curl);
578
        $info = curl_getinfo($curl);
579
        $message = curl_error($curl);
580
        $info['message'] = $message;
581
        curl_close($curl);
582
583
        log_info('http get_info', $info);
584
585
        return [$result, $info];
586
    }
587
}
588