Completed
Push — dev/recommend-plugins ( e0eb38...b4eeb7 )
by Kiyotaka
06:56
created

PluginController::index()   C

Complexity

Conditions 9
Paths 300

Size

Total Lines 79

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
nc 300
nop 0
dl 0
loc 79
rs 5.2359
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Common\Constant;
17
use Eccube\Controller\AbstractController;
18
use Eccube\Entity\BaseInfo;
19
use Eccube\Entity\Plugin;
20
use Eccube\Exception\PluginApiException;
21
use Eccube\Exception\PluginException;
22
use Eccube\Form\Type\Admin\AuthenticationType;
23
use Eccube\Form\Type\Admin\PluginLocalInstallType;
24
use Eccube\Form\Type\Admin\PluginManagementType;
25
use Eccube\Repository\BaseInfoRepository;
26
use Eccube\Repository\PluginRepository;
27
use Eccube\Service\PluginApiService;
28
use Eccube\Service\PluginService;
29
use Eccube\Util\CacheUtil;
30
use Eccube\Util\StringUtil;
31
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
32
use Symfony\Component\DependencyInjection\Container;
33
use Symfony\Component\Filesystem\Filesystem;
34
use Symfony\Component\Finder\Finder;
35
use Symfony\Component\HttpFoundation\File\UploadedFile;
36
use Symfony\Component\HttpFoundation\JsonResponse;
37
use Symfony\Component\HttpFoundation\RedirectResponse;
38
use Symfony\Component\HttpFoundation\Request;
39
use Symfony\Component\Routing\Annotation\Route;
40
use Symfony\Component\Routing\Exception\RouteNotFoundException;
41
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
42
43
class PluginController extends AbstractController
44
{
45
    /**
46
     * @var PluginService
47
     */
48
    protected $pluginService;
49
50
    /**
51
     * @var BaseInfo
52
     */
53
    protected $BaseInfo;
54
55
    /**
56
     * @var PluginRepository
57
     */
58
    protected $pluginRepository;
59
60
    /**
61
     * @var PluginApiService
62
     */
63
    protected $pluginApiService;
64
65
    /**
66
     * PluginController constructor.
67
     *
68
     * @param PluginRepository $pluginRepository
69
     * @param PluginService $pluginService
70
     * @param BaseInfoRepository $baseInfoRepository
71
     * @param PluginApiService $pluginApiService
72
     *
73
     * @throws \Doctrine\ORM\NoResultException
74
     * @throws \Doctrine\ORM\NonUniqueResultException
75
     */
76
    public function __construct(PluginRepository $pluginRepository, PluginService $pluginService, BaseInfoRepository $baseInfoRepository, PluginApiService $pluginApiService)
77
    {
78
        $this->pluginRepository = $pluginRepository;
79
        $this->pluginService = $pluginService;
80
        $this->BaseInfo = $baseInfoRepository->get();
81
        $this->pluginApiService = $pluginApiService;
82
    }
83
84
    /**
85
     * インストール済プラグイン画面
86
     *
87
     * @Route("/%eccube_admin_route%/store/plugin", name="admin_store_plugin")
88
     * @Template("@admin/Store/plugin.twig")
89
     *
90
     * @return array
91
     *
92
     * @throws PluginException
93
     */
94
    public function index()
95
    {
96
        $pluginForms = [];
97
        $configPages = [];
98
        $Plugins = $this->pluginRepository->findBy([], ['code' => 'ASC']);
99
100
        // ファイル設置プラグインの取得.
101
        $unregisteredPlugins = $this->getUnregisteredPlugins($Plugins);
102
        $unregisteredPluginsConfigPages = [];
103
        foreach ($unregisteredPlugins as $unregisteredPlugin) {
104
            try {
105
                $code = $unregisteredPlugin['code'];
106
                // プラグイン用設定画面があれば表示(プラグイン用のサービスプロバイダーに定義されているか)
107
                $unregisteredPluginsConfigPages[$code] = $this->generateUrl('plugin_'.$code.'_config');
108
            } catch (RouteNotFoundException $e) {
109
                // プラグインで設定画面のルートが定義されていない場合は無視
110
            }
111
        }
112
113
        $officialPlugins = [];
114
        $unofficialPlugins = [];
115
116
        foreach ($Plugins as $Plugin) {
117
            $form = $this->formFactory
118
                ->createNamedBuilder(
119
                    'form'.$Plugin->getId(),
120
                    PluginManagementType::class,
121
                    null,
122
                    [
123
                        'plugin_id' => $Plugin->getId(),
124
                    ]
125
                )
126
                ->getForm();
127
            $pluginForms[$Plugin->getId()] = $form->createView();
128
129
            try {
130
                // プラグイン用設定画面があれば表示(プラグイン用のサービスプロバイダーに定義されているか)
131
                $configPages[$Plugin->getCode()] = $this->generateUrl(Container::underscore($Plugin->getCode()).'_admin_config');
132
            } catch (\Exception $e) {
133
                // プラグインで設定画面のルートが定義されていない場合は無視
134
            }
135
            if ($Plugin->getSource() == 0) {
136
                // 商品IDが設定されていない場合、非公式プラグイン
137
                $unofficialPlugins[] = $Plugin;
138
            } else {
139
                $officialPlugins[$Plugin->getSource()] = $Plugin;
140
            }
141
        }
142
143
        // オーナーズストア通信
144
        $officialPluginsDetail = [];
145
        try {
146
            $data = $this->pluginApiService->getPurchased();
147
            foreach ($data as $item) {
148
                if (isset($officialPlugins[$item['id']]) === false) {
149
                    $Plugin = new Plugin();
150
                    $Plugin->setName($item['name']);
151
                    $Plugin->setCode($item['code']);
152
                    $Plugin->setVersion($item['version']);
153
                    $Plugin->setSource($item['id']);
154
                    $Plugin->setEnabled(false);
155
                    $officialPlugins[$item['id']] = $Plugin;
156
                }
157
                $officialPluginsDetail[$item['id']] = $item;
158
            }
159
        } catch (PluginApiException $e) {
160
            $this->addWarning($e->getMessage(), 'admin');
161
        }
162
163
        return [
164
            'plugin_forms' => $pluginForms,
165
            'officialPlugins' => $officialPlugins,
166
            'unofficialPlugins' => $unofficialPlugins,
167
            'configPages' => $configPages,
168
            'unregisteredPlugins' => $unregisteredPlugins,
169
            'unregisteredPluginsConfigPages' => $unregisteredPluginsConfigPages,
170
            'officialPluginsDetail' => $officialPluginsDetail,
171
        ];
172
    }
173
174
    /**
175
     * インストール済プラグインからのアップデート
176
     *
177
     * @Route("/%eccube_admin_route%/store/plugin/{id}/update", requirements={"id" = "\d+"}, name="admin_store_plugin_update", methods={"POST"})
178
     *
179
     * @param Request $request
180
     * @param Plugin $Plugin
181
     *
182
     * @return RedirectResponse
183
     */
184
    public function update(Request $request, Plugin $Plugin)
185
    {
186
        $form = $this->formFactory
187
            ->createNamedBuilder(
188
                'form'.$Plugin->getId(),
189
                PluginManagementType::class,
190
                null,
191
                [
192
                    'plugin_id' => null, // placeHolder
193
                ]
194
            )
195
            ->getForm();
196
197
        $message = '';
198
        $form->handleRequest($request);
199
        if ($form->isSubmitted() && $form->isValid()) {
200
            $tmpDir = null;
201
            try {
202
                $formFile = $form['plugin_archive']->getData();
203
                $tmpDir = $this->pluginService->createTempDir();
204
                $tmpFile = sha1(StringUtil::random(32)).'.'.$formFile->getClientOriginalExtension();
205
                $formFile->move($tmpDir, $tmpFile);
206
                $this->pluginService->update($Plugin, $tmpDir.'/'.$tmpFile);
207
                $fs = new Filesystem();
208
                $fs->remove($tmpDir);
209
                $this->addSuccess('admin.plugin.update.complete', 'admin');
210
211
                return $this->redirectToRoute('admin_store_plugin');
212
            } catch (PluginException $e) {
213
                if (!empty($tmpDir) && file_exists($tmpDir)) {
214
                    $fs = new Filesystem();
215
                    $fs->remove($tmpDir);
216
                }
217
                $message = $e->getMessage();
218
            } catch (\Exception $er) {
219
                // Catch composer install error | Other error
220
                if (!empty($tmpDir) && file_exists($tmpDir)) {
221
                    $fs = new Filesystem();
222
                    $fs->remove($tmpDir);
223
                }
224
                log_error('plugin install failed.', ['original-message' => $er->getMessage()]);
225
                $message = 'admin.plugin.install.fail';
226
            }
227
        } else {
228
            $errors = $form->getErrors(true);
229
            foreach ($errors as $error) {
230
                $message = $error->getMessage();
0 ignored issues
show
Bug introduced by
The method getMessage does only exist in Symfony\Component\Form\FormError, but not in Symfony\Component\Form\FormErrorIterator.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
231
            }
232
        }
233
234
        $this->addError($message, 'admin');
235
236
        return $this->redirectToRoute('admin_store_plugin');
237
    }
238
239
    /**
240
     * 対象のプラグインを有効にします。
241
     *
242
     * @Route("/%eccube_admin_route%/store/plugin/{id}/enable", requirements={"id" = "\d+"}, name="admin_store_plugin_enable", methods={"POST"})
243
     *
244
     * @param Plugin $Plugin
245
     *
246
     * @return RedirectResponse|JsonResponse
247
     *
248
     * @throws PluginException
249
     */
250
    public function enable(Plugin $Plugin, CacheUtil $cacheUtil, Request $request)
251
    {
252
        $this->isTokenValid();
253
254
        $log = null;
255
256
        if ($Plugin->isEnabled()) {
257
            if ($request->isXmlHttpRequest()) {
258
                return $this->json(['success' => true]);
259
            } else {
260
                $this->addError('admin.plugin.already.enable', 'admin');
261
            }
262
        } else {
263
            // ストアからインストールしたプラグインは依存プラグインが有効化されているかを確認
264
            if ($Plugin->getSource()) {
265
                $requires = $this->pluginService->getPluginRequired($Plugin);
266 View Code Duplication
                $requires = array_filter($requires, function ($req) {
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...
267
                    $code = preg_replace('/^ec-cube\//', '', $req['name']);
268
                    /** @var Plugin $DependPlugin */
269
                    $DependPlugin = $this->pluginRepository->findOneBy(['code' => $code]);
270
271
                    return $DependPlugin->isEnabled() == false;
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
272
                });
273
                if (!empty($requires)) {
274
                    $names = array_map(function ($req) {
275
                        return "「${req['description']}」";
276
                    }, $requires);
277
                    $message = trans('%depend_name%を先に有効化してください。', ['%name%' => $Plugin->getName(), '%depend_name%' => implode(', ', $names)]);
278
279
                    if ($request->isXmlHttpRequest()) {
280
                        return $this->json(['success' => false, 'message' => $message], 400);
281
                    } else {
282
                        $this->addError($message, 'admin');
283
284
                        return $this->redirectToRoute('admin_store_plugin');
285
                    }
286
                }
287
            }
288
289
            ob_start();
290
291
            if (!$Plugin->isInitialized()) {
292
                $this->pluginService->installWithCode($Plugin->getCode());
293
            }
294
295
            $this->pluginService->enable($Plugin);
296
            $log = ob_get_clean();
297
            ob_end_flush();
298
        }
299
300
        $cacheUtil->clearCache();
301
302 View Code Duplication
        if ($request->isXmlHttpRequest()) {
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...
303
            return $this->json(['success' => true, 'log' => $log]);
304
        } else {
305
            $this->addSuccess(trans('「%plugin_name%」を有効にしました。', ['%plugin_name%' => $Plugin->getName()]), 'admin');
306
307
            return $this->redirectToRoute('admin_store_plugin');
308
        }
309
    }
310
311
    /**
312
     * 対象のプラグインを無効にします。
313
     *
314
     * @Route("/%eccube_admin_route%/store/plugin/{id}/disable", requirements={"id" = "\d+"}, name="admin_store_plugin_disable", methods={"POST"})
315
     *
316
     * @param Request $request
317
     * @param Plugin $Plugin
318
     * @param CacheUtil $cacheUtil
319
     *
320
     * @return \Symfony\Component\HttpFoundation\JsonResponse|RedirectResponse
321
     */
322
    public function disable(Request $request, Plugin $Plugin, CacheUtil $cacheUtil)
323
    {
324
        $this->isTokenValid();
325
326
        $log = null;
327
        if ($Plugin->isEnabled()) {
328
            $dependents = $this->pluginService->findDependentPluginNeedDisable($Plugin->getCode());
329
            if (!empty($dependents)) {
330
                $dependName = $dependents[0];
331
                $DependPlugin = $this->pluginRepository->findOneBy(['code' => $dependents[0]]);
332
                if ($DependPlugin) {
333
                    $dependName = $DependPlugin->getName();
334
                }
335
                $message = trans('admin.plugin.disable.depend', ['%name%' => $Plugin->getName(), '%depend_name%' => $dependName]);
336
337
                if ($request->isXmlHttpRequest()) {
338
                    return $this->json(['message' => $message], 400);
339
                } else {
340
                    $this->addError($message, 'admin');
341
342
                    return $this->redirectToRoute('admin_store_plugin');
343
                }
344
            }
345
346
            ob_start();
347
            $this->pluginService->disable($Plugin);
348
            $log = ob_get_clean();
349
            ob_end_flush();
350 View Code Duplication
        } else {
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...
351
            if ($request->isXmlHttpRequest()) {
352
                return $this->json(['success' => true, 'log' => $log]);
353
            } else {
354
                $this->addError('admin.plugin.already.disable', 'admin');
355
356
                return $this->redirectToRoute('admin_store_plugin');
357
            }
358
        }
359
360
        $cacheUtil->clearCache();
361
362 View Code Duplication
        if ($request->isXmlHttpRequest()) {
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...
363
            return $this->json(['success' => true, 'log' => $log]);
364
        } else {
365
            $this->addSuccess('admin.plugin.disable.complete', 'admin');
366
367
            return $this->redirectToRoute('admin_store_plugin');
368
        }
369
    }
370
371
    /**
372
     * 対象のプラグインを削除します。
373
     *
374
     * @Route("/%eccube_admin_route%/store/plugin/{id}/uninstall", requirements={"id" = "\d+"}, name="admin_store_plugin_uninstall", methods={"DELETE"})
375
     *
376
     * @param Plugin $Plugin
377
     *
378
     * @return RedirectResponse
379
     *
380
     * @throws \Exception
381
     */
382
    public function uninstall(Plugin $Plugin)
383
    {
384
        $this->isTokenValid();
385
386
        if ($Plugin->isEnabled()) {
387
            $this->addError('admin.plugin.uninstall.error.not_disable', 'admin');
388
389
            return $this->redirectToRoute('admin_store_plugin');
390
        }
391
392
        // Check other plugin depend on it
393
        $pluginCode = $Plugin->getCode();
394
        $otherDepend = $this->pluginService->findDependentPlugin($pluginCode);
395
        if (!empty($otherDepend)) {
396
            $DependPlugin = $this->pluginRepository->findOneBy(['code' => $otherDepend[0]]);
397
            $dependName = $otherDepend[0];
398
            if ($DependPlugin) {
399
                $dependName = $DependPlugin->getName();
400
            }
401
            $message = trans('admin.plugin.uninstall.depend', ['%name%' => $Plugin->getName(), '%depend_name%' => $dependName]);
402
            $this->addError($message, 'admin');
403
404
            return $this->redirectToRoute('admin_store_plugin');
405
        }
406
407
        $this->pluginService->uninstall($Plugin);
408
        $this->addSuccess('admin.plugin.uninstall.complete', 'admin');
409
410
        return $this->redirectToRoute('admin_store_plugin');
411
    }
412
413
    /**
414
     * プラグインファイルアップロード画面
415
     *
416
     * @Route("/%eccube_admin_route%/store/plugin/install", name="admin_store_plugin_install")
417
     * @Template("@admin/Store/plugin_install.twig")
418
     *
419
     * @param Request $request
420
     *
421
     * @return array|RedirectResponse
422
     */
423
    public function install(Request $request)
424
    {
425
        $form = $this->formFactory
426
            ->createBuilder(PluginLocalInstallType::class)
427
            ->getForm();
428
        $errors = [];
429
        $form->handleRequest($request);
430
        if ($form->isSubmitted() && $form->isValid()) {
431
            $tmpDir = null;
432
            try {
433
                $service = $this->pluginService;
434
                /** @var UploadedFile $formFile */
435
                $formFile = $form['plugin_archive']->getData();
436
                $tmpDir = $service->createTempDir();
437
                // 拡張子を付けないとpharが動かないので付ける
438
                $tmpFile = sha1(StringUtil::random(32)).'.'.$formFile->getClientOriginalExtension();
439
                $formFile->move($tmpDir, $tmpFile);
440
                $tmpPath = $tmpDir.'/'.$tmpFile;
441
                $service->install($tmpPath);
442
                // Remove tmp file
443
                $fs = new Filesystem();
444
                $fs->remove($tmpDir);
445
                $this->addSuccess('admin.plugin.install.complete', 'admin');
446
447
                return $this->redirectToRoute('admin_store_plugin');
448
            } catch (PluginException $e) {
449
                if (!empty($tmpDir) && file_exists($tmpDir)) {
450
                    $fs = new Filesystem();
451
                    $fs->remove($tmpDir);
452
                }
453
                log_error('plugin install failed.', ['original-message' => $e->getMessage()]);
454
                $errors[] = $e;
455
            } catch (\Exception $er) {
456
                // Catch composer install error | Other error
457
                if (!empty($tmpDir) && file_exists($tmpDir)) {
458
                    $fs = new Filesystem();
459
                    $fs->remove($tmpDir);
460
                }
461
                log_error('plugin install failed.', ['original-message' => $er->getMessage()]);
462
                $this->addError('admin.plugin.install.fail', 'admin');
463
            }
464
        } else {
465
            foreach ($form->getErrors(true) as $error) {
466
                $errors[] = $error;
467
            }
468
        }
469
470
        return [
471
            'form' => $form->createView(),
472
            'errors' => $errors,
473
        ];
474
    }
475
476
    /**
477
     * 認証キー設定画面
478
     *
479
     * @Route("/%eccube_admin_route%/store/plugin/authentication_setting", name="admin_store_authentication_setting")
480
     * @Template("@admin/Store/authentication_setting.twig")
481
     */
482
    public function authenticationSetting(Request $request)
483
    {
484
        $builder = $this->formFactory
485
            ->createBuilder(AuthenticationType::class, $this->BaseInfo);
486
487
        $form = $builder->getForm();
488
        $form->handleRequest($request);
489
490
        if ($form->isSubmitted() && $form->isValid()) {
491
            // 認証キーの登録 and PHP path
492
            $this->BaseInfo = $form->getData();
493
            $this->entityManager->persist($this->BaseInfo);
494
            $this->entityManager->flush();
495
496
            $this->addSuccess('admin.common.save_complete', 'admin');
497
        }
498
499
        return [
500
            'form' => $form->createView(),
501
            'eccubeUrl' => $this->generateUrl('homepage', [], UrlGeneratorInterface::ABSOLUTE_URL),
502
            'eccubeShopName' => $this->BaseInfo->getShopName(),
503
        ];
504
    }
505
506
    /**
507
     * フォルダ設置のみのプラグインを取得する.
508
     *
509
     * @param array $plugins
510
     *
511
     * @return array
512
     *
513
     * @throws PluginException
514
     */
515
    protected function getUnregisteredPlugins(array $plugins)
516
    {
517
        $finder = new Finder();
518
        $pluginCodes = [];
519
520
        // DB登録済みプラグインコードのみ取得
521
        foreach ($plugins as $key => $plugin) {
522
            $pluginCodes[] = $plugin->getCode();
523
        }
524
        // DB登録済みプラグインコードPluginディレクトリから排他
525
        $dirs = $finder->in($this->eccubeConfig['plugin_realdir'])->depth(0)->directories();
526
527
        // プラグイン基本チェック
528
        $unregisteredPlugins = [];
529
        foreach ($dirs as $dir) {
530
            $pluginCode = $dir->getBasename();
531
            if (in_array($pluginCode, $pluginCodes, true)) {
532
                continue;
533
            }
534
            try {
535
                $this->pluginService->checkPluginArchiveContent($dir->getRealPath());
536
            } catch (PluginException $e) {
537
                //config.yamlに不備があった際は全てスキップ
538
                log_warning($e->getMessage());
539
                continue;
540
            }
541
            $config = $this->pluginService->readConfig($dir->getRealPath());
542
            $unregisteredPlugins[$pluginCode]['name'] = isset($config['name']) ? $config['name'] : null;
543
            $unregisteredPlugins[$pluginCode]['event'] = isset($config['event']) ? $config['event'] : null;
544
            $unregisteredPlugins[$pluginCode]['version'] = isset($config['version']) ? $config['version'] : null;
545
            $unregisteredPlugins[$pluginCode]['enabled'] = Constant::DISABLED;
546
            $unregisteredPlugins[$pluginCode]['code'] = isset($config['code']) ? $config['code'] : null;
547
        }
548
549
        return $unregisteredPlugins;
550
    }
551
}
552