Completed
Pull Request — master (#358)
by Simon
03:44
created

preDispatch()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 9
Ratio 100 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 0
dl 9
loc 9
rs 9.6666
c 0
b 0
f 0
1
<?php
2
/**
3
 * (c) shopware AG <[email protected]>
4
 * For the full copyright and license information, please view the LICENSE
5
 * file that was distributed with this source code.
6
 */
7
8
use Shopware\Components\CSRFWhitelistAware;
9
use Shopware\Components\Model\ModelManager;
10
use Shopware\CustomModels\Connect\Attribute;
11
use Shopware\Models\Article\Repository as ArticleRepository;
12
use Shopware\Models\Category\Category;
13
use ShopwarePlugins\Connect\Components\Config;
14
use ShopwarePlugins\Connect\Components\ConnectExport;
15
use ShopwarePlugins\Connect\Components\ConnectFactory;
16
use ShopwarePlugins\Connect\Components\ErrorHandler;
17
use Shopware\Models\Article\Article;
18
use ShopwarePlugins\Connect\Components\Validator\ProductAttributesValidator\ProductsAttributesValidator;
19
use ShopwarePlugins\Connect\Components\Marketplace\MarketplaceSettingsApplier;
20
use ShopwarePlugins\Connect\Components\Marketplace\MarketplaceSettings;
21
use ShopwarePlugins\Connect\Components\ProductStream\ProductStreamService;
22
use ShopwarePlugins\Connect\Components\SnHttpClient;
23
use ShopwarePlugins\Connect\Struct\SearchCriteria;
24
use ShopwarePlugins\Connect\Subscribers\Connect;
25
use Shopware\Connect\SDK;
26
27
class Shopware_Controllers_Backend_Connect extends \Shopware_Controllers_Backend_ExtJs implements CSRFWhitelistAware
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
28
{
29
    /**
30
     * @var ConnectFactory
31
     */
32
    private $factory;
33
34
    /**
35
     * @var Config
36
     */
37
    private $configComponent;
38
39
    /**
40
     * @var MarketplaceSettingsApplier
41
     */
42
    private $marketplaceSettingsApplier;
43
44
    /**
45
     * @var \Shopware\Connect\SDK
46
     */
47
    private $sdk;
48
49
    /**
50
     * @var SnHttpClient
51
     */
52
    private $snHttpClient;
53
54
    /**
55
     * @var ProductStreamService
56
     */
57
    private $productStreamService;
58
59 View Code Duplication
    public function preDispatch()
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...
60
    {
61
        $this->container->get('template')->addTemplateDir(
62
            Shopware_Plugins_Backend_SwagConnect_Bootstrap::PLUGIN_PATH . '/Views'
63
        );
64
        $this->container->get('snippets')->addConfigDir(
65
            Shopware_Plugins_Backend_SwagConnect_Bootstrap::PLUGIN_PATH . 'Snippets/'
66
        );
67
    }
68
69
    /**
70
     * @return ModelManager
71
     */
72
    public function getModelManager()
73
    {
74
        return $this->get('models');
75
    }
76
77
    /**
78
     * @return \Shopware\Connect\SDK
79
     */
80
    public function getSDK()
81
    {
82
        if ($this->sdk === null) {
83
            $this->sdk = $this->get('ConnectSDK');
84
        }
85
86
        return $this->sdk;
87
    }
88
89
    /**
90
     * @return \ShopwarePlugins\Connect\Components\Helper
91
     */
92
    public function getHelper()
93
    {
94
        return $this->getConnectFactory()->getHelper();
95
    }
96
97
    /**
98
     * @return ConnectFactory
99
     */
100
    public function getConnectFactory()
101
    {
102
        if ($this->factory === null) {
103
            $this->factory = new ConnectFactory();
104
        }
105
106
        return $this->factory;
107
    }
108
109
    /**
110
     * @return ArticleRepository
111
     */
112
    private function getArticleRepository()
113
    {
114
        return $this->getModelManager()->getRepository(
115
            Article::class
116
        );
117
    }
118
119
    /**
120
     * @return \Shopware\Models\Category\Repository
121
     */
122
    private function getCategoryRepository()
123
    {
124
        return $this->getModelManager()->getRepository(
125
            Category::class
126
        );
127
    }
128
129
    /**
130
     * Will return a category model for the given id. If the attribute should not exist
131
     * it will be created
132
     *
133
     * @param $id
134
     * @return null|Category
135
     */
136
    private function getCategoryModelById($id)
137
    {
138
        $categoryModel = $this->getCategoryRepository()->find($id);
139
        if (!$categoryModel || !$categoryModel->getAttribute()) {
140
            $attribute = new \Shopware\Models\Attribute\Category();
141
            $attribute->setCategory($categoryModel);
142
            $this->getModelManager()->persist($attribute);
143
            $this->getModelManager()->flush($attribute);
144
        }
145
146
        return $categoryModel;
147
    }
148
149
    /**
150
     * When the backend module is being loaded, update connect products.
151
     *
152
     * It might be considerable to move this to e.g. the lifecycle events of the products
153
     */
154
    public function indexAction()
155
    {
156
        $this->getHelper()->updateConnectProducts();
157
158
        parent::loadAction();
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (loadAction() instead of indexAction()). Are you sure this is correct? If so, you might want to change this to $this->loadAction().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
159
    }
160
161
    /**
162
     * When the backend module is being loaded, update connect products.
163
     *
164
     * It might be considerable to move this to e.g. the lifecycle events of the products
165
     */
166
    public function refreshConnectItemsAction()
167
    {
168
        $this->getHelper()->updateConnectProducts();
169
    }
170
171
    /**
172
     * If the price type is purchase or both
173
     * and shopware is 5.2 or greater
174
     * insert detailPurchasePrice in connect config table
175
     * when priceFieldForPurchasePriceExport is empty
176
     */
177
    private function updatePurchasePriceField()
178
    {
179
        $field = $this->getConfigComponent()->getConfig('priceFieldForPurchasePriceExport');
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $field is correct as $this->getConfigComponen...orPurchasePriceExport') (which targets ShopwarePlugins\Connect\...nts\Config::getConfig()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
180
        if ($field) {
181
            return;
182
        }
183
184
        if (!method_exists('Shopware\Models\Article\Detail', 'setPurchasePrice')) {
185
            return;
186
        }
187
188
        if ($this->getSDK()->getPriceType() == \Shopware\Connect\SDK::PRICE_TYPE_PURCHASE
189
            || $this->getSDK()->getPriceType() == \Shopware\Connect\SDK::PRICE_TYPE_BOTH
190
        ) {
191
            $this->getConfigComponent()->setConfig(
192
                'priceFieldForPurchasePriceExport',
193
                'detailPurchasePrice',
194
                null,
195
                'export'
196
            );
197
        }
198
    }
199
200
    /**
201
     * Helper function to return a QueryBuilder for creating the listing queries for the import and export listings
202
     *
203
     * @param $filter
204
     * @param $order
205
     * @return \Doctrine\ORM\QueryBuilder
206
     */
207
    private function getListQueryBuilder($filter, $order)
208
    {
209
        $builder = $this->getModelManager()->createQueryBuilder();
210
        $builder->from('Shopware\CustomModels\Connect\Attribute', 'at');
211
        $builder->join('at.article', 'a');
212
        $builder->join('a.mainDetail', 'd');
213
        $builder->leftJoin('d.prices', 'p', 'with', "p.from = 1 AND p.customerGroupKey = 'EK'");
214
        $builder->leftJoin('a.supplier', 's');
215
        $builder->leftJoin('a.tax', 't');
216
217
        $builder->select([
218
            'a.id',
219
            'd.number as number',
220
            'd.inStock as inStock',
221
            'a.name as name',
222
            's.name as supplier',
223
            'a.active as active',
224
            't.tax as tax',
225
            'p.price * (100 + t.tax) / 100 as price',
226
            'at.category'
227
        ]);
228
229
        // show only main variant in export/import lists
230
        $builder->groupBy('at.articleId');
231
232
        foreach ($filter as $key => $rule) {
233
            switch ($rule['property']) {
234
                case 'search':
235
                    $builder->andWhere('d.number LIKE :search OR a.name LIKE :search OR s.name LIKE :search')
236
                        ->setParameter('search', $rule['value']);
237
                    break;
238
                case 'categoryId':
239
                    $builder->join('a.categories', 'c', 'with', 'c.id = :categoryId OR c.path LIKE :categoryPath')
240
                        ->setParameter('categoryId', $rule['value'])
241
                        ->setParameter('categoryPath', '%|' . $rule['value'] . '|%');
242
                    break;
243
                case 'supplierId':
244
                    $builder->andWhere('a.supplierId = :supplierId')
245
                        ->setParameter('supplierId', $rule['value']);
246
                    break;
247
                case 'exportStatus':
248
                    $builder->andWhere('at.exportStatus LIKE :status')
249
                        ->setParameter('status', $rule['value']);
250
                    break;
251
                case 'active':
252
                    $builder->andWhere('a.active LIKE :active')
253
                        ->setParameter('active', $rule['value']);
254
                    break;
255
                default:
256
                    continue;
257
            }
258
        }
259
260
        $builder->addOrderBy($order);
261
262
        return $builder;
263
    }
264
265
    /**
266
     * Get all products exported to connect
267
     */
268
    public function getExportListAction()
269
    {
270
        $filter = (array) $this->Request()->getParam('filter', []);
271
        $order = reset($this->Request()->getParam('sort', []));
0 ignored issues
show
Bug introduced by
$this->Request()->getParam('sort', array()) cannot be passed to reset() as the parameter $array expects a reference.
Loading history...
272
273
        $criteria = new SearchCriteria([
274
            'offset' => (int) $this->Request()->getParam('start'),
275
            'limit' => (int) $this->Request()->getParam('limit'),
276
            'orderBy' => $order['property'],
277
            'orderByDirection' => $order['direction'],
278
279
        ]);
280
281
        foreach ($filter as $key => $rule) {
282
            $field = $rule['property'];
283
            $criteria->{$field} = $rule['value'];
284
        }
285
286
        $exportList = $this->getConnectExport()->getExportList($criteria);
287
288
        $this->View()->assign([
289
            'success' => true,
290
            'data' => $exportList->articles,
291
            'total' => $exportList->count,
292
        ]);
293
    }
294
295
    public function getExportStatusAction()
296
    {
297
        $attrRepo = $this->getModelManager()->getRepository('Shopware\CustomModels\Connect\Attribute');
298
299
        $syncedItems = $attrRepo->countStatus([
300
            Attribute::STATUS_SYNCED,
301
        ]);
302
303
        $totalItems = $attrRepo->countStatus([
304
            Attribute::STATUS_INSERT,
305
            Attribute::STATUS_UPDATE,
306
            Attribute::STATUS_SYNCED,
307
        ]);
308
309
        $this->View()->assign([
310
            'success' => true,
311
            'data' => $syncedItems,
312
            'total' => $totalItems,
313
        ]);
314
    }
315
316
    /**
317
     * Get all products imported from connect
318
     */
319
    public function getImportListAction()
320
    {
321
        $filter = (array) $this->Request()->getParam('filter', []);
322
        $sort = $this->Request()->getParam('sort', []);
323
324
        foreach ($sort as $key => $currentSorter) {
325
            if ($currentSorter['property'] == 'category') {
326
                unset($sort[$key]);
327
            }
328
        }
329
330
        $builder = $this->getListQueryBuilder(
331
            $filter,
332
            $sort
333
        );
334
        $builder->addSelect([
335
            'at.shopId',
336
            'at.sourceId',
337
            'at.exportStatus as connectStatus',
338
        ]);
339
        $builder->andWhere('at.shopId IS NOT NULL');
340
341
        $builder->addOrderBy('at.category', 'ASC');
342
343
        $query = $builder->getQuery();
344
345
        $query->setFirstResult($this->Request()->getParam('start'));
346
        $query->setMaxResults($this->Request()->getParam('limit'));
347
348
        $countResult = array_map('current', $builder->select(['COUNT(DISTINCT at.articleId) as current'])->orderBy('current')->getQuery()->getScalarResult());
349
        $total = array_sum($countResult);
0 ignored issues
show
Unused Code introduced by
$total 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...
350
        $total = array_sum($countResult);
351
        // todo@sb: find better solution. getQueryCount method counts s_plugin_connect_items.id like they are not grouped by article id
352
//        $total = Shopware()->Models()->getQueryCount($query);
353
        $data = $query->getArrayResult();
354
355
        $this->View()->assign([
356
            'success' => true,
357
            'data' => $data,
358
            'total' => $total
359
        ]);
360
    }
361
362
    /**
363
     * Import parts of the connect category tree to shopware
364
     */
365
    public function importConnectCategoriesAction()
366
    {
367
        $fromCategory = $this->Request()->getParam('fromCategory');
368
        $toCategory = $this->Request()->getParam('toCategory');
369
370
        $entityManager = $this->getModelManager();
371
        $helper = $this->getHelper();
0 ignored issues
show
Unused Code introduced by
$helper 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...
372
373
        // Make sure that the target category exists
374
        $toCategoryModel = $this->getCategoryRepository()->find($toCategory);
375
        if (!$toCategoryModel) {
376
            throw new \RuntimeException("Category with id  {$toCategory} not found");
377
        }
378
379
        // The user might have changed the mapping without saving and then hit the "importCategories"
380
        // button. So we save the parent category's mapping first
381
        $parentCategory = $this->getCategoryModelById($toCategory);
382
        $parentCategory->getAttribute()->setConnectImportMapping($fromCategory);
383
        $entityManager->flush();
384
385
        try {
386
            $entityManager->getConnection()->beginTransaction();
387
            $this->importConnectCategories($fromCategory, $toCategory);
388
            $entityManager->getConnection()->commit();
389
        } catch (\Exception $e) {
390
            $entityManager->getConnection()->rollback();
391
            throw new \RuntimeException('Could not import categories', 0, $e);
392
        }
393
    }
394
395
    /**
396
     * Will import a connect category tree into shopware.
397
     *
398
     * @param $fromCategory
399
     * @param $toCategory
400
     */
401
    public function importConnectCategories($fromCategory, $toCategory)
402
    {
403
        $categoriesToImport = $this->getFlatConnectCategories($fromCategory);
404
        $toCategoryModel = $this->getCategoryRepository()->find($toCategory);
405
        $entityManager = $this->getModelManager();
406
407
        /*
408
         * The import string allows to identify categories, which have already been imported for
409
         * this exact import. This does not prevent the user from importing the same sub-tree
410
         * into multiple shopware categories. But it does prevent him from importing the same sub-tree
411
         * into the same category multiple times
412
         */
413
        $importString = $fromCategory . '-' . $toCategory;
414
        $currentLevel = 1;
415
        $mappings = [];
416
417
        foreach ($categoriesToImport as $id => $category) {
418
            $name = $category['name'];
419
            $parent = $category['parent'];
420
            $level = $category['level'];
421
422
            // Only flush after the level changed - this speeds up the import
423
            if ($currentLevel != $level) {
424
                Shopware()->Models()->flush();
425
            }
426
            $currentLevel = $level;
427
428
            /** @var Category $parentModel */
429
            if (!$parent) {
430
                // Top category level - use toCategoryModel
431
                $parentModel = $toCategoryModel;
432
            } else {
433
                // Parent was created before and is referenced in $mappings
434
                $parentModel = $mappings[$parent];
435
            }
436
437
            // Check if there is already a category attribute for this import
438
            $categoryAttributes = $entityManager->getRepository('\Shopware\Models\Attribute\Category')->findBy(
439
                ['connectImported' => $importString, 'connectImportMapping' => $id],
440
                null,
441
                1
442
            );
443
444
            if (!empty($categoryAttributes)) {
445
                /** @var \Shopware\Models\Attribute\Category $categoryAttribute */
446
                $categoryAttribute = array_pop($categoryAttributes);
447
                $category = $categoryAttribute->getCategory();
448
            } else {
449
                // Create category and attribute model
450
                $category = new Category();
451
                $category->setName($name);
452
                $category->setParent($parentModel);
453
454
                $attribute = new \Shopware\Models\Attribute\Category();
455
                $attribute->setConnectImportMapping($id);
456
                $attribute->setConnectImported($importString);
457
                $category->setAttribute($attribute);
458
459
                Shopware()->Models()->persist($category);
460
                Shopware()->Models()->persist($attribute);
461
            }
462
463
464
            // Store the new category model in out $mappings array
465
            $mappings[$id] = $category;
466
        }
467
468
        Shopware()->Models()->flush();
469
    }
470
471
    public function initParamsAction()
472
    {
473
        $marketplaceIcon = $this->getConfigComponent()->getConfig('marketplaceIcon', Connect::MARKETPLACE_ICON);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $marketplaceIcon is correct as $this->getConfigComponen...nect::MARKETPLACE_ICON) (which targets ShopwarePlugins\Connect\...nts\Config::getConfig()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
474
        $marketplaceName = $this->getConfigComponent()->getConfig('marketplaceName', Connect::MARKETPLACE_NAME);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $marketplaceName is correct as $this->getConfigComponen...nect::MARKETPLACE_NAME) (which targets ShopwarePlugins\Connect\...nts\Config::getConfig()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
475
        $marketplaceNetworkUrl = $this->getConfigComponent()->getConfig('marketplaceNetworkUrl', Connect::MARKETPLACE_SOCIAL_NETWORK_URL);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $marketplaceNetworkUrl is correct as $this->getConfigComponen...ACE_SOCIAL_NETWORK_URL) (which targets ShopwarePlugins\Connect\...nts\Config::getConfig()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
476
        $defaultMarketplace = $this->getConfigComponent()->getConfig('isDefault', true);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $defaultMarketplace is correct as $this->getConfigComponen...nfig('isDefault', true) (which targets ShopwarePlugins\Connect\...nts\Config::getConfig()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
477
        $isFixedPriceAllowed = 0;
478
        $priceType = $this->getSDK()->getPriceType();
479
        if ($priceType === SDK::PRICE_TYPE_BOTH ||
480
            $priceType === SDK::PRICE_TYPE_RETAIL) {
481
            $isFixedPriceAllowed = 1;
482
        }
483
        $marketplaceIncomingIcon = ($marketplaceName == Connect::MARKETPLACE_NAME ? Connect::MARKETPLACE_GREEN_ICON : $marketplaceIcon);
484
        $marketplaceLogo = $this->getConfigComponent()->getConfig('marketplaceLogo', Connect::MARKETPLACE_LOGO);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $marketplaceLogo is correct as $this->getConfigComponen...nect::MARKETPLACE_LOGO) (which targets ShopwarePlugins\Connect\...nts\Config::getConfig()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
485
        $purchasePriceInDetail = method_exists('Shopware\Models\Article\Detail', 'setPurchasePrice') ? 1 : 0;
486
487
        $this->View()->assign([
488
            'success' => true,
489
            'data' => [
490
                'marketplaceName' => $marketplaceName,
491
                'marketplaceNetworkUrl' => $marketplaceNetworkUrl,
492
                'marketplaceIcon' => $marketplaceIcon,
493
                'defaultMarketplace' => $defaultMarketplace,
494
                'isFixedPriceAllowed' => $isFixedPriceAllowed,
495
                'marketplaceIncomingIcon' => $marketplaceIncomingIcon,
496
                'marketplaceLogo' => $marketplaceLogo,
497
                'purchasePriceInDetail' => $purchasePriceInDetail,
498
            ]
499
        ]);
500
    }
501
502
    /**
503
     * Returns a flat array of connect categories
504
     *
505
     * @param $rootCategory
506
     * @return array(
0 ignored issues
show
Documentation introduced by
The doc-type array( could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
507
     *      string => array('id' => string, 'name' => string, 'level' => int, 'parent' => string|null)
508
     * )
509
     */
510
    private function getFlatConnectCategories($rootCategory)
511
    {
512
        $sdk = $this->getSDK();
513
        $connectCategories = $sdk->getCategories();
0 ignored issues
show
Bug introduced by
The method getCategories() does not seem to exist on object<Shopware\Connect\SDK>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
514
515
        $categoriesToImport = [];
516
        foreach ($connectCategories as $id => $name) {
517
            // Skip all entries which do not start with the parent or do not have it at all
518
            if (strpos($id, $rootCategory) !== 0) {
519
                continue;
520
            }
521
522
            $level = substr_count(preg_replace("#^{$rootCategory}#", '', $id), '/');
523
524
            // Skip the root category
525
            if ($level == 0) {
526
                continue;
527
            }
528
529
            $categoriesToImport[$id] = [
530
                'id' => $id,
531
                'name' => $name,
532
                'level' => $level,
533
                'parent' => $level == 1 ? null : implode('/', array_slice(explode('/', $id), 0, -1))
534
            ];
535
        }
536
537
        // Sort the categories ascending by their level, so parent categories can be imported first
538
        uasort(
539
            $categoriesToImport,
540
            function ($a, $b) {
541
                $a = $a['level'];
542
                $b = $b['level'];
543
                if ($a == $b) {
544
                    return 0;
545
                }
546
547
                return ($a < $b) ? -1 : 1;
548
            }
549
        );
550
551
        return $categoriesToImport;
552
    }
553
554
    /**
555
     * Save a given mapping of a given category to all subcategories
556
     */
557
    public function applyMappingToChildrenAction()
558
    {
559
        $categoryId = $this->Request()->getParam('category');
560
        $mapping = $this->Request()->getParam('mapping');
561
562
        $entityManager = $this->getModelManager();
563
564
        try {
565
            $entityManager->getConnection()->beginTransaction();
566
            $this->applyMappingToChildren($mapping, $categoryId);
567
            $entityManager->getConnection()->commit();
568
            $this->View()->assign([
569
                'success' => true
570
            ]);
571
        } catch (\Exception $e) {
572
            $entityManager->getConnection()->rollback();
573
            $this->View()->assign([
574
                'message' => $e->getMessage(),
575
                'success' => false
576
            ]);
577
        }
578
    }
579
580
    /**
581
     * Returns success true if user could be logged in, or false if something went wrong
582
     *
583
     * @return array(
0 ignored issues
show
Documentation introduced by
The doc-type array( could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
584
     *      string => bool
585
     * )
586
     */
587
    public function loginAction()
588
    {
589
        /** @var \Shopware\Components\HttpClient\GuzzleHttpClient $client */
590
        $client = $this->get('http_client');
591
592
        $shopwareId = $this->Request()->getParam('shopwareId');
593
        $password = $this->Request()->getParam('password');
594
        $loginUrl = $this->getHost() . '/sdk/pluginCommunication/login';
595
596
        // Try to login into connect
597
        $response = $client->post(
598
            $loginUrl,
599
            [
600
                'content-type' => 'application/x-www-form-urlencoded'
601
            ],
602
            [
603
                'username' => urlencode($shopwareId),
604
                'password' => urlencode($password)
605
            ]
606
        );
607
608
        $responseObject = json_decode($response->getBody());
609
610
        if (!$responseObject->success) {
611
            $message = $responseObject->reason;
612
613
            if ($responseObject->reason == SDK::WRONG_CREDENTIALS_MESSAGE) {
614
                $snippets = Shopware()->Snippets()->getNamespace('backend/connect/view/main');
615
                $message = $snippets->get(
616
                    'error/wrong_credentials',
617
                    SDK::WRONG_CREDENTIALS_MESSAGE,
618
                    true
619
                );
620
            }
621
622
            $this->View()->assign([
623
                'success' => false,
624
                'message' => $message
625
            ]);
626
627
            return;
628
        }
629
630
        try {
631
            // set apiKey in Connect config table
632
            // after that create completely new SDK instance,
633
            // because correct apiKey should be used during creation
634
            $this->getConfigComponent()->setConfig('apiKey', $responseObject->apiKey, null, 'general');
635
            $sdk = $this->getConnectFactory()->createSDK();
636
            $sdk->verifySdk();
637
            $this->getConfigComponent()->setConfig('apiKeyVerified', true);
638
            $this->getConfigComponent()->setConfig('shopwareId', $shopwareId, null, 'general');
639
            $this->removeConnectMenuEntry();
640
            $marketplaceSettings = $sdk->getMarketplaceSettings();
641
            $this->getMarketplaceApplier()->apply(new MarketplaceSettings($marketplaceSettings));
642
        } catch (\Exception $e) {
643
            $this->getConfigComponent()->setConfig('apiKey', null, null, 'general');
644
645
            $this->View()->assign([
646
                'success' => false,
647
                'message' => $e->getMessage()
648
            ]);
649
650
            return;
651
        }
652
653
        $this->View()->assign([
654
            'success' => true
655
        ]);
656
    }
657
658
    /**
659
     * Returns success true if user could be logged in, or false if something went wrong
660
     *
661
     * @return array(
0 ignored issues
show
Documentation introduced by
The doc-type array( could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
662
     *      string => bool
663
     * )
664
     */
665
    public function registerAction()
666
    {
667
        /** @var \Shopware\Components\HttpClient\GuzzleHttpClient $client */
668
        $client = $this->get('http_client');
669
670
        $shopwareId = $this->Request()->getParam('shopwareID');
671
        $password = $this->Request()->getParam('password');
672
        $email = $this->Request()->getParam('email');
673
674
        // Enter the valid production url here
675
        $host = $this->getHost();
676
677
        $loginUrl = $host . '/sdk/pluginCommunication/register';
678
679
        $response = $client->post(
680
            $loginUrl,
681
            [
682
                'content-type' => 'application/x-www-form-urlencoded'
683
            ],
684
            [
685
                'username'  => urlencode($shopwareId),
686
                'password'  => urlencode($password),
687
                'email'     => urlencode($email)
688
            ]
689
        );
690
691
        $responseObject = json_decode($response->getBody());
692
693
        if (!$responseObject->success) {
694
            $this->View()->assign([
695
                'success' => false,
696
                'message' => $responseObject->reason
697
            ]);
698
699
            return;
700
        }
701
702
        try {
703
            // set apiKey in Connect config table
704
            // after that create completely new SDK instance,
705
            // because correct apiKey should be used during creation
706
            $this->getConfigComponent()->setConfig('apiKey', $responseObject->apiKey, null, 'general');
707
            $sdk = $this->getConnectFactory()->createSDK();
708
            $sdk->verifySdk();
709
            $this->getConfigComponent()->setConfig('apiKeyVerified', true);
710
            $this->getConfigComponent()->setConfig('shopwareId', $shopwareId, null, 'general');
711
            $this->removeConnectMenuEntry();
712
            $marketplaceSettings = $sdk->getMarketplaceSettings();
713
            $this->getMarketplaceApplier()->apply(new MarketplaceSettings($marketplaceSettings));
714
        } catch (\Exception $e) {
715
            $this->getConfigComponent()->setConfig('apiKey', null, null, 'general');
716
717
            $this->View()->assign([
718
                'success' => false,
719
                'message' => $e->getMessage()
720
            ]);
721
722
            return;
723
        }
724
725
        $this->View()->assign([
726
            'success' => true
727
        ]);
728
    }
729
730
    /**
731
     * Redirects and auto login to SN system
732
     */
733
    public function autoLoginAction()
734
    {
735
        return $this->redirect('http://' . $this->getHost() . '/login');
736
    }
737
738
    /**
739
     * @param bool $loggedIn
740
     * @throws Zend_Db_Adapter_Exception
741
     */
742
    private function removeConnectMenuEntry($loggedIn = true)
743
    {
744
        /** @var Enlight_Components_Db_Adapter_Pdo_Mysql $db */
745
        $db = Shopware()->Db();
746
747
        $result = $db->fetchAssoc("SELECT id, parent, pluginID FROM s_core_menu WHERE controller = 'connect' AND name = 'Register' ORDER BY id DESC LIMIT 1");
748
        if (empty($result)) {
749
            return;
750
        }
751
        $row = current($result);
752
753
        $db->exec('DELETE FROM s_core_menu WHERE id = ' . $row['id']);
754
755
        $insertSql = "INSERT INTO s_core_menu (
756
            parent,
757
            name,
758
            class,
759
            pluginID,
760
            controller,
761
            action,
762
            onclick,
763
            active
764
          ) VALUES (
765
            '#parent#',
766
            '#name#',
767
            '#class#',
768
            #pluginID#,
769
            '#controller#',
770
            '#action#',
771
            '#onclick#',
772
            1
773
          )";
774
775
        $db->exec(strtr($insertSql, [
776
            '#parent#' => $row['parent'],
777
            '#name#' => 'Import',
778
            '#class#' => 'sc-icon-import',
779
            '#pluginID#' => $row['pluginID'],
780
            '#controller#' => 'Connect',
781
            '#onclick#' => '',
782
            '#action#' => 'Import'
783
        ]));
784
785
        $db->exec(strtr($insertSql, [
786
            '#parent#' => $row['parent'],
787
            '#name#' => 'Export',
788
            '#class#' => 'sc-icon-export',
789
            '#pluginID#' => $row['pluginID'],
790
            '#controller#' => 'Connect',
791
            '#onclick#' => '',
792
            '#action#' => 'Export'
793
        ]));
794
795
        $db->exec(strtr($insertSql, [
796
            '#parent#' => $row['parent'],
797
            '#name#' => 'Settings',
798
            '#class#' => 'sprite-gear',
799
            '#pluginID#' => $row['pluginID'],
800
            '#controller#' => 'Connect',
801
            '#onclick#' => '',
802
            '#action#' => 'Settings'
803
        ]));
804
805
        $db->exec(strtr($insertSql, [
806
            '#parent#' => $row['parent'],
807
            '#name#' => 'OpenConnect',
808
            '#class#' => 'connect-icon',
809
            '#pluginID#' => $row['pluginID'],
810
            '#controller#' => 'Connect',
811
            '#onclick#' => 'window.open("connect/autoLogin")',
812
            '#action#' => 'OpenConnect'
813
        ]));
814
    }
815
816
    /**
817
     * Helper that will assign a given mapping to all children of a given category
818
     *
819
     * @param $mapping string
820
     * @param $categoryId int
821
     * @throws \Exception
822
     */
823
    private function applyMappingToChildren($mapping, $categoryId)
824
    {
825
        $helper = $this->getHelper();
0 ignored issues
show
Unused Code introduced by
$helper 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...
826
        $ids = $this->getChildCategoriesIds($categoryId);
827
        $entityManager = $this->getModelManager();
828
829
830
        if (!$categoryId) {
831
            throw new \RuntimeException("Category '{$categoryId}' not found");
832
        }
833
834
        // First of all try to save the mapping for the parent category. If that fails,
835
        // it mustn't be done for the child categories
836
        $parentCategory = $this->getCategoryModelById($categoryId);
837
        $parentCategory->getAttribute()->setConnectExportMapping($mapping);
838
        $entityManager->flush();
839
840
        // Don't set the children with models in order to speed things up
841
        $builder = $entityManager->createQueryBuilder();
842
        $builder->update('\Shopware\Models\Attribute\Category', 'categoryAttribute')
843
            ->set('categoryAttribute.connectExportMapping', $builder->expr()->literal($mapping))
844
            ->where($builder->expr()->in('categoryAttribute.categoryId', $ids));
845
846
        $builder->getQuery()->execute();
847
    }
848
849
    /**
850
     * Helper function which returns the IDs of the child categories of a given parent category
851
     *
852
     * @param $parentId int
853
     * @return array
854
     */
855
    private function getChildCategoriesIds($parentId)
856
    {
857
        $query = $this->getModelManager()->createQuery('SELECT c.id from Shopware\Models\Category\Category c WHERE c.path LIKE ?1 ');
858
        $query->setParameter(1, ["%|{$parentId}|%"]);
859
        $result = $query->getResult(\Doctrine\ORM\AbstractQuery::HYDRATE_ARRAY);
860
861
        // Pop IDs from result rows
862
        return array_map(
863
            function ($row) {
864
                return array_pop($row);
865
            },
866
            $result
867
        );
868
    }
869
870
    /**
871
     * Collect all source ids by given article ids
872
     */
873
    public function getArticleSourceIdsAction()
874
    {
875
        try {
876
            $exportAll = (bool) $this->Request()->getPost('exportAll', false);
877
            $articleIds = $this->Request()->getPost('ids', []);
878
879
            if ($exportAll) {
880
                $articleIds = $this->getHelper()->getAllNonConnectArticleIds();
881
            }
882
883
            if (!is_array($articleIds)) {
884
                $articleIds = [$articleIds];
885
            }
886
887
            $sourceIds = $this->getHelper()->getArticleSourceIds($articleIds);
888
889
            $this->View()->assign([
890
                'success' => true,
891
                'sourceIds' => $sourceIds
892
            ]);
893
        } catch (\Exception $e) {
894
            $this->View()->assign([
895
                'success' => false,
896
                'message' => $e->getMessage()
897
            ]);
898
        }
899
    }
900
901
    /**
902
     * Called when a product variants were marked for update in the connect backend module
903
     */
904
    public function insertOrUpdateProductAction()
905
    {
906
        // if priceType comes from SN and shopware version is 5.2
907
        // priceFieldForPurchasePriceExport is empty
908
        // we need to set it because there isn't customer groups
909
        // purchasePrice is stored always in article detail
910
        $this->updatePurchasePriceField();
911
        $sourceIds = $this->Request()->getPost('sourceIds');
912
        $connectExport = $this->getConnectExport();
913
914
        try {
915
            $errors = $connectExport->export($sourceIds);
916
        } catch (\RuntimeException $e) {
917
            $this->View()->assign([
918
                'success' => false,
919
                'messages' => [ErrorHandler::TYPE_DEFAULT_ERROR => [$e->getMessage()]]
920
            ]);
921
922
            return;
923
        }
924
925
        if (!empty($errors)) {
926
            $this->View()->assign([
927
                'success' => false,
928
                'messages' => $errors
929
            ]);
930
931
            return;
932
        }
933
934
        $this->View()->assign([
935
            'success' => true
936
        ]);
937
    }
938
939
    /**
940
     * Delete a product from connect export
941
     */
942
    public function deleteProductAction()
943
    {
944
        $sdk = $this->getSDK();
945
        $ids = $this->Request()->getPost('ids');
946
        foreach ($ids as $id) {
947
            /** @var \Shopware\Models\Article\Article $model */
948
            $model = $this->getConnectExport()->getArticleModelById($id);
949
            if ($model === null) {
950
                continue;
951
            }
952
            /** @var \Shopware\Models\Article\Detail $detail */
953
            foreach ($model->getDetails() as $detail) {
954
                $attribute = $this->getHelper()->getConnectAttributeByModel($detail);
955
                $sdk->recordDelete($attribute->getSourceId());
956
                $attribute->setExportStatus(Attribute::STATUS_DELETE);
957
                $attribute->setExported(false);
958
            }
959
        }
960
        Shopware()->Models()->flush();
961
    }
962
963
    /**
964
     * Verify a given api key against the connect server
965
     */
966
    public function verifyApiKeyAction()
967
    {
968
        $sdk = $this->getSDK();
969
        try {
970
            $key = $this->Request()->getPost('apiKey');
971
            $sdk->verifyKey($key);
972
            $this->View()->assign([
973
                'success' => true
974
            ]);
975
            $this->getConfigComponent()->setConfig('apiKeyVerified', true);
976
            $marketplaceSettings = $sdk->getMarketplaceSettings();
977
            $this->getMarketplaceApplier()->apply(new MarketplaceSettings($marketplaceSettings));
978
        } catch (\Exception $e) {
979
            $this->View()->assign([
980
                'message' => $e->getMessage(),
981
                'success' => false
982
            ]);
983
            $this->getConfigComponent()->setConfig('apiKeyVerified', false);
984
        }
985
986
        $this->getModelManager()->flush();
987
    }
988
989
    /**
990
     * Returns the connectAttribute data for a given articleId
991
     *
992
     * @throws RuntimeException
993
     * @throws Exception
994
     */
995
    public function getConnectDataAction()
996
    {
997
        $articleId = $this->Request()->getParam('articleId');
998
999
        if (!$articleId) {
1000
            throw new \Exception('Connect: ArticleId empty');
1001
        }
1002
1003
        /** @var Article $articleModel */
1004
        $articleModel = $this->getArticleRepository()->find($articleId);
1005
1006
        if (!$articleModel) {
1007
            throw new \RuntimeException("Could not find model for article with id {$articleId}");
1008
        }
1009
1010
        $data = $this->getHelper()->getOrCreateConnectAttributes($articleModel);
1011
1012
        $data = $this->getModelManager()->toArray($data);
1013
        if (isset($data['articleId'])) {
1014
            $data = [$data];
1015
        }
1016
1017
        $this->View()->assign([
1018
            'success' => true,
1019
            'data' => $data
1020
        ]);
1021
    }
1022
1023
    /**
1024
     * Save a given connect attribute
1025
     */
1026
    public function saveConnectAttributeAction()
1027
    {
1028
        $data = $this->Request()->getParams();
1029
1030
        /** @var \Shopware\CustomModels\Connect\Attribute $connectAttribute */
1031
        $connectAttribute = $this->getModelManager()->find('Shopware\CustomModels\Connect\Attribute', $data['id']);
1032
        if (!$connectAttribute) {
1033
            throw new \RuntimeException("Could not find connect attribute with id {$data['id']}");
1034
        }
1035
1036
        /** @var \Shopware\Models\Article\Detail $detail */
1037
        foreach ($connectAttribute->getArticle()->getDetails() as $detail) {
1038
            $connectAttribute = $this->getHelper()->getConnectAttributeByModel($detail);
1039
            // Only allow changes in the fixedPrice field if this is a local product
1040
            if (!$connectAttribute->getShopId()) {
1041
                $connectAttribute->setFixedPrice($data['fixedPrice']);
1042
            }
1043
            // Save the update fields
1044
            foreach ($data as $key => $value) {
1045
                if (strpos($key, 'update') === 0) {
1046
                    $setter = 'set' . ucfirst($key);
1047
                    $connectAttribute->$setter($value);
1048
                }
1049
            }
1050
            $this->getModelManager()->persist($connectAttribute);
1051
        }
1052
1053
        $this->getModelManager()->flush();
1054
1055
        $this->View()->assign(['success' => true]);
1056
    }
1057
1058
    /**
1059
     * Saves the changed "connectAllowed" attribute. Saving this attribute should be done
1060
     * by the shipping-module on its own, right now (as of SW 4.2.0) it does not do so.
1061
     *
1062
     * todo: Once the shipping module is fixed, increase the required version of this plugin
1063
     * and remove this and the unnecessary ExtJS extensions
1064
     */
1065
    public function saveShippingAttributeAction()
1066
    {
1067
        $shippingId = $this->Request()->getParam('shippingId');
1068
        $connectAllowed = $this->Request()->getParam('connectAllowed', true);
1069
1070
        if (!$shippingId) {
1071
            return;
1072
        }
1073
1074
        $shippingRepo = $this->getModelManager()->getRepository('\Shopware\Models\Dispatch\Dispatch');
1075
        /** @var \Shopware\Models\Dispatch\Dispatch $shipping */
1076
        $shipping = $shippingRepo->find($shippingId);
1077
1078
        if (!$shipping) {
1079
            return;
1080
        }
1081
1082
        $attribute = $shipping->getAttribute();
1083
1084
        if (!$attribute) {
1085
            $attribute = new \Shopware\Models\Attribute\Dispatch();
1086
            $attribute->setDispatch($shipping);
1087
            $shipping->setAttribute($attribute);
1088
            $this->getModelManager()->persist($attribute);
1089
        }
1090
1091
        $attribute->setConnectAllowed($connectAllowed);
1092
1093
        $this->getModelManager()->flush();
1094
1095
        $this->View()->assign('success', true);
1096
    }
1097
1098
    /**
1099
     * Lists all logs
1100
     */
1101
    public function getLogsAction()
1102
    {
1103
        $params = $this->Request()->getParams();
1104
        $order = $this->Request()->getParam('sort', [['property' => 'time', 'direction' => 'DESC']]);
1105
        $filters = $this->Request()->getParam('filter');
1106
1107
        $commandFilters = [];
1108
        foreach ($params as $key => $param) {
1109
            if (strpos($key, 'commandFilter_') !== false && $param == 'true') {
1110
                $commandFilters[] = str_replace('commandFilter_', '', $key);
1111
            }
1112
        }
1113
1114
        if (empty($commandFilters)) {
1115
            return;
1116
        }
1117
1118
        foreach ($order as &$rule) {
1119
            if ($rule['property'] == 'time') {
1120
                $rule['property'] = 'id';
1121
            }
1122
            $rule['property'] = 'logs.' . $rule['property'];
1123
        }
1124
1125
        $builder = $this->getModelManager()->createQueryBuilder();
1126
        $builder->select('logs');
1127
        $builder->from('Shopware\CustomModels\Connect\Log', 'logs')
1128
            ->addOrderBy($order)
1129
            ->where('logs.command IN (:commandFilter)')
1130
            ->setParameter('commandFilter', $commandFilters);
1131
1132
        foreach ($filters as $filter) {
1133
            switch ($filter['property']) {
1134
                case 'search':
1135
                    $builder->andWhere(
1136
                        'logs.request LIKE :search OR logs.response LIKE :search'
1137
                    );
1138
                    $builder->setParameter('search', $filter['value']);
1139
                    break;
1140
            }
1141
        }
1142
1143
        switch ($this->Request()->getParam('errorFilter', -1)) {
1144
            case 0:
1145
                $builder->andWhere('logs.isError = 1');
1146
                break;
1147
            case 1:
1148
                $builder->andWhere('logs.isError = 0');
1149
                break;
1150
        }
1151
1152
        $query = $builder->getQuery()
1153
            ->setFirstResult($this->Request()->getParam('start', 0))
1154
            ->setMaxResults($this->Request()->getParam('limit', 25));
1155
1156
        $total = Shopware()->Models()->getQueryCount($query);
1157
        $data = $query->getArrayResult();
1158
1159
        $this->View()->assign([
1160
            'success' => true,
1161
            'data' => $data,
1162
            'total' => $total
1163
        ]);
1164
    }
1165
1166
    /**
1167
     * Get a list of log commands
1168
     */
1169
    public function getLogCommandsAction()
1170
    {
1171
        $data = $this->getModelManager()->getConnection()->fetchAll(
1172
            'SELECT DISTINCT `command` FROM `s_plugin_connect_log`'
1173
        );
1174
1175
        $data = array_map(function ($column) {
1176
            return $column['command'];
1177
        }, $data);
1178
1179
        // Enforce these fields
1180
        foreach (['fromShop', 'toShop', 'getLastRevision', 'update', 'checkProducts', 'buy', 'reserveProducts', 'confirm'] as $value) {
1181
            if (!in_array($value, $data)) {
1182
                $data[] = $value;
1183
            }
1184
        }
1185
1186
        $this->View()->assign([
1187
            'success' => true,
1188
            'data' => $data
1189
        ]);
1190
    }
1191
1192
    /**
1193
     * Delete all log entries
1194
     */
1195
    public function clearLogAction()
1196
    {
1197
        $connection = $this->getModelManager()->getConnection();
1198
        $connection->exec('TRUNCATE `s_plugin_connect_log`;');
1199
    }
1200
1201
    /**
1202
     * @return ConnectExport
1203
     */
1204
    public function getConnectExport()
1205
    {
1206
        return new ConnectExport(
1207
            $this->getHelper(),
1208
            $this->getSDK(),
1209
            $this->getModelManager(),
1210
            new ProductsAttributesValidator(),
1211
            $this->getConfigComponent(),
1212
            new ErrorHandler(),
1213
            $this->container->get('events')
1214
        );
1215
    }
1216
1217
    /**
1218
     * @return Config
1219
     */
1220
    public function getConfigComponent()
1221
    {
1222
        if ($this->configComponent === null) {
1223
            $this->configComponent = new Config($this->getModelManager());
1224
        }
1225
1226
        return $this->configComponent;
1227
    }
1228
1229
    /**
1230
     * Saves the dynamic streams to the db.
1231
     * Cronjob will looks for those streams to process them
1232
     */
1233
    public function prepareDynamicStreamsAction()
1234
    {
1235
        $streamIds = $this->Request()->getParam('streamIds', []);
1236
1237
        try {
1238
            $streamService = $this->getProductStreamService();
1239
            $streams = $streamService->findStreams($streamIds);
1240
1241
            if (!$streams) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $streams of type Shopware\Models\ProductStream\ProductStream[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1242
                $message = Shopware()->Snippets()->getNamespace('backend/connect/view/main')->get(
1243
                    'export/message/error_no_stream_selected',
1244
                    'No streams were selected',
1245
                    true
1246
                );
1247
                throw new \Exception($message);
1248
            }
1249
1250
            $modelManager = $this->getModelManager();
1251
1252
            foreach ($streams as $stream) {
1253
                $streamAttr = $streamService->createStreamAttribute($stream);
1254
1255
                if (!$streamAttr->getExportStatus()) {
1256
                    $streamAttr->setExportStatus(ProductStreamService::STATUS_PENDING);
1257
                }
1258
1259
                $modelManager->persist($streamAttr);
1260
            }
1261
1262
            $modelManager->flush();
1263
        } catch (\Exception $e) {
1264
            $this->View()->assign([
1265
                'success' => false,
1266
                'message' => $e->getMessage()
1267
            ]);
1268
        }
1269
1270
        $this->View()->assign([
1271
            'success' => true,
1272
        ]);
1273
    }
1274
1275
    /**
1276
     * Add given category to products
1277
     */
1278
    public function assignProductsToCategoryAction()
1279
    {
1280
        $articleIds = $this->Request()->getParam('ids');
1281
        $categoryId = (int) $this->Request()->getParam('category');
1282
1283
        /** @var Category $category */
1284
        $category = $this->getCategoryModelById($categoryId);
1285
        if (!is_null($category)) {
1286
            foreach ($articleIds as $id) {
1287
                /** @var \Shopware\Models\Article\Article $article */
1288
                $article = $this->getConnectExport()->getArticleModelById($id);
1289
                if (is_null($article)) {
1290
                    continue;
1291
                }
1292
                $article->addCategory($category);
1293
                $this->getModelManager()->persist($article);
1294
            }
1295
            $this->getModelManager()->flush();
1296
        }
1297
1298
        $this->View()->assign(
1299
            [
1300
                'success' => true
1301
            ]
1302
        );
1303
    }
1304
1305
    public function getStreamListAction()
1306
    {
1307
        $productStreamService = $this->getProductStreamService();
1308
1309
        $result = $productStreamService->getList(
1310
            $this->Request()->getParam('start', 0),
1311
            $this->Request()->getParam('limit', 20)
1312
        );
1313
1314
        $this->View()->assign(
1315
            [
1316
                'success' => true,
1317
                'data' => $result['data'],
1318
                'total' => $result['total'],
1319
            ]
1320
        );
1321
    }
1322
1323
    public function getStreamProductsCountAction()
1324
    {
1325
        $streamId = $this->request->getParam('id', null);
1326
1327
        if ($streamId === null) {
1328
            $this->View()->assign([
1329
                'success' => false,
1330
                'message' => 'No stream selected'
1331
            ]);
1332
        }
1333
1334
        $streamsAssignments = $this->getStreamAssignments($streamId);
1335
1336
        if (!$streamsAssignments) {
1337
            return;
1338
        }
1339
1340
        $sourceIds = $this->getHelper()->getArticleSourceIds($streamsAssignments->getArticleIds());
1341
1342
        $this->View()->assign([
1343
            'success' => true,
1344
            'sourceIds' => $sourceIds
1345
        ]);
1346
    }
1347
1348
    public function exportStreamAction()
1349
    {
1350
        $streamIds = $this->request->getParam('streamIds', []);
1351
        $currentStreamIndex = $this->request->getParam('currentStreamIndex', 0);
1352
        $offset = $this->request->getParam('offset', 0);
1353
        $limit = $this->request->getParam('limit', 1);
1354
        $articleDetailIds = $this->request->getParam('articleDetailIds', []);
1355
1356
        $streamId = $streamIds[$currentStreamIndex];
1357
1358
        $productStreamService = $this->getProductStreamService();
1359
1360
        $streamsAssignments = $this->getStreamAssignments($streamId);
1361
1362
        if (!$streamsAssignments) {
1363
            return;
1364
        }
1365
1366
        $sourceIds = $this->getHelper()->getArticleSourceIds($streamsAssignments->getArticleIds());
1367
        $sliced = array_slice($sourceIds, $offset, $limit);
1368
1369
        $exported = $this->exportStreamProducts($streamId, $sliced, $streamsAssignments);
1370
1371
        if (!$exported) {
1372
            return;
1373
        }
1374
1375
        $nextStreamIndex = $currentStreamIndex;
1376
        $newArticleDetailIds = $articleDetailIds;
1377
        $newOffset = $offset + $limit;
1378
        $hasMoreIterations = true;
1379
1380
        $processedStreams = $currentStreamIndex;
1381
1382
        //In this case all the products from a single stream were exported successfully but there are still more streams to be processed.
1383
        if ($newOffset > count($articleDetailIds) && $currentStreamIndex + 1 <= (count($streamIds) - 1)) {
1384
            $productStreamService->changeStatus($streamId, ProductStreamService::STATUS_EXPORT);
1385
            $productStreamService->log($streamId, 'Success');
1386
            $nextStreamIndex = $currentStreamIndex + 1;
1387
1388
            $streamsAssignments = $this->getStreamAssignments($streamIds[$nextStreamIndex]);
1389
1390
            if (!$streamsAssignments) {
1391
                return;
1392
            }
1393
1394
            $sourceIds = $this->getHelper()->getArticleSourceIds($streamsAssignments->getArticleIds());
1395
            $newArticleDetailIds = $sourceIds;
1396
            $newOffset = 0;
1397
        }
1398
1399
        //In this case all the products from all streams were exported successfully.
1400
        if ($newOffset > count($articleDetailIds) && $currentStreamIndex + 1 > (count($streamIds) - 1)) {
1401
            $productStreamService->changeStatus($streamId, ProductStreamService::STATUS_EXPORT);
1402
            $hasMoreIterations = false;
1403
            $newOffset = count($articleDetailIds);
1404
            $processedStreams = count($streamIds);
1405
        }
1406
1407
1408
        $this->View()->assign([
1409
            'success' => true,
1410
            'articleDetailIds' => $newArticleDetailIds,
1411
            'nextStreamIndex' => $nextStreamIndex,
1412
            'newOffset' => $newOffset,
1413
            'hasMoreIterations' => $hasMoreIterations,
1414
            'processedStreams' => $processedStreams,
1415
        ]);
1416
    }
1417
1418
    public function hasManyVariantsAction()
1419
    {
1420
        $streamId = $this->request->getParam('streamId');
1421
        $articleId = $this->request->getParam('articleId');
1422
1423
        if (!$this->getProductStreamService()->isStreamExported($streamId)) {
1424
            return;
1425
        }
1426
1427
        $sourceIds = $this->getHelper()->getSourceIdsFromArticleId($articleId);
1428
1429
        $hasManyVariants = false;
1430
1431
        if (count($sourceIds) > ProductStreamService::PRODUCT_LIMIT) {
1432
            $hasManyVariants = true;
1433
        }
1434
1435
        $this->View()->assign([
1436
            'success' => true,
1437
            'hasManyVariants' => $hasManyVariants
1438
        ]);
1439
    }
1440
1441
    public function exportAllWithCronAction()
1442
    {
1443
        try {
1444
            $db = Shopware()->Db();
1445
            $this->getConfigComponent()->setConfig('autoUpdateProducts', 2, null, 'export');
1446
1447
            $db->update(
1448
                's_crontab',
1449
                ['active' => 1],
1450
                "action = 'ShopwareConnectUpdateProducts' OR action = 'Shopware_CronJob_ShopwareConnectUpdateProducts'"
1451
            );
1452
1453
            $db->update(
1454
                's_plugin_connect_items',
1455
                ['cron_update' => 1, 'export_status' => Attribute::STATUS_UPDATE],
1456
                'shop_id IS NULL'
1457
            );
1458
1459
            $this->View()->assign([
1460
                'success' => true,
1461
            ]);
1462
        } catch (\Exception $e) {
1463
            $this->View()->assign([
1464
                'success' => false,
1465
                'message' => $e->getMessage()
1466
            ]);
1467
        }
1468
    }
1469
1470
    private function getStreamAssignments($streamId)
1471
    {
1472
        $productStreamService = $this->getProductStreamService();
1473
1474
        try {
1475
            $streamsAssignments = $productStreamService->prepareStreamsAssignments($streamId);
1476
        } catch (\Exception $e) {
1477
            $this->View()->assign([
1478
                'success' => false,
1479
                'messages' => [ErrorHandler::TYPE_DEFAULT_ERROR => [$e->getMessage()]]
1480
            ]);
1481
1482
            return false;
1483
        }
1484
1485
        return $streamsAssignments;
1486
    }
1487
1488
    private function exportStreamProducts($streamId, $sourceIds, $streamsAssignments)
1489
    {
1490
        $productStreamService = $this->getProductStreamService();
1491
        $connectExport = $this->getConnectExport();
1492
1493
        try {
1494
            $errorMessages = $connectExport->export($sourceIds, $streamsAssignments);
1495
        } catch (\RuntimeException $e) {
1496
            $productStreamService->changeStatus($streamId, ProductStreamService::STATUS_ERROR);
1497
            $this->View()->assign([
1498
                'success' => false,
1499
                'messages' => [ErrorHandler::TYPE_DEFAULT_ERROR => [$e->getMessage()]]
1500
            ]);
1501
1502
            return false;
1503
        }
1504
1505
        if (!empty($errorMessages)) {
1506
            $productStreamService->changeStatus($streamId, ProductStreamService::STATUS_ERROR);
1507
1508
            $errorMessagesText = '';
1509
            $displayedErrorTypes = [
1510
                ErrorHandler::TYPE_DEFAULT_ERROR,
1511
                ErrorHandler::TYPE_PRICE_ERROR
1512
            ];
1513
1514
            foreach ($displayedErrorTypes as $displayedErrorType) {
1515
                $errorMessagesText .= implode('\n', $errorMessages[$displayedErrorType]);
1516
            }
1517
1518
            $productStreamService->log($streamId, $errorMessagesText);
1519
1520
            $this->View()->assign([
1521
                'success' => false,
1522
                'messages' => $errorMessages
1523
            ]);
1524
1525
            return false;
1526
        }
1527
1528
        return true;
1529
    }
1530
1531
    /**
1532
     * Deletes products from connect stream export
1533
     */
1534
    public function removeStreamsAction()
1535
    {
1536
        $streamIds = $this->request->getParam('ids', []);
1537
1538
        $productStreamService = $this->getProductStreamService();
1539
        $connectExport = $this->getConnectExport();
1540
1541
        $filteredStreamIds = $productStreamService->filterExportedStreams($streamIds);
1542
1543
        foreach ($filteredStreamIds as $streamId) {
1544
            try {
1545
                $removedRecords = [];
1546
1547
                $assignments = $productStreamService->getStreamAssignments($streamId);
1548
                $sourceIds = $this->getHelper()->getArticleSourceIds($assignments->getArticleIds());
1549
                $items = $connectExport->fetchConnectItems($sourceIds, false);
1550
1551 View Code Duplication
                foreach ($items as $item) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1552
                    if ($productStreamService->allowToRemove($assignments, $streamId, $item['articleId'])) {
1553
                        $this->getSDK()->recordDelete($item['sourceId']);
1554
                        $removedRecords[] = $item['sourceId'];
1555
                    } else {
1556
                        //updates items with the new streams
1557
                        $streamCollection = $assignments->getStreamsByArticleId($item['articleId']);
1558
                        if (!$this->getHelper()->isMainVariant($item['sourceId']) || !$streamCollection) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $streamCollection of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1559
                            continue;
1560
                        }
1561
1562
                        //removes current stream from the collection
1563
                        unset($streamCollection[$streamId]);
1564
1565
                        $this->getSDK()->recordStreamAssignment(
1566
                            $item['sourceId'],
1567
                            $streamCollection,
1568
                            $item['groupId']
1569
                        );
1570
                    }
1571
                }
1572
1573
                $connectExport->updateConnectItemsStatus($removedRecords, Attribute::STATUS_DELETE);
1574
                $this->getSDK()->recordStreamDelete($streamId);
1575
                $productStreamService->changeStatus($streamId, ProductStreamService::STATUS_DELETE);
1576
            } catch (\Exception $e) {
1577
                $productStreamService->changeStatus($streamId, ProductStreamService::STATUS_ERROR);
1578
                $this->View()->assign([
1579
                    'success' => false,
1580
                    'message' => $e->getMessage()
1581
                ]);
1582
1583
                return;
1584
            }
1585
        }
1586
    }
1587
1588 View Code Duplication
    private function getMarketplaceApplier()
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...
1589
    {
1590
        if (!$this->marketplaceSettingsApplier) {
1591
            $this->marketplaceSettingsApplier = new MarketplaceSettingsApplier(
1592
                $this->getConfigComponent(),
1593
                Shopware()->Models(),
1594
                Shopware()->Db()
1595
            );
1596
        }
1597
1598
        return $this->marketplaceSettingsApplier;
1599
    }
1600
1601
    /**
1602
     * @return SnHttpClient
1603
     */
1604 View Code Duplication
    protected function getSnHttpClient()
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...
1605
    {
1606
        if (!$this->snHttpClient) {
1607
            $this->snHttpClient = new SnHttpClient(
1608
                $this->get('http_client'),
1609
                new \Shopware\Connect\Gateway\PDO(Shopware()->Db()->getConnection()),
1610
                $this->getConfigComponent()
1611
            );
1612
        }
1613
1614
        return $this->snHttpClient;
1615
    }
1616
1617
    /**
1618
     * @return string
1619
     */
1620
    protected function getHost()
1621
    {
1622
        $host = $this->getConfigComponent()->getConfig('connectDebugHost');
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $host is correct as $this->getConfigComponen...fig('connectDebugHost') (which targets ShopwarePlugins\Connect\...nts\Config::getConfig()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
1623
        if (!$host || $host == '') {
1624
            $host = $this->getConfigComponent()->getMarketplaceUrl();
1625
        }
1626
1627
        return $host;
1628
    }
1629
1630
    /**
1631
     * @return ProductStreamService
1632
     */
1633
    protected function getProductStreamService()
1634
    {
1635
        if ($this->productStreamService === null) {
1636
            $this->productStreamService = $this->get('swagconnect.product_stream_service');
1637
        }
1638
1639
        return $this->productStreamService;
1640
    }
1641
1642
    /**
1643
     * {@inheritdoc}
1644
     */
1645
    public function getWhitelistedCSRFActions()
1646
    {
1647
        return ['login', 'autoLogin'];
1648
    }
1649
}
1650