Completed
Pull Request — experimental/3.1 (#2679)
by
unknown
91:17 queued 85:09
created

OwnerStoreController::apiUninstall()   A

Complexity

Conditions 3
Paths 6

Size

Total Lines 19
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 13
c 1
b 0
f 0
nc 6
nop 2
dl 0
loc 19
ccs 0
cts 0
cp 0
crap 12
rs 9.4285
1
<?php
2
/*
3
 * This file is part of EC-CUBE
4
 *
5
 * Copyright(c) 2000-2015 LOCKON CO.,LTD. All Rights Reserved.
6
 *
7
 * http://www.lockon.co.jp/
8
 *
9
 * This program is free software; you can redistribute it and/or
10
 * modify it under the terms of the GNU General Public License
11
 * as published by the Free Software Foundation; either version 2
12
 * of the License, or (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program; if not, write to the Free Software
21
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
 */
23
namespace Eccube\Controller\Admin\Store;
24
25
use Doctrine\ORM\EntityManager;
26
use Eccube\Annotation\Inject;
27
use Eccube\Application;
28
use Eccube\Common\Constant;
29
use Eccube\Controller\AbstractController;
30
use Eccube\Entity\Plugin;
31
use Eccube\Repository\PluginRepository;
32
use Eccube\Service\Composer\ComposerServiceInterface;
33
use Eccube\Service\PluginService;
34
use Eccube\Service\SystemService;
35
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
36
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
37
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
38
use Symfony\Component\HttpFoundation\RedirectResponse;
39
use Symfony\Component\HttpFoundation\Request;
40
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
41
42
/**
43
 * @Route(service=OwnerStoreController::class)
44
 */
45
class OwnerStoreController extends AbstractController
46
{
47
    /**
48
     * @Inject("config")
49
     * @var array
50
     */
51
    protected $appConfig;
52
53
    /**
54
     * @Inject(PluginRepository::class)
55
     * @var PluginRepository
56
     */
57
    protected $pluginRepository;
58
59
    /**
60
     * @Inject(PluginService::class)
61
     * @var PluginService
62
     */
63
    protected $pluginService;
64
65
    /**
66
     * @Inject("eccube.service.composer")
67
     * @var ComposerServiceInterface
68
     */
69
    protected $composerService;
70
71
    /**
72
     * @var EntityManager
73
     * @Inject("orm.em")
74
     */
75
    protected $em;
76
77
    /**
78
     * @Inject(SystemService::class)
79
     * @var SystemService
80
     */
81
    protected $systemService;
82
83
    private static $vendorName = 'ec-cube';
84
85
    /**
86
     * Owner's Store Plugin Installation Screen - Search function
87
     *
88
     * @Route("/{_admin}/store/plugin/search", name="admin_store_plugin_owners_search")
89
     * @Template("Store/plugin_search.twig")
90
     * @param Application $app
91
     * @param Request     $request
92
     * @return array
93
     */
94
    public function search(Application $app, Request $request)
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...
95
    {
96
        // Acquire downloadable plug-in information from owners store
97
        $items = array();
98
        $promotionItems = array();
99
        $message = '';
100
        // Owner's store communication
101
        $url = $this->appConfig['package_repo_url'].'/search/packages.json';
102
        list($json, $info) = $this->getRequestApi($url);
103
        if ($json === false) {
104
            $message = $this->getResponseErrorMessage($info);
105
        } else {
106
            $data = json_decode($json, true);
107
            if (isset($data['success']) && $data['success']) {
108
                // Check plugin installed
109
                $pluginInstalled = $this->pluginRepository->findAll();
110
                // Update_status 1 : not install/purchased 、2 : Installed、 3 : Update、4 : paid purchase
111
                foreach ($data['item'] as $item) {
112
                    // Not install/purchased
113
                    $item['update_status'] = 1;
114
                    /** @var Plugin $plugin */
115
                    foreach ($pluginInstalled as $plugin) {
116
                        if ($plugin->getSource() == $item['product_id']) {
117
                            // Installed
118
                            $item['update_status'] = 2;
119
                            if ($this->pluginService->isUpdate($plugin->getVersion(), $item['version'])) {
120
                                // Need update
121
                                $item['update_status'] = 3;
122
                            }
123
                        }
124
                    }
125
                    $items[] = $item;
126
                }
127
128
                // EC-CUBE version check
129
                foreach ($items as &$item) {
130
                    // Not applicable version
131
                    $item['version_check'] = 0;
132
                    if (in_array(Constant::VERSION, $item['eccube_version'])) {
133
                        // Match version
134
                        $item['version_check'] = 1;
135
                    }
136
                    if ($item['price'] != '0' && $item['purchased'] == '0') {
137
                        // Not purchased with paid items
138
                        $item['update_status'] = 4;
139
                    }
140
                    // Add plugin dependency
141
                    $item['depend'] = $this->pluginService->getRequirePluginName($items, $item);
142
                }
143
                unset($item);
144
145
                // Promotion item
146
                $i = 0;
147 View Code Duplication
                foreach ($items as $item) {
148
                    if ($item['promotion'] == 1) {
149
                        $promotionItems[] = $item;
150
                        unset($items[$i]);
151
                    }
152
                    $i++;
153
                }
154
            } else {
155
                $message = $app->trans('admin.plugin.authentication.fail');
156
            }
157
        }
158
159
        return [
160
            'items' => $items,
161
            'promotionItems' => $promotionItems,
162
            'message' => $message,
163
        ];
164
    }
165
166
    /**
167
     * Do confirm page
168
     *
169
     * @Route("/{_admin}/store/plugin/{id}/confirm", requirements={"id" = "\d+"}, name="admin_store_plugin_install_confirm")
170
     * @Template("Store/plugin_confirm.twig")
171
     * @param Application $app
172
     * @param Request     $request
173
     * @param string      $id
174
     * @return array
175
     */
176
    public function doConfirm(Application $app, Request $request, $id)
0 ignored issues
show
Unused Code introduced by
The parameter $app 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...
177
    {
178
        // Owner's store communication
179
        $url = $this->appConfig['package_repo_url'].'/search/packages.json';
180
        list($json, $info) = $this->getRequestApi($url);
0 ignored issues
show
Unused Code introduced by
The assignment to $info is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
181
        $data = json_decode($json, true);
182
        $items = $data['item'];
183
184
        // Find plugin in api
185
        $index = array_search($id, array_column($items, 'product_id'));
186
        if ($index === false) {
187
            throw new NotFoundHttpException();
188
        }
189
190
        $pluginCode = $items[$index]['product_code'];
191
192
        $plugin = $this->pluginService->buildInfo($items, $pluginCode);
193
194
        // Prevent infinity loop: A -> B -> A.
195
        $dependents[] = $plugin;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$dependents was never initialized. Although not strictly required by PHP, it is generally a good practice to add $dependents = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
196
        $dependents = $this->pluginService->getDependency($items, $plugin, $dependents);
0 ignored issues
show
Bug introduced by
It seems like $plugin defined by $this->pluginService->bu...fo($items, $pluginCode) on line 192 can also be of type null; however, Eccube\Service\PluginService::getDependency() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
197
        // Unset first param
198
        unset($dependents[0]);
199
200
        return [
0 ignored issues
show
introduced by
Add a comma after each item in a multi-line array
Loading history...
201
            'item' => $plugin,
202
            'dependents' => $dependents,
203
            'is_update' => $request->get('is_update', false)
204
        ];
205
    }
206
207
    /**
208
     * Api Install plugin by composer connect with package repo
209
     *
210
     * @Route("/{_admin}/store/plugin/api/{pluginCode}/{eccubeVersion}/{version}" , name="admin_store_plugin_api_install")
211
     *
212
     * @param Application $app
213
     * @param Request     $request
214
     * @param string      $pluginCode
215
     * @param string      $eccubeVersion
216
     * @param string      $version
217
     * @return RedirectResponse
218
     */
219
    public function apiInstall(Application $app, Request $request, $pluginCode, $eccubeVersion, $version)
220
    {
221
        // Check plugin code
222
        $url = $this->appConfig['package_repo_url'].'/search/packages.json'.'?eccube_version='.$eccubeVersion.'&plugin_code='.$pluginCode.'&version='.$version;
223
        list($json, $info) = $this->getRequestApi($url);
0 ignored issues
show
Unused Code introduced by
The assignment to $info is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
224
        $existFlg = false;
225
        $data = json_decode($json, true);
226
        if ($data && isset($data['success'])) {
227
            $success = $data['success'];
228
            if ($success == '1' && isset($data['item'])) {
229
                foreach ($data['item'] as $item) {
230
                    if ($item['product_code'] == $pluginCode) {
231
                        $existFlg = true;
232
                        break;
233
                    }
234
                }
235
            }
236
        }
237
        if ($existFlg === false) {
238
            log_info(sprintf('%s plugin not found!', $pluginCode));
239
            $app->addError('admin.plugin.not.found', 'admin');
240
241
            return $app->redirect($app->url('admin_store_plugin_owners_search'));
242
        }
243
        $dependents = array();
244
        $items = $data['item'];
245
        $plugin = $this->pluginService->buildInfo($items, $pluginCode);
246
        $dependents[] = $plugin;
247
        $dependents = $this->pluginService->getDependency($items, $plugin, $dependents);
0 ignored issues
show
Bug introduced by
It seems like $plugin defined by $this->pluginService->bu...fo($items, $pluginCode) on line 245 can also be of type null; however, Eccube\Service\PluginService::getDependency() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
248
249
        // Unset first param
250
        unset($dependents[0]);
251
        $dependentModifier = [];
252
        $packageNames = '';
253
        if (!empty($dependents)) {
254
            foreach ($dependents as $item) {
255
                $packageNames .= self::$vendorName . '/' . $item['product_code'] . ' ';
0 ignored issues
show
Coding Style introduced by
Concat operator must not be surrounded by spaces
Loading history...
256
                $pluginItem = [
0 ignored issues
show
introduced by
Add a comma after each item in a multi-line array
Loading history...
257
                    "product_code" => $item['product_code'],
258
                    "version" => $item['version']
259
                ];
260
                array_push($dependentModifier, $pluginItem);
261
            }
262
        }
263
        $packageNames .= self::$vendorName . '/' . $pluginCode;
0 ignored issues
show
Coding Style introduced by
Concat operator must not be surrounded by spaces
Loading history...
264
        $data = array(
0 ignored issues
show
introduced by
Add a comma after each item in a multi-line array
Loading history...
265
            'code' => $pluginCode,
266
            'version' => $version,
267
            'core_version' => $eccubeVersion,
268
            'php_version' => phpversion(),
269
            'db_version' => $this->systemService->getDbversion(),
270
            'os' => php_uname('s') . ' ' . php_uname('r') . ' ' . php_uname('v'),
0 ignored issues
show
Coding Style introduced by
Concat operator must not be surrounded by spaces
Loading history...
271
            'host' => $request->getHost(),
272
            'web_server' => $request->server->get("SERVER_SOFTWARE"),
273
            'composer_version' => $this->composerService->composerVersion(),
274
            'composer_execute_mode' => $this->composerService->getMode(),
275
            'dependents' => json_encode($dependentModifier)
276
        );
277
278
        try {
279
            $this->composerService->execRequire($packageNames);
280
            // Do report to package repo
281
            $url = $this->appConfig['package_repo_url'] . '/report';
0 ignored issues
show
Coding Style introduced by
Concat operator must not be surrounded by spaces
Loading history...
282
            $this->postRequestApi($url, $data);
283
            $app->addSuccess('admin.plugin.install.complete', 'admin');
284
285
            return $app->redirect($app->url('admin_store_plugin'));
286
        } catch (\Exception $exception) {
287
            log_info($exception);
288
        }
289
290
        // Do report to package repo
291
        $url = $this->appConfig['package_repo_url'] . '/report/fail';
0 ignored issues
show
Coding Style introduced by
Concat operator must not be surrounded by spaces
Loading history...
292
        $this->postRequestApi($url, $data);
293
        $app->addError('admin.plugin.install.fail', 'admin');
294
295
        return $app->redirect($app->url('admin_store_plugin_owners_search'));
296
    }
297
298
    /**
299
     * Do confirm page
300
     *
301
     * @Route("/{_admin}/store/plugin/delete/{id}/confirm", requirements={"id" = "\d+"}, name="admin_store_plugin_delete_confirm")
302
     * @Template("Store/plugin_confirm_uninstall.twig")
303
     * @param Application $app
304
     * @param Plugin      $Plugin
305
     * @return array|RedirectResponse
306
     */
307
    public function deleteConfirm(Application $app, Plugin $Plugin)
308
    {
309
        // Owner's store communication
310
        $url = $this->appConfig['package_repo_url'].'/search/packages.json';
311
        list($json, $info) = $this->getRequestApi($url);
0 ignored issues
show
Unused Code introduced by
The assignment to $info is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
312
        $data = json_decode($json, true);
313
        $items = $data['item'];
314
315
        // The plugin depends on it
316
        $pluginCode = $Plugin->getCode();
317
        $otherDepend = $this->pluginService->findDependentPlugin($pluginCode);
318
319
        if (!empty($otherDepend)) {
320
            $DependPlugin = $this->pluginRepository->findOneBy(['code' => $otherDepend[0]]);
321
            $dependName = $otherDepend[0];
322
            if ($DependPlugin) {
323
                $dependName = $DependPlugin->getName();
324
            }
325
326
            $message = $app->trans('admin.plugin.uninstall.depend', ['%name%' => $Plugin->getName(), '%depend_name%' => $dependName]);
327
            $app->addError($message, 'admin');
328
329
            return $app->redirect($app->url('admin_store_plugin'));
330
        }
331
332
        // Check plugin in api
333
        $pluginSource = $Plugin->getSource();
334
        $index = array_search($pluginSource, array_column($items, 'product_id'));
335
        if ($index === false) {
336
            throw new NotFoundHttpException();
337
        }
338
339
        // Build info
340
        $pluginCode = $Plugin->getCode();
341
        $plugin = $this->pluginService->buildInfo($items, $pluginCode);
342
        $plugin['id'] = $Plugin->getId();
343
344
        return [
345
            'item' => $plugin,
346
        ];
347
    }
348
349
    /**
350
     * New ways to remove plugin: using composer command
351
     *
352
     * @Method("DELETE")
353
     * @Route("/{_admin}/store/plugin/api/{id}/uninstall", requirements={"id" = "\d+"}, name="admin_store_plugin_api_uninstall")
354
     * @param Application $app
355
     * @param Plugin      $Plugin
356
     * @return RedirectResponse
357
     */
358
    public function apiUninstall(Application $app, Plugin $Plugin)
359
    {
360
        $this->isTokenValid($app);
361
362
        if ($Plugin->isEnabled()) {
363
            $this->pluginService->disable($Plugin);
364
        }
365
        $pluginCode = $Plugin->getCode();
366
        $packageName = self::$vendorName.'/'.$pluginCode;
367
        try {
368
            $this->composerService->execRemove($packageName);
369
            $app->addSuccess('admin.plugin.uninstall.complete', 'admin');
370
        } catch (\Exception $exception) {
371
            log_info($exception);
372
            $app->addError('admin.plugin.uninstall.error', 'admin');
373
        }
374
375
        return $app->redirect($app->url('admin_store_plugin'));
376
    }
377
378
    /**
379
     * API request processing
380
     *
381
     * @param string  $url
382
     * @return array
383
     */
384
    private function getRequestApi($url)
385
    {
386
        $curl = curl_init($url);
387
388
        // Option array
389
        $options = array(
390
            // HEADER
391
            CURLOPT_HTTPGET => true,
392
            CURLOPT_SSL_VERIFYPEER => false,
393
            CURLOPT_RETURNTRANSFER => true,
394
            CURLOPT_FAILONERROR => true,
395
            CURLOPT_CAINFO => \Composer\CaBundle\CaBundle::getSystemCaRootBundlePath(),
396
        );
397
398
        // Set option value
399
        curl_setopt_array($curl, $options);
400
        $result = curl_exec($curl);
401
        $info = curl_getinfo($curl);
402
        $message = curl_error($curl);
403
        $info['message'] = $message;
404
        curl_close($curl);
405
406
        log_info('http get_info', $info);
407
408
        return array($result, $info);
409
    }
410
411
    /**
412
     * API post request processing
413
     *
414
     * @param string  $url
415
     * @param array $data
416
     * @return array
417
     */
418
    private function postRequestApi($url, $data)
419
    {
420
        $curl = curl_init($url);
421
        curl_setopt($curl, CURLOPT_URL, $url);
422
        curl_setopt($curl, CURLOPT_POST, 1);
423
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
424
        curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
425
        $result = curl_exec($curl);
426
        $info = curl_getinfo($curl);
427
        $message = curl_error($curl);
428
        $info['message'] = $message;
429
        curl_close($curl);
430
431
        log_info('http post_info', $info);
432
        return array($result, $info);
0 ignored issues
show
introduced by
Missing blank line before return statement
Loading history...
433
    }
434
435
    /**
436
     * Get message
437
     *
438
     * @param $info
439
     * @return string
440
     */
441 View Code Duplication
    private function getResponseErrorMessage($info)
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...
442
    {
443
        if (!empty($info)) {
444
            $statusCode = $info['http_code'];
445
            $message = $info['message'];
446
447
            $message = $statusCode.' : '.$message;
448
        } else {
449
            $message = "タイムアウトエラーまたはURLの指定に誤りがあります。";
450
        }
451
452
        return $message;
453
    }
454
455
    /**
456
     * Do confirm update page
457
     *
458
     * @Route("/{_admin}/store/plugin/{id}/update/confirm", requirements={"id" = "\d+"}, name="admin_store_plugin_update_confirm")
459
     * @Template("Store/plugin_confirm.twig")
460
     * @param Application $app
461
     * @param Request $request
0 ignored issues
show
introduced by
Expected 5 spaces after parameter type; 1 found
Loading history...
462
     * @param string $id
0 ignored issues
show
introduced by
Expected 6 spaces after parameter type; 1 found
Loading history...
463
     * @return array
464
     */
465
    public function doUpdateConfirm(Application $app, 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...
introduced by
Declare public methods first, then protected ones and finally private ones
Loading history...
466
    {
467
        $url = $app->url('admin_store_plugin_install_confirm', ['id' => $id, 'is_update' => true]);
468
        return $app->forward($url);
0 ignored issues
show
introduced by
Missing blank line before return statement
Loading history...
469
    }
470
}
471