Completed
Push — experimental/3.1 ( ec61eb...005fa9 )
by Ryo
50:44 queued 43:12
created

OwnerStoreController::doUpdateConfirm()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 4
c 0
b 0
f 0
nc 1
nop 2
dl 0
loc 7
ccs 0
cts 0
cp 0
crap 2
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\HttpFoundation\Response;
41
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
42
43
/**
44
 * @Route(service=OwnerStoreController::class)
45
 */
46
class OwnerStoreController extends AbstractController
47
{
48
    /**
49
     * @Inject("config")
50
     * @var array
51
     */
52
    protected $appConfig;
53
54
    /**
55
     * @Inject(PluginRepository::class)
56
     * @var PluginRepository
57
     */
58
    protected $pluginRepository;
59
60
    /**
61
     * @Inject(PluginService::class)
62
     * @var PluginService
63
     */
64
    protected $pluginService;
65
66
    /**
67
     * @Inject("eccube.service.composer")
68
     * @var ComposerServiceInterface
69
     */
70
    protected $composerService;
71
72
    /**
73
     * @var EntityManager
74
     * @Inject("orm.em")
75
     */
76
    protected $em;
77
78
    /**
79
     * @Inject(SystemService::class)
80
     * @var SystemService
81
     */
82
    protected $systemService;
83
84
    private static $vendorName = 'ec-cube';
85
86
    /**
87
     * Owner's Store Plugin Installation Screen - Search function
88
     *
89
     * @Route("/{_admin}/store/plugin/search", name="admin_store_plugin_owners_search")
90
     * @Template("Store/plugin_search.twig")
91
     * @param Application $app
92
     * @param Request     $request
93
     * @return array
94
     */
95
    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...
96
    {
97
        // Acquire downloadable plug-in information from owners store
98
        $items = array();
99
        $promotionItems = array();
100
        $message = '';
101
        // Owner's store communication
102
        $url = $this->appConfig['package_repo_url'].'/search/packages.json';
103
        list($json, $info) = $this->getRequestApi($url);
104
        if ($json === false) {
105
            $message = $this->getResponseErrorMessage($info);
106
        } else {
107
            $data = json_decode($json, true);
108
            if (isset($data['success']) && $data['success']) {
109
                // Check plugin installed
110
                $pluginInstalled = $this->pluginRepository->findAll();
111
                // Update_status 1 : not install/purchased 、2 : Installed、 3 : Update、4 : paid purchase
112
                foreach ($data['item'] as $item) {
113
                    // Not install/purchased
114
                    $item['update_status'] = 1;
115
                    /** @var Plugin $plugin */
116
                    foreach ($pluginInstalled as $plugin) {
117
                        if ($plugin->getSource() == $item['product_id']) {
118
                            // Installed
119
                            $item['update_status'] = 2;
120
                            if ($this->pluginService->isUpdate($plugin->getVersion(), $item['version'])) {
121
                                // Need update
122
                                $item['update_status'] = 3;
123
                            }
124
                        }
125
                    }
126
                    $items[] = $item;
127
                }
128
129
                // EC-CUBE version check
130
                foreach ($items as &$item) {
131
                    // Not applicable version
132
                    $item['version_check'] = 0;
133
                    if (in_array(Constant::VERSION, $item['eccube_version'])) {
134
                        // Match version
135
                        $item['version_check'] = 1;
136
                    }
137
                    if ($item['price'] != '0' && $item['purchased'] == '0') {
138
                        // Not purchased with paid items
139
                        $item['update_status'] = 4;
140
                    }
141
                    // Add plugin dependency
142
                    $item['depend'] = $this->pluginService->getRequirePluginName($items, $item);
143
                }
144
                unset($item);
145
146
                // Promotion item
147
                $i = 0;
148 View Code Duplication
                foreach ($items as $item) {
149
                    if ($item['promotion'] == 1) {
150
                        $promotionItems[] = $item;
151
                        unset($items[$i]);
152
                    }
153
                    $i++;
154
                }
155
            } else {
156
                $message = $app->trans('admin.plugin.authentication.fail');
157
            }
158
        }
159
160
        return [
161
            'items' => $items,
162
            'promotionItems' => $promotionItems,
163
            'message' => $message,
164
        ];
165
    }
166
167
    /**
168
     * Do confirm page
169
     *
170
     * @Route("/{_admin}/store/plugin/{id}/confirm", requirements={"id" = "\d+"}, name="admin_store_plugin_install_confirm")
171
     * @Template("Store/plugin_confirm.twig")
172
     * @param Application $app
173
     * @param Request     $request
174
     * @param string      $id
175
     * @return array
176
     */
177
    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...
178
    {
179
        // Owner's store communication
180
        $url = $this->appConfig['package_repo_url'].'/search/packages.json';
181
        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...
182
        $data = json_decode($json, true);
183
        $items = $data['item'];
184
185
        // Find plugin in api
186
        $index = array_search($id, array_column($items, 'product_id'));
187
        if ($index === false) {
188
            throw new NotFoundHttpException();
189
        }
190
191
        $pluginCode = $items[$index]['product_code'];
192
193
        $plugin = $this->pluginService->buildInfo($items, $pluginCode);
194
195
        // Prevent infinity loop: A -> B -> A.
196
        $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...
197
        $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 193 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...
198
        // Unset first param
199
        unset($dependents[0]);
200
201
        return [
0 ignored issues
show
introduced by
Add a comma after each item in a multi-line array
Loading history...
202
            'item' => $plugin,
203
            'dependents' => $dependents,
204
            'is_update' => $request->get('is_update', false)
205
        ];
206
    }
207
208
    /**
209
     * Api Install plugin by composer connect with package repo
210
     *
211
     * @Route("/{_admin}/store/plugin/api/{pluginCode}/{eccubeVersion}/{version}" , name="admin_store_plugin_api_install")
212
     *
213
     * @param Application $app
214
     * @param Request     $request
215
     * @param string      $pluginCode
216
     * @param string      $eccubeVersion
217
     * @param string      $version
218
     * @return RedirectResponse
219
     */
220
    public function apiInstall(Application $app, Request $request, $pluginCode, $eccubeVersion, $version)
221
    {
222
        // Check plugin code
223
        $url = $this->appConfig['package_repo_url'].'/search/packages.json'.'?eccube_version='.$eccubeVersion.'&plugin_code='.$pluginCode.'&version='.$version;
224
        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...
225
        $existFlg = false;
226
        $data = json_decode($json, true);
227
        if ($data && isset($data['success'])) {
228
            $success = $data['success'];
229
            if ($success == '1' && isset($data['item'])) {
230
                foreach ($data['item'] as $item) {
231
                    if ($item['product_code'] == $pluginCode) {
232
                        $existFlg = true;
233
                        break;
234
                    }
235
                }
236
            }
237
        }
238
        if ($existFlg === false) {
239
            log_info(sprintf('%s plugin not found!', $pluginCode));
240
            $app->addError('admin.plugin.not.found', 'admin');
241
242
            return $app->redirect($app->url('admin_store_plugin_owners_search'));
243
        }
244
        $dependents = array();
245
        $items = $data['item'];
246
        $plugin = $this->pluginService->buildInfo($items, $pluginCode);
247
        $dependents[] = $plugin;
248
        $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 246 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...
249
250
        // Unset first param
251
        unset($dependents[0]);
252
        $dependentModifier = [];
253
        $packageNames = '';
254
        if (!empty($dependents)) {
255
            foreach ($dependents as $item) {
256
                $packageNames .= self::$vendorName . '/' . $item['product_code'] . ' ';
0 ignored issues
show
Coding Style introduced by
Concat operator must not be surrounded by spaces
Loading history...
257
                $pluginItem = [
0 ignored issues
show
introduced by
Add a comma after each item in a multi-line array
Loading history...
258
                    "product_code" => $item['product_code'],
259
                    "version" => $item['version']
260
                ];
261
                array_push($dependentModifier, $pluginItem);
262
            }
263
        }
264
        $packageNames .= self::$vendorName . '/' . $pluginCode;
0 ignored issues
show
Coding Style introduced by
Concat operator must not be surrounded by spaces
Loading history...
265
        $data = array(
0 ignored issues
show
introduced by
Add a comma after each item in a multi-line array
Loading history...
266
            'code' => $pluginCode,
267
            'version' => $version,
268
            'core_version' => $eccubeVersion,
269
            'php_version' => phpversion(),
270
            'db_version' => $this->systemService->getDbversion(),
271
            '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...
272
            'host' => $request->getHost(),
273
            'web_server' => $request->server->get("SERVER_SOFTWARE"),
274
            'composer_version' => $this->composerService->composerVersion(),
275
            'composer_execute_mode' => $this->composerService->getMode(),
276
            'dependents' => json_encode($dependentModifier)
277
        );
278
279
        try {
280
            $this->composerService->execRequire($packageNames);
281
            // Do report to package repo
282
            $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...
283
            $this->postRequestApi($url, $data);
284
            $app->addSuccess('admin.plugin.install.complete', 'admin');
285
286
            return $app->redirect($app->url('admin_store_plugin'));
287
        } catch (\Exception $exception) {
288
            log_info($exception);
289
        }
290
291
        // Do report to package repo
292
        $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...
293
        $this->postRequestApi($url, $data);
294
        $app->addError('admin.plugin.install.fail', 'admin');
295
296
        return $app->redirect($app->url('admin_store_plugin_owners_search'));
297
    }
298
299
    /**
300
     * Do confirm page
301
     *
302
     * @Route("/{_admin}/store/plugin/delete/{id}/confirm", requirements={"id" = "\d+"}, name="admin_store_plugin_delete_confirm")
303
     * @Template("Store/plugin_confirm_uninstall.twig")
304
     * @param Application $app
305
     * @param Plugin      $Plugin
306
     * @return array|RedirectResponse
307
     */
308
    public function deleteConfirm(Application $app, Plugin $Plugin)
309
    {
310
        // Owner's store communication
311
        $url = $this->appConfig['package_repo_url'].'/search/packages.json';
312
        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...
313
        $data = json_decode($json, true);
314
        $items = $data['item'];
315
316
        // The plugin depends on it
317
        $pluginCode = $Plugin->getCode();
318
        $otherDepend = $this->pluginService->findDependentPlugin($pluginCode);
319
320
        if (!empty($otherDepend)) {
321
            $DependPlugin = $this->pluginRepository->findOneBy(['code' => $otherDepend[0]]);
322
            $dependName = $otherDepend[0];
323
            if ($DependPlugin) {
324
                $dependName = $DependPlugin->getName();
325
            }
326
327
            $message = $app->trans('admin.plugin.uninstall.depend', ['%name%' => $Plugin->getName(), '%depend_name%' => $dependName]);
328
            $app->addError($message, 'admin');
329
330
            return $app->redirect($app->url('admin_store_plugin'));
331
        }
332
333
        // Check plugin in api
334
        $pluginSource = $Plugin->getSource();
335
        $index = array_search($pluginSource, array_column($items, 'product_id'));
336
        if ($index === false) {
337
            throw new NotFoundHttpException();
338
        }
339
340
        // Build info
341
        $pluginCode = $Plugin->getCode();
342
        $plugin = $this->pluginService->buildInfo($items, $pluginCode);
343
        $plugin['id'] = $Plugin->getId();
344
345
        return [
346
            'item' => $plugin,
347
        ];
348
    }
349
350
    /**
351
     * New ways to remove plugin: using composer command
352
     *
353
     * @Method("DELETE")
354
     * @Route("/{_admin}/store/plugin/api/{id}/uninstall", requirements={"id" = "\d+"}, name="admin_store_plugin_api_uninstall")
355
     * @param Application $app
356
     * @param Plugin      $Plugin
357
     * @return RedirectResponse
358
     */
359
    public function apiUninstall(Application $app, Plugin $Plugin)
360
    {
361
        $this->isTokenValid($app);
362
363
        if ($Plugin->isEnabled()) {
364
            $this->pluginService->disable($Plugin);
365
        }
366
        $pluginCode = $Plugin->getCode();
367
        $packageName = self::$vendorName.'/'.$pluginCode;
368
        try {
369
            $this->composerService->execRemove($packageName);
370
            $app->addSuccess('admin.plugin.uninstall.complete', 'admin');
371
        } catch (\Exception $exception) {
372
            log_info($exception);
373
            $app->addError('admin.plugin.uninstall.error', 'admin');
374
        }
375
376
        return $app->redirect($app->url('admin_store_plugin'));
377
    }
378
379
    /**
380
     * API request processing
381
     *
382
     * @param string  $url
383
     * @return array
384
     */
385
    private function getRequestApi($url)
386
    {
387
        $curl = curl_init($url);
388
389
        // Option array
390
        $options = array(
391
            // HEADER
392
            CURLOPT_HTTPGET => true,
393
            CURLOPT_SSL_VERIFYPEER => false,
394
            CURLOPT_RETURNTRANSFER => true,
395
            CURLOPT_FAILONERROR => true,
396
            CURLOPT_CAINFO => \Composer\CaBundle\CaBundle::getSystemCaRootBundlePath(),
397
        );
398
399
        // Set option value
400
        curl_setopt_array($curl, $options);
401
        $result = curl_exec($curl);
402
        $info = curl_getinfo($curl);
403
        $message = curl_error($curl);
404
        $info['message'] = $message;
405
        curl_close($curl);
406
407
        log_info('http get_info', $info);
408
409
        return array($result, $info);
410
    }
411
412
    /**
413
     * API post request processing
414
     *
415
     * @param string  $url
416
     * @param array $data
417
     * @return array
418
     */
419
    private function postRequestApi($url, $data)
420
    {
421
        $curl = curl_init($url);
422
        curl_setopt($curl, CURLOPT_URL, $url);
423
        curl_setopt($curl, CURLOPT_POST, 1);
424
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
425
        curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
426
        $result = curl_exec($curl);
427
        $info = curl_getinfo($curl);
428
        $message = curl_error($curl);
429
        $info['message'] = $message;
430
        curl_close($curl);
431
432
        log_info('http post_info', $info);
433
        return array($result, $info);
0 ignored issues
show
introduced by
Missing blank line before return statement
Loading history...
434
    }
435
436
    /**
437
     * Get message
438
     *
439
     * @param $info
440
     * @return string
441
     */
442 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...
443
    {
444
        if (!empty($info)) {
445
            $statusCode = $info['http_code'];
446
            $message = $info['message'];
447
448
            $message = $statusCode.' : '.$message;
449
        } else {
450
            $message = "タイムアウトエラーまたはURLの指定に誤りがあります。";
451
        }
452
453
        return $message;
454
    }
455
456
    /**
457
     * Do confirm update page
458
     *
459
     * @Route("/{_admin}/store/plugin/{id}/update/confirm", requirements={"id" = "\d+"}, name="admin_store_plugin_update_confirm")
460
     * @Template("Store/plugin_confirm.twig")
461
     * @param Application $app
462
     * @param Plugin $plugin
0 ignored issues
show
introduced by
Expected 6 spaces after parameter type; 1 found
Loading history...
463
     * @return Response
464
     */
465
    public function doUpdateConfirm(Application $app, Plugin $plugin)
0 ignored issues
show
introduced by
Declare public methods first, then protected ones and finally private ones
Loading history...
466
    {
467
        $source = $plugin->getSource();
468
        $url = $app->url('admin_store_plugin_install_confirm', ['id' => $source, 'is_update' => true]);
469
470
        return $app->forward($url);
471
    }
472
}
473