Completed
Push — 4.0 ( 16398e...769a6f )
by chihiro
25s queued 10s
created

OwnerStoreController::apiUninstall()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 46

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
cc 5
nc 6
nop 2
dl 0
loc 46
ccs 0
cts 23
cp 0
crap 30
rs 8.867
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\Exception\PluginApiException;
21
use Eccube\Form\Type\Admin\SearchPluginApiType;
22
use Eccube\Repository\BaseInfoRepository;
23
use Eccube\Repository\PluginRepository;
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\CacheUtil;
30
use Eccube\Util\FormUtil;
31
use Knp\Component\Pager\Paginator;
32
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
33
use Symfony\Component\HttpFoundation\Request;
34
use Symfony\Component\HttpKernel\KernelEvents;
35
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
36
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
37
use Symfony\Component\Routing\Annotation\Route;
38
39
/**
40
 * @Route("/%eccube_admin_route%/store/plugin/api")
41
 */
42
class OwnerStoreController extends AbstractController
43
{
44
    /**
45
     * @var PluginRepository
46
     */
47
    protected $pluginRepository;
48
49
    /**
50
     * @var PluginService
51
     */
52
    protected $pluginService;
53
54
    /**
55
     * @var ComposerServiceInterface
56
     */
57
    protected $composerService;
58
59
    /**
60
     * @var SystemService
61
     */
62
    protected $systemService;
63
64
    /**
65
     * @var PluginApiService
66
     */
67
    protected $pluginApiService;
68
69
    private static $vendorName = 'ec-cube';
70
71
    /** @var BaseInfo */
72
    private $BaseInfo;
73
74
    /** @var CacheUtil */
75
    private $cacheUtil;
76
77
    /**
78
     * OwnerStoreController constructor.
79
     *
80
     * @param PluginRepository $pluginRepository
81
     * @param PluginService $pluginService
82
     * @param ComposerProcessService $composerProcessService
83
     * @param ComposerServiceInterface $composerService
84
     * @param SystemService $systemService
85
     * @param PluginApiService $pluginApiService
86
     * @param BaseInfoRepository $baseInfoRepository
87
     * @param CacheUtil $cacheUtil
88
     *
89
     * @throws \Doctrine\ORM\NoResultException
90
     * @throws \Doctrine\ORM\NonUniqueResultException
91
     */
92
    public function __construct(
93
        PluginRepository $pluginRepository,
94
        PluginService $pluginService,
95
        ComposerProcessService $composerProcessService,
96
        ComposerServiceInterface $composerService,
97
        SystemService $systemService,
98
        PluginApiService $pluginApiService,
99
        BaseInfoRepository $baseInfoRepository,
100
        CacheUtil $cacheUtil
101
    ) {
102
        $this->pluginRepository = $pluginRepository;
103
        $this->pluginService = $pluginService;
104
        $this->systemService = $systemService;
105
        $this->pluginApiService = $pluginApiService;
106
        $this->BaseInfo = $baseInfoRepository->get();
107
        $this->cacheUtil = $cacheUtil;
108
109
        // TODO: Check the flow of the composer service below
110
        $memoryLimit = $this->systemService->getMemoryLimit();
111
        if ($memoryLimit == -1 or $memoryLimit >= $this->eccubeConfig['eccube_composer_memory_limit']) {
112
            $this->composerService = $composerService;
113
        } else {
114
            $this->composerService = $composerProcessService;
115
        }
116
    }
117
118
    /**
119
     * Owner's Store Plugin Installation Screen - Search function
120
     *
121
     * @Route("/search", name="admin_store_plugin_owners_search")
122
     * @Route("/search/page/{page_no}", name="admin_store_plugin_owners_search_page", requirements={"page_no" = "\d+"})
123
     * @Template("@admin/Store/plugin_search.twig")
124
     *
125
     * @param Request     $request
126
     * @param int $page_no
127
     * @param Paginator $paginator
128
     *
129
     * @return array
130
     */
131
    public function search(Request $request, $page_no = null, Paginator $paginator)
132
    {
133
        if (empty($this->BaseInfo->getAuthenticationKey())) {
134
            $this->addWarning('admin.store.plugin.search.not_auth', 'admin');
135
136
            return $this->redirectToRoute('admin_store_authentication_setting');
137
        }
138
139
        // Acquire downloadable plug-in information from owners store
140
        $category = [];
141
142
        $json = $this->pluginApiService->getCategory();
143
        if (!empty($json)) {
144
            $data = json_decode($json, true);
145
            $category = array_column($data, 'name', 'id');
146
        }
147
148
        // build form with master data
149
        $builder = $this->formFactory
150
            ->createBuilder(SearchPluginApiType::class, null, ['category' => $category]);
151
        $searchForm = $builder->getForm();
152
153
        $searchForm->handleRequest($request);
154
        $searchData = $searchForm->getData();
155
        if ($searchForm->isSubmitted()) {
156
            if ($searchForm->isValid()) {
157
                $page_no = 1;
158
                $searchData = $searchForm->getData();
159
                $this->session->set('eccube.admin.plugin_api.search', FormUtil::getViewData($searchForm));
160
                $this->session->set('eccube.admin.plugin_api.search.page_no', $page_no);
161
            }
162
        } else {
163
            // quick search
164
            if (is_numeric($categoryId = $request->get('category_id')) && array_key_exists($categoryId, $category)) {
165
                $searchForm['category_id']->setData($categoryId);
166
            }
167
            // reset page count
168
            $this->session->set('eccube.admin.plugin_api.search.page_count', $this->eccubeConfig->get('eccube_default_page_count'));
169 View Code Duplication
            if (null !== $page_no || $request->get('resume')) {
170
                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...
171
                    $this->session->set('eccube.admin.plugin_api.search.page_no', (int) $page_no);
172
                } else {
173
                    $page_no = $this->session->get('eccube.admin.plugin_api.search.page_no', 1);
174
                }
175
                $viewData = $this->session->get('eccube.admin.plugin_api.search', []);
176
                $searchData = FormUtil::submitAndGetData($searchForm, $viewData);
177
            } else {
178
                $page_no = 1;
179
                // submit default value
180
                $viewData = FormUtil::getViewData($searchForm);
181
                $searchData = FormUtil::submitAndGetData($searchForm, $viewData);
182
                $this->session->set('eccube.admin.plugin_api.search', $searchData);
183
                $this->session->set('eccube.admin.plugin_api.search.page_no', $page_no);
184
            }
185
        }
186
187
        // set page count
188
        $pageCount = $this->session->get('eccube.admin.plugin_api.search.page_count', $this->eccubeConfig->get('eccube_default_page_count'));
189
        if (($PageMax = $searchForm['page_count']->getData()) instanceof PageMax) {
190
            $pageCount = $PageMax->getId();
191
            $this->session->set('eccube.admin.plugin_api.search.page_count', $pageCount);
192
        }
193
194
        // Owner's store communication
195
        $searchData['page_no'] = $page_no;
196
        $searchData['page_count'] = $pageCount;
197
198
        $total = 0;
199
        $items = [];
200
201
        try {
202
            $data = $this->pluginApiService->getPlugins($searchData);
203
            $total = $data['total'];
204
            $items = $data['plugins'];
205
        } catch (PluginApiException $e) {
206
            $this->addError($e->getMessage(), 'admin');
207
        }
208
209
        // The usage is set because `$items` are already paged.
210
        // virtual paging
211
        $pagination = $paginator->paginate($items, 1, $pageCount);
212
        $pagination->setTotalItemCount($total);
213
        $pagination->setCurrentPageNumber($page_no);
214
        $pagination->setItemNumberPerPage($pageCount);
215
216
        return [
217
            'pagination' => $pagination,
218
            'total' => $total,
219
            'searchForm' => $searchForm->createView(),
220
            'page_no' => $page_no,
221
            'Categories' => $category,
222
        ];
223
    }
224
225
    /**
226
     * Do confirm page
227
     *
228
     * @Route("/install/{id}/confirm", requirements={"id" = "\d+"}, name="admin_store_plugin_install_confirm")
229
     * @Template("@admin/Store/plugin_confirm.twig")
230
     *
231
     * @param Request $request
232
     *
233
     * @return array
234
     *
235
     * @throws \Eccube\Exception\PluginException
236
     */
237
    public function doConfirm(Request $request, $id)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
238
    {
239
        try {
240
            $item = $this->pluginApiService->getPlugin($id);
241
            // Todo: need define item's dependency mechanism
242
            $requires = $this->pluginService->getPluginRequired($item);
243
244
            return [
245
                'item' => $item,
246
                'requires' => $requires,
247
                'is_update' => false,
248
            ];
249
        } catch (PluginApiException $e) {
250
            $this->addError($e->getMessage(), 'admin');
251
252
            return $this->redirectToRoute('admin_store_authentication_setting');
253
        }
254
    }
255
256
    /**
257
     * Api Install plugin by composer connect with package repo
258
     *
259
     * @Route("/install", name="admin_store_plugin_api_install", methods={"POST"})
260
     *
261
     * @param Request $request
262
     *
263
     * @return \Symfony\Component\HttpFoundation\JsonResponse
264
     */
265
    public function apiInstall(Request $request, EventDispatcherInterface $dispatcher)
266
    {
267
        $this->isTokenValid();
268
269
        // .maintenanceファイルを設置
270
        $this->systemService->switchMaintenance(true);
271
272
        // TERMINATE時のイベントを設定
273
        $dispatcher->addListener(KernelEvents::TERMINATE, function () {
274
            // .maintenanceファイルを削除
275
            $this->systemService->switchMaintenance();
276
        });
277
278
        $this->cacheUtil->clearCache();
279
280
        $pluginCode = $request->get('pluginCode');
281
282
        $log = null;
283
        try {
284
            $log = $this->composerService->execRequire('ec-cube/'.$pluginCode);
285
286
            return $this->json(['success' => true, 'log' => $log]);
287
        } catch (\Exception $e) {
288
            $log = $e->getMessage();
289
            log_error($e);
290
        }
291
292
        return $this->json(['success' => false, 'log' => $log], 500);
293
    }
294
295
    /**
296
     * New ways to remove plugin: using composer command
297
     *
298
     * @Route("/delete/{id}/uninstall", requirements={"id" = "\d+"}, name="admin_store_plugin_api_uninstall", methods={"DELETE"})
299
     *
300
     * @param Plugin $Plugin
301
     *
302
     * @return \Symfony\Component\HttpFoundation\JsonResponse
303
     */
304
    public function apiUninstall(Plugin $Plugin, EventDispatcherInterface $dispatcher)
305
    {
306
        $this->isTokenValid();
307
308
        // .maintenanceファイルを設置
309
        $this->systemService->switchMaintenance(true);
310
311
        // TERMINATE時のイベントを設定
312
        $dispatcher->addListener(KernelEvents::TERMINATE, function () {
313
            // .maintenanceファイルを削除
314
            $this->systemService->switchMaintenance();
315
        });
316
317
        $this->cacheUtil->clearCache();
318
319
        if ($Plugin->isEnabled()) {
320
            return $this->json(['success' => false, 'message' => trans('admin.plugin.uninstall.error.not_disable')], 400);
321
        }
322
323
        $pluginCode = $Plugin->getCode();
324
        $otherDepend = $this->pluginService->findDependentPlugin($pluginCode);
325
326
        if (!empty($otherDepend)) {
327
            $DependPlugin = $this->pluginRepository->findOneBy(['code' => $otherDepend[0]]);
328
            $dependName = $otherDepend[0];
329
            if ($DependPlugin) {
330
                $dependName = $DependPlugin->getName();
331
            }
332
            $message = trans('admin.plugin.uninstall.depend', ['%name%' => $Plugin->getName(), '%depend_name%' => $dependName]);
333
334
            return $this->json(['success' => false, 'message' => $message], 400);
335
        }
336
337
        $pluginCode = $Plugin->getCode();
338
        $packageName = self::$vendorName.'/'.$pluginCode;
339
340
        try {
341
            $log = $this->composerService->execRemove($packageName);
342
343
            return $this->json(['success' => false, 'log' => $log]);
344
        } catch (\Exception $e) {
345
            log_error($e);
346
347
            return $this->json(['success' => false, 'log' => $e->getMessage()], 500);
348
        }
349
    }
350
351
    /**
352
     * オーナーズブラグインインストール、アップデート
353
     *
354
     * @Route("/upgrade", name="admin_store_plugin_api_upgrade", methods={"POST"})
355
     *
356
     * @param Request $request
357
     *
358
     * @return \Symfony\Component\HttpFoundation\JsonResponse
359
     */
360
    public function apiUpgrade(Request $request)
361
    {
362
        $this->isTokenValid();
363
364
        $this->cacheUtil->clearCache();
365
366
        $pluginCode = $request->get('pluginCode');
367
        $version = $request->get('version');
368
369
        $log = null;
370
        try {
371
            $log = $this->composerService->execRequire('ec-cube/'.$pluginCode.':'.$version);
372
            return $this->json(['success' => true, 'log' => $log]);
373
        } catch (\Exception $e) {
374
            $log = $e->getMessage();
375
            log_error($e);
376
        }
377
378
        return $this->json(['success' => false, 'log' => $log], 500);
379
    }
380
381
    /**
382
     * オーナーズブラグインインストール、スキーマ更新
383
     *
384
     * @Route("/schema_update", name="admin_store_plugin_api_schema_update", methods={"POST"})
385
     *
386
     * @param Request $request
387
     *
388
     * @return \Symfony\Component\HttpFoundation\JsonResponse
389
     */
390
    public function apiSchemaUpdate(Request $request)
391
    {
392
        $this->isTokenValid();
393
394
        $this->cacheUtil->clearCache();
395
396
        $pluginCode = $request->get('pluginCode');
397
398
        try {
399
            $Plugin = $this->pluginRepository->findByCode($pluginCode);
400
401
            if (!$Plugin) {
402
                throw new NotFoundHttpException();
403
            }
404
405
            $config = $this->pluginService->readConfig($this->pluginService->calcPluginDir($Plugin->getCode()));
406
407
            ob_start();
408
            $this->pluginService->generateProxyAndUpdateSchema($Plugin, $config);
409
410
            // 初期化されていなければインストール処理を実行する
411
            if (!$Plugin->isInitialized()) {
412
                $this->pluginService->callPluginManagerMethod($config, 'install');
413
                $Plugin->setInitialized(true);
414
                $this->entityManager->persist($Plugin);
415
                $this->entityManager->flush();
416
            }
417
418
            $log = ob_get_clean();
419
            while (ob_get_level() > 0) {
420
                ob_end_flush();
421
            }
422
423
            return $this->json(['success' => true, 'log' => $log]);
424
        } catch (\Exception $e) {
425
            $log = $e->getMessage();
426
            log_error($e);
427
428
            return $this->json(['success' => false, 'log' => $log], 500);
429
        }
430
    }
431
432
    /**
433
     * オーナーズブラグインインストール、更新処理
434
     *
435
     * @Route("/update", name="admin_store_plugin_api_update", methods={"POST"})
436
     *
437
     * @param Request $request
438
     *
439
     * @return \Symfony\Component\HttpFoundation\JsonResponse
440
     */
441
    public function apiUpdate(Request $request, EventDispatcherInterface $dispatcher)
442
    {
443
        $this->isTokenValid();
444
445
        // TERMINATE時のイベントを設定
446
        $dispatcher->addListener(KernelEvents::TERMINATE, function () {
447
            // .maintenanceファイルを削除
448
            $this->systemService->switchMaintenance(false, SystemService::AUTO_MAINTENANCE_UPDATE);
449
        });
450
451
        $this->cacheUtil->clearCache();
452
453
        $pluginCode = $request->get('pluginCode');
454
455
        $log = null;
456
        try {
457
            $Plugin = $this->pluginRepository->findByCode($pluginCode);
458
            if (!$Plugin) {
459
                throw new NotFoundHttpException();
460
            }
461
462
            $config = $this->pluginService->readConfig($this->pluginService->calcPluginDir($Plugin->getCode()));
463
            ob_start();
464
            $this->pluginService->updatePlugin($Plugin, $config);
465
            $log = ob_get_clean();
466
            while (ob_get_level() > 0) {
467
                ob_end_flush();
468
            }
469
470
            return $this->json(['success' => true, 'log' => $log]);
471
        } catch (\Exception $e) {
472
            $log = $e->getMessage();
473
            log_error($e);
474
        }
475
476
        return $this->json(['success' => false, 'log' => $log], 500);
477
    }
478
479
    /**
480
     * Do confirm update page
481
     *
482
     * @Route("/upgrade/{id}/confirm", requirements={"id" = "\d+"}, name="admin_store_plugin_update_confirm")
483
     * @Template("@admin/Store/plugin_confirm.twig")
484
     *
485
     * @param Plugin $Plugin
486
     *
487
     * @return array
488
     */
489
    public function doUpdateConfirm(Plugin $Plugin)
490
    {
491
        try {
492
            $item = $this->pluginApiService->getPlugin($Plugin->getSource());
493
494
            return [
495
                'item' => $item,
496
                'requires' => [],
497
                'is_update' => true,
498
                'Plugin' => $Plugin,
499
            ];
500
        } catch (PluginApiException $e) {
501
            $this->addError($e->getMessage(), 'admin');
502
503
            return $this->redirectToRoute('admin_store_authentication_setting');
504
        }
505
    }
506
}
507