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\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
|
|
|
/** @var CacheUtil */ |
73
|
|
|
private $cacheUtil; |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* OwnerStoreController constructor. |
77
|
|
|
* |
78
|
|
|
* @param PluginRepository $pluginRepository |
79
|
|
|
* @param PluginService $pluginService |
80
|
|
|
* @param ComposerProcessService $composerProcessService |
81
|
|
|
* @param ComposerServiceInterface $composerService |
82
|
|
|
* @param SystemService $systemService |
83
|
|
|
* @param PluginApiService $pluginApiService |
84
|
|
|
* @param BaseInfoRepository $baseInfoRepository |
85
|
|
|
* @param CacheUtil $cacheUtil |
86
|
|
|
* |
87
|
|
|
* @throws \Doctrine\ORM\NoResultException |
88
|
|
|
* @throws \Doctrine\ORM\NonUniqueResultException |
89
|
|
|
*/ |
90
|
|
|
public function __construct( |
91
|
|
|
PluginRepository $pluginRepository, |
92
|
|
|
PluginService $pluginService, |
93
|
|
|
ComposerProcessService $composerProcessService, |
94
|
|
|
ComposerServiceInterface $composerService, |
95
|
|
|
SystemService $systemService, |
96
|
|
|
PluginApiService $pluginApiService, |
97
|
|
|
BaseInfoRepository $baseInfoRepository, |
98
|
|
|
CacheUtil $cacheUtil |
99
|
|
|
) { |
100
|
|
|
$this->pluginRepository = $pluginRepository; |
101
|
|
|
$this->pluginService = $pluginService; |
102
|
|
|
$this->systemService = $systemService; |
103
|
|
|
$this->pluginApiService = $pluginApiService; |
104
|
|
|
$this->BaseInfo = $baseInfoRepository->get(); |
105
|
|
|
$this->cacheUtil = $cacheUtil; |
106
|
|
|
|
107
|
|
|
// TODO: Check the flow of the composer service below |
108
|
|
|
$memoryLimit = $this->systemService->getMemoryLimit(); |
109
|
|
|
if ($memoryLimit == -1 or $memoryLimit >= $this->eccubeConfig['eccube_composer_memory_limit']) { |
110
|
|
|
$this->composerService = $composerService; |
111
|
|
|
} else { |
112
|
|
|
$this->composerService = $composerProcessService; |
113
|
|
|
} |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
/** |
117
|
|
|
* Owner's Store Plugin Installation Screen - Search function |
118
|
|
|
* |
119
|
|
|
* @Route("/search", name="admin_store_plugin_owners_search") |
120
|
|
|
* @Route("/search/page/{page_no}", name="admin_store_plugin_owners_search_page", requirements={"page_no" = "\d+"}) |
121
|
|
|
* @Template("@admin/Store/plugin_search.twig") |
122
|
|
|
* |
123
|
|
|
* @param Request $request |
124
|
|
|
* @param int $page_no |
125
|
|
|
* @param Paginator $paginator |
126
|
|
|
* |
127
|
|
|
* @return array |
128
|
|
|
*/ |
129
|
|
|
public function search(Request $request, $page_no = null, Paginator $paginator) |
130
|
|
|
{ |
131
|
|
|
if (empty($this->BaseInfo->getAuthenticationKey())) { |
132
|
|
|
$this->addWarning('認証キーを設定してください。', 'admin'); |
133
|
|
|
|
134
|
|
|
return $this->redirectToRoute('admin_store_authentication_setting'); |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
// Acquire downloadable plug-in information from owners store |
138
|
|
|
$category = []; |
139
|
|
|
|
140
|
|
|
$json = $this->pluginApiService->getCategory(); |
141
|
|
|
if (!empty($json)) { |
142
|
|
|
$data = json_decode($json, true); |
143
|
|
|
$category = array_column($data, 'name', 'id'); |
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
// build form with master data |
147
|
|
|
$builder = $this->formFactory |
148
|
|
|
->createBuilder(SearchPluginApiType::class, null, ['category' => $category]); |
149
|
|
|
$searchForm = $builder->getForm(); |
150
|
|
|
|
151
|
|
|
$searchForm->handleRequest($request); |
152
|
|
|
$searchData = $searchForm->getData(); |
153
|
|
|
if ($searchForm->isSubmitted()) { |
154
|
|
|
if ($searchForm->isValid()) { |
155
|
|
|
$page_no = 1; |
156
|
|
|
$searchData = $searchForm->getData(); |
157
|
|
|
$this->session->set('eccube.admin.plugin_api.search', FormUtil::getViewData($searchForm)); |
158
|
|
|
$this->session->set('eccube.admin.plugin_api.search.page_no', $page_no); |
159
|
|
|
} |
160
|
|
|
} else { |
161
|
|
|
// quick search |
162
|
|
|
if (is_numeric($categoryId = $request->get('category_id')) && array_key_exists($categoryId, $category)) { |
163
|
|
|
$searchForm['category_id']->setData($categoryId); |
164
|
|
|
} |
165
|
|
|
// reset page count |
166
|
|
|
$this->session->set('eccube.admin.plugin_api.search.page_count', $this->eccubeConfig->get('eccube_default_page_count')); |
167
|
|
View Code Duplication |
if (null !== $page_no || $request->get('resume')) { |
|
|
|
|
168
|
|
|
if ($page_no) { |
|
|
|
|
169
|
|
|
$this->session->set('eccube.admin.plugin_api.search.page_no', (int) $page_no); |
170
|
|
|
} else { |
171
|
|
|
$page_no = $this->session->get('eccube.admin.plugin_api.search.page_no', 1); |
172
|
|
|
} |
173
|
|
|
$viewData = $this->session->get('eccube.admin.plugin_api.search', []); |
174
|
|
|
$searchData = FormUtil::submitAndGetData($searchForm, $viewData); |
175
|
|
|
} else { |
176
|
|
|
$page_no = 1; |
177
|
|
|
// submit default value |
178
|
|
|
$viewData = FormUtil::getViewData($searchForm); |
179
|
|
|
$searchData = FormUtil::submitAndGetData($searchForm, $viewData); |
180
|
|
|
$this->session->set('eccube.admin.plugin_api.search', $searchData); |
181
|
|
|
$this->session->set('eccube.admin.plugin_api.search.page_no', $page_no); |
182
|
|
|
} |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
// set page count |
186
|
|
|
$pageCount = $this->session->get('eccube.admin.plugin_api.search.page_count', $this->eccubeConfig->get('eccube_default_page_count')); |
187
|
|
|
if (($PageMax = $searchForm['page_count']->getData()) instanceof PageMax) { |
188
|
|
|
$pageCount = $PageMax->getId(); |
189
|
|
|
$this->session->set('eccube.admin.plugin_api.search.page_count', $pageCount); |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
// Owner's store communication |
193
|
|
|
$searchData['page_no'] = $page_no; |
194
|
|
|
$searchData['page_count'] = $pageCount; |
195
|
|
|
|
196
|
|
|
$total = 0; |
197
|
|
|
$items = []; |
198
|
|
|
|
199
|
|
|
try { |
200
|
|
|
$data = $this->pluginApiService->getPlugins($searchData); |
201
|
|
|
$total = $data['total']; |
202
|
|
|
$items = $data['plugins']; |
203
|
|
|
} catch (PluginApiException $e) { |
204
|
|
|
$this->addError($e->getMessage(), 'admin'); |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
// The usage is set because `$items` are already paged. |
208
|
|
|
// virtual paging |
209
|
|
|
$pagination = $paginator->paginate($items, 1, $pageCount); |
210
|
|
|
$pagination->setTotalItemCount($total); |
211
|
|
|
$pagination->setCurrentPageNumber($page_no); |
212
|
|
|
$pagination->setItemNumberPerPage($pageCount); |
213
|
|
|
|
214
|
|
|
return [ |
215
|
|
|
'pagination' => $pagination, |
216
|
|
|
'total' => $total, |
217
|
|
|
'searchForm' => $searchForm->createView(), |
218
|
|
|
'page_no' => $page_no, |
219
|
|
|
'Categories' => $category, |
220
|
|
|
]; |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
/** |
224
|
|
|
* Do confirm page |
225
|
|
|
* |
226
|
|
|
* @Route("/install/{id}/confirm", requirements={"id" = "\d+"}, name="admin_store_plugin_install_confirm") |
227
|
|
|
* @Template("@admin/Store/plugin_confirm.twig") |
228
|
|
|
* |
229
|
|
|
* @param Request $request |
230
|
|
|
* |
231
|
|
|
* @return array |
232
|
|
|
* |
233
|
|
|
* @throws \Eccube\Exception\PluginException |
234
|
|
|
*/ |
235
|
|
|
public function doConfirm(Request $request, $id) |
|
|
|
|
236
|
|
|
{ |
237
|
|
|
try { |
238
|
|
|
$item = $this->pluginApiService->getPlugin($id); |
239
|
|
|
// Todo: need define item's dependency mechanism |
240
|
|
|
$requires = $this->pluginService->getPluginRequired($item); |
241
|
|
|
|
242
|
|
|
return [ |
243
|
|
|
'item' => $item, |
244
|
|
|
'requires' => $requires, |
245
|
|
|
'is_update' => false, |
246
|
|
|
]; |
247
|
|
|
} catch (PluginApiException $e) { |
248
|
|
|
$this->addError($e->getMessage(), 'admin'); |
249
|
|
|
|
250
|
|
|
return $this->redirectToRoute('admin_store_authentication_setting'); |
251
|
|
|
} |
252
|
|
|
} |
253
|
|
|
|
254
|
|
|
/** |
255
|
|
|
* Api Install plugin by composer connect with package repo |
256
|
|
|
* |
257
|
|
|
* @Route("/install", name="admin_store_plugin_api_install", methods={"POST"}) |
258
|
|
|
* |
259
|
|
|
* @param Request $request |
260
|
|
|
* |
261
|
|
|
* @return \Symfony\Component\HttpFoundation\JsonResponse |
262
|
|
|
*/ |
263
|
|
|
public function apiInstall(Request $request) |
264
|
|
|
{ |
265
|
|
|
$this->isTokenValid(); |
266
|
|
|
|
267
|
|
|
$this->cacheUtil->clearCache(); |
268
|
|
|
|
269
|
|
|
$pluginCode = $request->get('pluginCode'); |
270
|
|
|
|
271
|
|
|
$log = null; |
272
|
|
|
try { |
273
|
|
|
$log = $this->composerService->execRequire('ec-cube/'.$pluginCode); |
274
|
|
|
|
275
|
|
|
return $this->json(['success' => true, 'log' => $log]); |
276
|
|
|
} catch (\Exception $e) { |
277
|
|
|
$log = $e->getMessage(); |
278
|
|
|
log_error($e); |
279
|
|
|
} |
280
|
|
|
|
281
|
|
|
return $this->json(['success' => false, 'log' => $log], 500); |
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
/** |
285
|
|
|
* New ways to remove plugin: using composer command |
286
|
|
|
* |
287
|
|
|
* @Route("/delete/{id}/uninstall", requirements={"id" = "\d+"}, name="admin_store_plugin_api_uninstall", methods={"DELETE"}) |
288
|
|
|
* |
289
|
|
|
* @param Plugin $Plugin |
290
|
|
|
* |
291
|
|
|
* @return \Symfony\Component\HttpFoundation\JsonResponse |
292
|
|
|
*/ |
293
|
|
|
public function apiUninstall(Plugin $Plugin) |
294
|
|
|
{ |
295
|
|
|
$this->isTokenValid(); |
296
|
|
|
|
297
|
|
|
$this->cacheUtil->clearCache(); |
298
|
|
|
|
299
|
|
|
if ($Plugin->isEnabled()) { |
300
|
|
|
return $this->json(['success' => false, 'message' => trans('admin.plugin.uninstall.error.not_disable')], 400); |
301
|
|
|
} |
302
|
|
|
|
303
|
|
|
$pluginCode = $Plugin->getCode(); |
304
|
|
|
$otherDepend = $this->pluginService->findDependentPlugin($pluginCode); |
305
|
|
|
|
306
|
|
|
if (!empty($otherDepend)) { |
307
|
|
|
$DependPlugin = $this->pluginRepository->findOneBy(['code' => $otherDepend[0]]); |
308
|
|
|
$dependName = $otherDepend[0]; |
309
|
|
|
if ($DependPlugin) { |
310
|
|
|
$dependName = $DependPlugin->getName(); |
311
|
|
|
} |
312
|
|
|
$message = trans('admin.plugin.uninstall.depend', ['%name%' => $Plugin->getName(), '%depend_name%' => $dependName]); |
313
|
|
|
|
314
|
|
|
return $this->json(['success' => false, 'message' => $message], 400); |
315
|
|
|
} |
316
|
|
|
|
317
|
|
|
$pluginCode = $Plugin->getCode(); |
318
|
|
|
$packageName = self::$vendorName.'/'.$pluginCode; |
319
|
|
|
|
320
|
|
|
try { |
321
|
|
|
$log = $this->composerService->execRemove($packageName); |
322
|
|
|
|
323
|
|
|
return $this->json(['success' => false, 'log' => $log]); |
324
|
|
|
} catch (\Exception $e) { |
325
|
|
|
log_error($e); |
326
|
|
|
|
327
|
|
|
return $this->json(['success' => false, 'log' => $e->getMessage()], 500); |
328
|
|
|
} |
329
|
|
|
} |
330
|
|
|
|
331
|
|
|
/** |
332
|
|
|
* オーナーズブラグインインストール、アップデート |
333
|
|
|
* |
334
|
|
|
* @Route("/upgrade", name="admin_store_plugin_api_upgrade", methods={"POST"}) |
335
|
|
|
* |
336
|
|
|
* @param Request $request |
337
|
|
|
* |
338
|
|
|
* @return \Symfony\Component\HttpFoundation\JsonResponse |
339
|
|
|
*/ |
340
|
|
|
public function apiUpgrade(Request $request) |
341
|
|
|
{ |
342
|
|
|
$this->isTokenValid(); |
343
|
|
|
|
344
|
|
|
$this->cacheUtil->clearCache(); |
345
|
|
|
|
346
|
|
|
$pluginCode = $request->get('pluginCode'); |
347
|
|
|
$version = $request->get('version'); |
348
|
|
|
|
349
|
|
|
$log = null; |
350
|
|
|
try { |
351
|
|
|
$log = $this->composerService->execRequire('ec-cube/'.$pluginCode.':'.$version); |
352
|
|
|
return $this->json(['success' => true, 'log' => $log]); |
353
|
|
|
} catch (\Exception $e) { |
354
|
|
|
$log = $e->getMessage(); |
355
|
|
|
log_error($e); |
356
|
|
|
} |
357
|
|
|
|
358
|
|
|
return $this->json(['success' => false, 'log' => $log], 500); |
359
|
|
|
} |
360
|
|
|
|
361
|
|
|
/** |
362
|
|
|
* オーナーズブラグインインストール、スキーマ更新 |
363
|
|
|
* |
364
|
|
|
* @Route("/schema_update", name="admin_store_plugin_api_schema_update", methods={"POST"}) |
365
|
|
|
* |
366
|
|
|
* @param Request $request |
367
|
|
|
* |
368
|
|
|
* @return \Symfony\Component\HttpFoundation\JsonResponse |
369
|
|
|
*/ |
370
|
|
|
public function apiSchemaUpdate(Request $request) |
371
|
|
|
{ |
372
|
|
|
$this->isTokenValid(); |
373
|
|
|
|
374
|
|
|
$this->cacheUtil->clearCache(); |
375
|
|
|
|
376
|
|
|
$pluginCode = $request->get('pluginCode'); |
377
|
|
|
|
378
|
|
|
try { |
379
|
|
|
$Plugin = $this->pluginRepository->findByCode($pluginCode); |
380
|
|
|
|
381
|
|
|
if (!$Plugin) { |
382
|
|
|
throw new NotFoundHttpException(); |
383
|
|
|
} |
384
|
|
|
|
385
|
|
|
$config = $this->pluginService->readConfig($this->pluginService->calcPluginDir($Plugin->getCode())); |
386
|
|
|
|
387
|
|
|
ob_start(); |
388
|
|
|
$this->pluginService->generateProxyAndUpdateSchema($Plugin, $config); |
389
|
|
|
|
390
|
|
|
// 初期化されていなければインストール処理を実行する |
391
|
|
|
if (!$Plugin->isInitialized()) { |
392
|
|
|
$this->pluginService->callPluginManagerMethod($config, 'install'); |
393
|
|
|
$Plugin->setInitialized(true); |
394
|
|
|
$this->entityManager->persist($Plugin); |
395
|
|
|
$this->entityManager->flush(); |
396
|
|
|
} |
397
|
|
|
|
398
|
|
|
$log = ob_get_clean(); |
399
|
|
|
while (ob_get_level() > 0) { |
400
|
|
|
ob_end_flush(); |
401
|
|
|
} |
402
|
|
|
|
403
|
|
|
return $this->json(['success' => true, 'log' => $log]); |
404
|
|
|
} catch (\Exception $e) { |
405
|
|
|
$log = $e->getMessage(); |
406
|
|
|
log_error($e); |
407
|
|
|
|
408
|
|
|
return $this->json(['success' => false, 'log' => $log], 500); |
409
|
|
|
} |
410
|
|
|
} |
411
|
|
|
|
412
|
|
|
/** |
413
|
|
|
* オーナーズブラグインインストール、更新処理 |
414
|
|
|
* |
415
|
|
|
* @Route("/update", name="admin_store_plugin_api_update", methods={"POST"}) |
416
|
|
|
* |
417
|
|
|
* @param Request $request |
418
|
|
|
* |
419
|
|
|
* @return \Symfony\Component\HttpFoundation\JsonResponse |
420
|
|
|
*/ |
421
|
|
|
public function apiUpdate(Request $request) |
422
|
|
|
{ |
423
|
|
|
$this->isTokenValid(); |
424
|
|
|
|
425
|
|
|
$this->cacheUtil->clearCache(); |
426
|
|
|
|
427
|
|
|
$pluginCode = $request->get('pluginCode'); |
428
|
|
|
|
429
|
|
|
$log = null; |
430
|
|
|
try { |
431
|
|
|
$Plugin = $this->pluginRepository->findByCode($pluginCode); |
432
|
|
|
if (!$Plugin) { |
433
|
|
|
throw new NotFoundHttpException(); |
434
|
|
|
} |
435
|
|
|
|
436
|
|
|
$config = $this->pluginService->readConfig($this->pluginService->calcPluginDir($Plugin->getCode())); |
437
|
|
|
ob_start(); |
438
|
|
|
$this->pluginService->updatePlugin($Plugin, $config); |
439
|
|
|
$log = ob_get_clean(); |
440
|
|
|
while (ob_get_level() > 0) { |
441
|
|
|
ob_end_flush(); |
442
|
|
|
} |
443
|
|
|
|
444
|
|
|
return $this->json(['success' => true, 'log' => $log]); |
445
|
|
|
} catch (\Exception $e) { |
446
|
|
|
$log = $e->getMessage(); |
447
|
|
|
log_error($e); |
448
|
|
|
} |
449
|
|
|
|
450
|
|
|
return $this->json(['success' => false, 'log' => $log], 500); |
451
|
|
|
} |
452
|
|
|
|
453
|
|
|
/** |
454
|
|
|
* Do confirm update page |
455
|
|
|
* |
456
|
|
|
* @Route("/upgrade/{id}/confirm", requirements={"id" = "\d+"}, name="admin_store_plugin_update_confirm") |
457
|
|
|
* @Template("@admin/Store/plugin_confirm.twig") |
458
|
|
|
* |
459
|
|
|
* @param Plugin $Plugin |
460
|
|
|
* |
461
|
|
|
* @return array |
462
|
|
|
*/ |
463
|
|
|
public function doUpdateConfirm(Plugin $Plugin) |
464
|
|
|
{ |
465
|
|
|
try { |
466
|
|
|
$item = $this->pluginApiService->getPlugin($Plugin->getSource()); |
467
|
|
|
|
468
|
|
|
return [ |
469
|
|
|
'item' => $item, |
470
|
|
|
'requires' => [], |
471
|
|
|
'is_update' => true, |
472
|
|
|
'Plugin' => $Plugin, |
473
|
|
|
]; |
474
|
|
|
} catch (PluginApiException $e) { |
475
|
|
|
$this->addError($e->getMessage(), 'admin'); |
476
|
|
|
|
477
|
|
|
return $this->redirectToRoute('admin_store_authentication_setting'); |
478
|
|
|
} |
479
|
|
|
} |
480
|
|
|
} |
481
|
|
|
|
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.