Failed Conditions
Push — 4.0 ( 669dcd...af34e4 )
by
unknown
06:18
created

Eccube/Controller/Admin/Store/PluginController.php (1 issue)

Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

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