Completed
Push — master ( 6dd24f...103ed1 )
by Jonas
16s queued 11s
created

userHasPermissions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 8
rs 10
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
use Shopware\Components\CSRFWhitelistAware;
8
use Shopware\Components\Model\ModelManager;
9
use Shopware\CustomModels\Connect\Attribute;
10
use Shopware\Models\Article\Repository as ArticleRepository;
11
use Shopware\Models\Category\Category;
12
use ShopwarePlugins\Connect\Components\Config;
13
use ShopwarePlugins\Connect\Components\ConnectExport;
14
use ShopwarePlugins\Connect\Components\ConnectFactory;
15
use ShopwarePlugins\Connect\Components\ErrorHandler;
16
use Shopware\Models\Article\Article;
17
use ShopwarePlugins\Connect\Components\Validator\ProductAttributesValidator\ProductsAttributesValidator;
18
use ShopwarePlugins\Connect\Components\Marketplace\MarketplaceSettingsApplier;
19
use ShopwarePlugins\Connect\Components\Marketplace\MarketplaceSettings;
20
use ShopwarePlugins\Connect\Components\ProductStream\ProductStreamService;
21
use ShopwarePlugins\Connect\Components\SnHttpClient;
22
use ShopwarePlugins\Connect\Struct\SearchCriteria;
23
use ShopwarePlugins\Connect\Subscribers\Connect;
24
use Shopware\Connect\SDK;
25
use ShopwarePlugins\Connect\Components\ConfigFactory;
26
use ShopwarePlugins\Connect\Components\ProductStream\ProductStreamsAssignments;
27
28
class Shopware_Controllers_Backend_Connect extends \Shopware_Controllers_Backend_ExtJs implements CSRFWhitelistAware
29
{
30
    /**
31
     * @var ConnectFactory
32
     */
33
    private $factory;
34
35
    /**
36
     * @var Config
37
     */
38
    private $configComponent;
39
40
    /**
41
     * @var MarketplaceSettingsApplier
42
     */
43
    private $marketplaceSettingsApplier;
44
45
    /**
46
     * @var \Shopware\Connect\SDK
47
     */
48
    private $sdk;
49
50
    /**
51
     * @var SnHttpClient
52
     */
53
    private $snHttpClient;
54
55
    /**
56
     * @var ProductStreamService
57
     */
58
    private $productStreamService;
59
60
    /**
61
     * @var \ShopwarePlugins\Connect\Services\ExportAssignmentService
62
     */
63
    private $exportAssignmentService;
64
65
    /**
66
     * @return ModelManager
67
     */
68
    public function getModelManager()
69
    {
70
        return $this->get('models');
71
    }
72
73
    /**
74
     * @return \Shopware\Connect\SDK
75
     */
76
    public function getSDK()
77
    {
78
        if ($this->sdk === null) {
79
            $this->sdk = $this->get('ConnectSDK');
80
        }
81
82
        return $this->sdk;
83
    }
84
85
    /**
86
     * @return \ShopwarePlugins\Connect\Services\ExportAssignmentService
87
     */
88
    public function getExportAssignmentService()
89
    {
90
        if ($this->exportAssignmentService === null) {
91
            $this->exportAssignmentService = $this->get('swagconnect.export_assignment_service');
92
        }
93
94
        return $this->exportAssignmentService;
95
    }
96
97
    /**
98
     * @return \ShopwarePlugins\Connect\Components\Helper
99
     */
100
    public function getHelper()
101
    {
102
        return $this->getConnectFactory()->getHelper();
103
    }
104
105
    /**
106
     * @return ConnectFactory
107
     */
108
    public function getConnectFactory()
109
    {
110
        if ($this->factory === null) {
111
            $this->factory = new ConnectFactory();
112
        }
113
114
        return $this->factory;
115
    }
116
117
    /**
118
     * @return ArticleRepository
119
     */
120
    private function getArticleRepository()
121
    {
122
        return $this->getModelManager()->getRepository(
123
            Article::class
124
        );
125
    }
126
127
    /**
128
     * @return \Shopware\Models\Category\Repository
129
     */
130
    private function getCategoryRepository()
131
    {
132
        return $this->getModelManager()->getRepository(
133
            Category::class
134
        );
135
    }
136
137
    /**
138
     * Will return a category model for the given id. If the attribute should not exist
139
     * it will be created
140
     *
141
     * @param $id
142
     * @return null|Category
143
     */
144
    private function getCategoryModelById($id)
145
    {
146
        $categoryModel = $this->getCategoryRepository()->find($id);
147
        if (!$categoryModel || !$categoryModel->getAttribute()) {
148
            $attribute = new \Shopware\Models\Attribute\Category();
149
            $attribute->setCategory($categoryModel);
150
            $this->getModelManager()->persist($attribute);
151
            $this->getModelManager()->flush($attribute);
152
        }
153
154
        return $categoryModel;
155
    }
156
157
    /**
158
     * When the backend module is being loaded, update connect products.
159
     *
160
     * It might be considerable to move this to e.g. the lifecycle events of the products
161
     */
162
    public function indexAction()
163
    {
164
        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...
165
    }
166
167
    /**
168
     * When the backend module is being loaded, update connect products.
169
     *
170
     * It might be considerable to move this to e.g. the lifecycle events of the products
171
     */
172
    public function refreshConnectItemsAction()
173
    {
174
        $this->getHelper()->updateConnectProducts();
175
    }
176
177
    /**
178
     * If the price type is purchase or both
179
     * and shopware is 5.2 or greater
180
     * insert detailPurchasePrice in connect config table
181
     * when priceFieldForPurchasePriceExport is empty
182
     */
183
    private function updatePurchasePriceField()
184
    {
185
        $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...
186
        if ($field) {
187
            return;
188
        }
189
190
        if (!method_exists('Shopware\Models\Article\Detail', 'setPurchasePrice')) {
191
            return;
192
        }
193
194
        if ($this->getSDK()->getPriceType() == \Shopware\Connect\SDK::PRICE_TYPE_PURCHASE
195
            || $this->getSDK()->getPriceType() == \Shopware\Connect\SDK::PRICE_TYPE_BOTH
196
        ) {
197
            $this->getConfigComponent()->setConfig(
198
                'priceFieldForPurchasePriceExport',
199
                'detailPurchasePrice',
200
                null,
201
                'export'
202
            );
203
        }
204
    }
205
206
    /**
207
     * Helper function to return a QueryBuilder for creating the listing queries for the import and export listings
208
     *
209
     * @param $filter
210
     * @param $order
211
     * @return \Doctrine\ORM\QueryBuilder
212
     */
213
    private function getListQueryBuilder($filter, $order)
214
    {
215
        $builder = $this->getModelManager()->createQueryBuilder();
216
        $builder->from('Shopware\CustomModels\Connect\Attribute', 'at');
217
        $builder->join('at.article', 'a');
218
        $builder->join('a.mainDetail', 'd');
219
        $builder->leftJoin('d.prices', 'p', 'with', "p.from = 1 AND p.customerGroupKey = 'EK'");
220
        $builder->leftJoin('a.supplier', 's');
221
        $builder->leftJoin('a.tax', 't');
222
223
        $builder->select([
224
            'a.id',
225
            'd.number as number',
226
            'd.inStock as inStock',
227
            'a.name as name',
228
            's.name as supplier',
229
            'a.active as active',
230
            't.tax as tax',
231
            'p.price * (100 + t.tax) / 100 as price',
232
            'at.category'
233
        ]);
234
235
        // show only main variant in export/import lists
236
        $builder->groupBy('at.articleId');
237
238
        foreach ($filter as $key => $rule) {
239
            switch ($rule['property']) {
240
                case 'search':
241
                    $builder->andWhere('d.number LIKE :search OR a.name LIKE :search OR s.name LIKE :search')
242
                        ->setParameter('search', $rule['value']);
243
                    break;
244
                case 'categoryId':
245
                    $builder->join('a.categories', 'c', 'with', 'c.id = :categoryId OR c.path LIKE :categoryPath')
246
                        ->setParameter('categoryId', $rule['value'])
247
                        ->setParameter('categoryPath', '%|' . $rule['value'] . '|%');
248
                    break;
249
                case 'supplierId':
250
                    $builder->andWhere('a.supplierId = :supplierId')
251
                        ->setParameter('supplierId', $rule['value']);
252
                    break;
253
                case 'exportStatus':
254
                    $builder->andWhere('at.exportStatus LIKE :status')
255
                        ->setParameter('status', $rule['value']);
256
                    break;
257
                case 'active':
258
                    $builder->andWhere('a.active LIKE :active')
259
                        ->setParameter('active', $rule['value']);
260
                    break;
261
                default:
262
                    continue;
263
            }
264
        }
265
266
        $builder->addOrderBy($order);
267
268
        return $builder;
269
    }
270
271
    /**
272
     * Get all products exported to connect
273
     */
274
    public function getExportListAction()
275
    {
276
        $filter = (array) $this->Request()->getParam('filter', []);
277
        $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...
278
279
        $criteria = new SearchCriteria([
280
            'offset' => (int) $this->Request()->getParam('start'),
281
            'limit' => (int) $this->Request()->getParam('limit'),
282
            'orderBy' => $order['property'],
283
            'orderByDirection' => $order['direction'],
284
285
        ]);
286
287
        foreach ($filter as $key => $rule) {
288
            $field = $rule['property'];
289
            $criteria->{$field} = $rule['value'];
290
        }
291
292
        $exportList = $this->getConnectExport()->getExportList($criteria);
293
294
        $this->View()->assign([
295
            'success' => true,
296
            'data' => $exportList->articles,
297
            'total' => $exportList->count,
298
        ]);
299
    }
300
301
    public function getExportStatusAction()
302
    {
303
        $attrRepo = $this->getModelManager()->getRepository('Shopware\CustomModels\Connect\Attribute');
304
305
        $syncedItems = $attrRepo->countStatus([
306
            Attribute::STATUS_SYNCED,
307
        ]);
308
309
        $totalItems = $attrRepo->countStatus([
310
            Attribute::STATUS_INSERT,
311
            Attribute::STATUS_UPDATE,
312
            Attribute::STATUS_SYNCED,
313
        ]);
314
315
        $this->View()->assign([
316
            'success' => true,
317
            'data' => $syncedItems,
318
            'total' => $totalItems,
319
        ]);
320
    }
321
322
    /**
323
     * Get all products imported from connect
324
     */
325
    public function getImportListAction()
326
    {
327
        $filter = (array) $this->Request()->getParam('filter', []);
328
        $sort = $this->Request()->getParam('sort', []);
329
330
        foreach ($sort as $key => $currentSorter) {
331
            if ($currentSorter['property'] == 'category') {
332
                unset($sort[$key]);
333
            }
334
        }
335
336
        $builder = $this->getListQueryBuilder(
337
            $filter,
338
            $sort
339
        );
340
        $builder->addSelect([
341
            'at.shopId',
342
            'at.sourceId',
343
            'at.exportStatus as connectStatus',
344
        ]);
345
        $builder->andWhere('at.shopId IS NOT NULL');
346
347
        $builder->addOrderBy('at.category', 'ASC');
348
349
        $query = $builder->getQuery();
350
351
        $query->setFirstResult($this->Request()->getParam('start'));
352
        $query->setMaxResults($this->Request()->getParam('limit'));
353
354
        $countResult = array_map('current', $builder->select(['COUNT(DISTINCT at.articleId) as current'])->orderBy('current')->getQuery()->getScalarResult());
355
        $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...
356
        $total = array_sum($countResult);
357
        // todo@sb: find better solution. getQueryCount method counts s_plugin_connect_items.id like they are not grouped by article id
358
//        $total = Shopware()->Models()->getQueryCount($query);
359
        $data = $query->getArrayResult();
360
361
        $this->View()->assign([
362
            'success' => true,
363
            'data' => $data,
364
            'total' => $total
365
        ]);
366
    }
367
368
    /**
369
     * Import parts of the connect category tree to shopware
370
     */
371
    public function importConnectCategoriesAction()
372
    {
373
        $fromCategory = $this->Request()->getParam('fromCategory');
374
        $toCategory = $this->Request()->getParam('toCategory');
375
376
        $entityManager = $this->getModelManager();
377
        $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...
378
379
        // Make sure that the target category exists
380
        $toCategoryModel = $this->getCategoryRepository()->find($toCategory);
381
        if (!$toCategoryModel) {
382
            throw new \RuntimeException("Category with id  {$toCategory} not found");
383
        }
384
385
        // The user might have changed the mapping without saving and then hit the "importCategories"
386
        // button. So we save the parent category's mapping first
387
        $parentCategory = $this->getCategoryModelById($toCategory);
388
        $parentCategory->getAttribute()->setConnectImportMapping($fromCategory);
389
        $entityManager->flush();
390
391
        try {
392
            $entityManager->getConnection()->beginTransaction();
393
            $this->importConnectCategories($fromCategory, $toCategory);
394
            $entityManager->getConnection()->commit();
395
        } catch (\Exception $e) {
396
            $entityManager->getConnection()->rollback();
397
            throw new \RuntimeException('Could not import categories', 0, $e);
398
        }
399
    }
400
401
    /**
402
     * Will import a connect category tree into shopware.
403
     *
404
     * @param $fromCategory
405
     * @param $toCategory
406
     */
407
    public function importConnectCategories($fromCategory, $toCategory)
408
    {
409
        $categoriesToImport = $this->getFlatConnectCategories($fromCategory);
410
        $toCategoryModel = $this->getCategoryRepository()->find($toCategory);
411
        $entityManager = $this->getModelManager();
412
413
        /*
414
         * The import string allows to identify categories, which have already been imported for
415
         * this exact import. This does not prevent the user from importing the same sub-tree
416
         * into multiple shopware categories. But it does prevent him from importing the same sub-tree
417
         * into the same category multiple times
418
         */
419
        $importString = $fromCategory . '-' . $toCategory;
420
        $currentLevel = 1;
421
        $mappings = [];
422
423
        foreach ($categoriesToImport as $id => $category) {
424
            $name = $category['name'];
425
            $parent = $category['parent'];
426
            $level = $category['level'];
427
428
            // Only flush after the level changed - this speeds up the import
429
            if ($currentLevel != $level) {
430
                Shopware()->Models()->flush();
431
            }
432
            $currentLevel = $level;
433
434
            /** @var Category $parentModel */
435
            if (!$parent) {
436
                // Top category level - use toCategoryModel
437
                $parentModel = $toCategoryModel;
438
            } else {
439
                // Parent was created before and is referenced in $mappings
440
                $parentModel = $mappings[$parent];
441
            }
442
443
            // Check if there is already a category attribute for this import
444
            $categoryAttributes = $entityManager->getRepository('\Shopware\Models\Attribute\Category')->findBy(
445
                ['connectImported' => $importString, 'connectImportMapping' => $id],
446
                null,
447
                1
448
            );
449
450
            if (!empty($categoryAttributes)) {
451
                /** @var \Shopware\Models\Attribute\Category $categoryAttribute */
452
                $categoryAttribute = array_pop($categoryAttributes);
453
                $category = $categoryAttribute->getCategory();
454
            } else {
455
                // Create category and attribute model
456
                $category = new Category();
457
                $category->setName($name);
458
                $category->setParent($parentModel);
459
460
                $attribute = new \Shopware\Models\Attribute\Category();
461
                $attribute->setConnectImportMapping($id);
462
                $attribute->setConnectImported($importString);
463
                $category->setAttribute($attribute);
464
465
                Shopware()->Models()->persist($category);
466
                Shopware()->Models()->persist($attribute);
467
            }
468
469
470
            // Store the new category model in out $mappings array
471
            $mappings[$id] = $category;
472
        }
473
474
        Shopware()->Models()->flush();
475
    }
476
477
    public function initParamsAction()
478
    {
479
        $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...
480
        $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...
481
        $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...
482
        $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...
483
        $isFixedPriceAllowed = 0;
484
        $priceType = $this->getSDK()->getPriceType();
485
        if ($priceType === SDK::PRICE_TYPE_BOTH ||
486
            $priceType === SDK::PRICE_TYPE_RETAIL) {
487
            $isFixedPriceAllowed = 1;
488
        }
489
        $marketplaceIncomingIcon = ($marketplaceName == Connect::MARKETPLACE_NAME ? Connect::MARKETPLACE_GREEN_ICON : $marketplaceIcon);
490
        $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...
491
        $purchasePriceInDetail = method_exists('Shopware\Models\Article\Detail', 'setPurchasePrice') ? 1 : 0;
492
493
        $this->View()->assign([
494
            'success' => true,
495
            'data' => [
496
                'marketplaceName' => $marketplaceName,
497
                'marketplaceNetworkUrl' => $marketplaceNetworkUrl,
498
                'marketplaceIcon' => $marketplaceIcon,
499
                'defaultMarketplace' => $defaultMarketplace,
500
                'isFixedPriceAllowed' => $isFixedPriceAllowed,
501
                'marketplaceIncomingIcon' => $marketplaceIncomingIcon,
502
                'marketplaceLogo' => $marketplaceLogo,
503
                'purchasePriceInDetail' => $purchasePriceInDetail,
504
            ]
505
        ]);
506
    }
507
508
    /**
509
     * Returns a flat array of connect categories
510
     *
511
     * @param $rootCategory
512
     * @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...
513
     *      string => array('id' => string, 'name' => string, 'level' => int, 'parent' => string|null)
514
     * )
515
     */
516
    private function getFlatConnectCategories($rootCategory)
517
    {
518
        $sdk = $this->getSDK();
519
        $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...
520
521
        $categoriesToImport = [];
522
        foreach ($connectCategories as $id => $name) {
523
            // Skip all entries which do not start with the parent or do not have it at all
524
            if (strpos($id, $rootCategory) !== 0) {
525
                continue;
526
            }
527
528
            $level = substr_count(preg_replace("#^{$rootCategory}#", '', $id), '/');
529
530
            // Skip the root category
531
            if ($level == 0) {
532
                continue;
533
            }
534
535
            $categoriesToImport[$id] = [
536
                'id' => $id,
537
                'name' => $name,
538
                'level' => $level,
539
                'parent' => $level == 1 ? null : implode('/', array_slice(explode('/', $id), 0, -1))
540
            ];
541
        }
542
543
        // Sort the categories ascending by their level, so parent categories can be imported first
544
        uasort(
545
            $categoriesToImport,
546
            function ($a, $b) {
547
                $a = $a['level'];
548
                $b = $b['level'];
549
                if ($a == $b) {
550
                    return 0;
551
                }
552
553
                return ($a < $b) ? -1 : 1;
554
            }
555
        );
556
557
        return $categoriesToImport;
558
    }
559
560
    /**
561
     * Save a given mapping of a given category to all subcategories
562
     */
563
    public function applyMappingToChildrenAction()
564
    {
565
        $categoryId = $this->Request()->getParam('category');
566
        $mapping = $this->Request()->getParam('mapping');
567
568
        $entityManager = $this->getModelManager();
569
570
        try {
571
            $entityManager->getConnection()->beginTransaction();
572
            $this->applyMappingToChildren($mapping, $categoryId);
573
            $entityManager->getConnection()->commit();
574
            $this->View()->assign([
575
                'success' => true
576
            ]);
577
        } catch (\Exception $e) {
578
            $entityManager->getConnection()->rollback();
579
            $this->View()->assign([
580
                'message' => $e->getMessage(),
581
                'success' => false
582
            ]);
583
        }
584
    }
585
586
    /**
587
     * Returns success true if user could be logged in, or false if something went wrong
588
     *
589
     * @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...
590
     *      string => bool
591
     * )
592
     */
593
    public function loginAction()
594
    {
595
        /** @var \Shopware\Components\HttpClient\GuzzleHttpClient $client */
596
        $client = $this->get('http_client');
597
598
        $shopwareId = $this->Request()->getParam('shopwareId');
599
        $password = $this->Request()->getParam('password');
600
        $loginUrl = $this->getHost() . '/sdk/pluginCommunication/login';
601
602
        // Try to login into connect
603
        $response = $client->post(
604
            $loginUrl,
605
            [
606
                'content-type' => 'application/x-www-form-urlencoded'
607
            ],
608
            [
609
                'username' => urlencode($shopwareId),
610
                'password' => urlencode($password)
611
            ]
612
        );
613
614
        $responseObject = json_decode($response->getBody());
615
616
        if (!$responseObject->success) {
617
            $message = $responseObject->reason;
618
619
            if ($responseObject->reason == SDK::WRONG_CREDENTIALS_MESSAGE) {
620
                $snippets = Shopware()->Snippets()->getNamespace('backend/connect/view/main');
621
                $message = $snippets->get(
622
                    'error/wrong_credentials',
623
                    SDK::WRONG_CREDENTIALS_MESSAGE,
624
                    true
625
                );
626
            }
627
628
            $this->View()->assign([
629
                'success' => false,
630
                'message' => $message
631
            ]);
632
633
            return;
634
        }
635
636
        try {
637
            // set apiKey in Connect config table
638
            // after that create completely new SDK instance,
639
            // because correct apiKey should be used during creation
640
            $this->getConfigComponent()->setConfig('apiKey', $responseObject->apiKey, null, 'general');
641
            $sdk = $this->getConnectFactory()->createSDK();
642
            $sdk->verifySdk();
643
            $this->getConfigComponent()->setConfig('apiKeyVerified', true);
644
            $this->getConfigComponent()->setConfig('shopwareId', $shopwareId, null, 'general');
645
            $this->removeConnectMenuEntry();
646
            $marketplaceSettings = $sdk->getMarketplaceSettings();
647
            $this->getMarketplaceApplier()->apply(new MarketplaceSettings($marketplaceSettings));
648
        } catch (\Exception $e) {
649
            $this->getConfigComponent()->setConfig('apiKey', null, null, 'general');
650
651
            $this->View()->assign([
652
                'success' => false,
653
                'message' => $e->getMessage()
654
            ]);
655
656
            return;
657
        }
658
659
        $this->View()->assign([
660
            'success' => true
661
        ]);
662
    }
663
664
    /**
665
     * Returns success true if user could be logged in, or false if something went wrong
666
     *
667
     * @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...
668
     *      string => bool
669
     * )
670
     */
671
    public function registerAction()
672
    {
673
        /** @var \Shopware\Components\HttpClient\GuzzleHttpClient $client */
674
        $client = $this->get('http_client');
675
676
        $shopwareId = $this->Request()->getParam('shopwareID');
677
        $password = $this->Request()->getParam('password');
678
        $email = $this->Request()->getParam('email');
679
680
        // Enter the valid production url here
681
        $host = $this->getHost();
682
683
        $loginUrl = $host . '/sdk/pluginCommunication/register';
684
685
        $response = $client->post(
686
            $loginUrl,
687
            [
688
                'content-type' => 'application/x-www-form-urlencoded'
689
            ],
690
            [
691
                'username'  => urlencode($shopwareId),
692
                'password'  => urlencode($password),
693
                'email'     => urlencode($email)
694
            ]
695
        );
696
697
        $responseObject = json_decode($response->getBody());
698
699
        if (!$responseObject->success) {
700
            $this->View()->assign([
701
                'success' => false,
702
                'message' => $responseObject->reason
703
            ]);
704
705
            return;
706
        }
707
708
        try {
709
            // set apiKey in Connect config table
710
            // after that create completely new SDK instance,
711
            // because correct apiKey should be used during creation
712
            $this->getConfigComponent()->setConfig('apiKey', $responseObject->apiKey, null, 'general');
713
            $sdk = $this->getConnectFactory()->createSDK();
714
            $sdk->verifySdk();
715
            $this->getConfigComponent()->setConfig('apiKeyVerified', true);
716
            $this->getConfigComponent()->setConfig('shopwareId', $shopwareId, null, 'general');
717
            $this->removeConnectMenuEntry();
718
            $marketplaceSettings = $sdk->getMarketplaceSettings();
719
            $this->getMarketplaceApplier()->apply(new MarketplaceSettings($marketplaceSettings));
720
        } catch (\Exception $e) {
721
            $this->getConfigComponent()->setConfig('apiKey', null, null, 'general');
722
723
            $this->View()->assign([
724
                'success' => false,
725
                'message' => $e->getMessage()
726
            ]);
727
728
            return;
729
        }
730
731
        $this->View()->assign([
732
            'success' => true
733
        ]);
734
    }
735
736
    /**
737
     * Redirects and auto login to SN system
738
     */
739
    public function autoLoginAction()
740
    {
741
        return $this->redirect('http://' . $this->getHost() . '/login');
742
    }
743
744
    /**
745
     * @param bool $loggedIn
746
     * @throws Zend_Db_Adapter_Exception
747
     */
748
    private function removeConnectMenuEntry($loggedIn = true)
749
    {
750
        /** @var Enlight_Components_Db_Adapter_Pdo_Mysql $db */
751
        $db = Shopware()->Db();
752
753
        $result = $db->fetchAssoc("SELECT id, parent, pluginID FROM s_core_menu WHERE controller = 'connect' AND name = 'Register' ORDER BY id DESC LIMIT 1");
754
        if (empty($result)) {
755
            return;
756
        }
757
        $row = current($result);
758
759
        $db->exec('DELETE FROM s_core_menu WHERE id = ' . $row['id']);
760
761
        $insertSql = "INSERT INTO s_core_menu (
762
            parent,
763
            name,
764
            class,
765
            pluginID,
766
            controller,
767
            action,
768
            onclick,
769
            active
770
          ) VALUES (
771
            '#parent#',
772
            '#name#',
773
            '#class#',
774
            #pluginID#,
775
            '#controller#',
776
            '#action#',
777
            '#onclick#',
778
            1
779
          )";
780
781
        $db->exec(strtr($insertSql, [
782
            '#parent#' => $row['parent'],
783
            '#name#' => 'Import',
784
            '#class#' => 'sc-icon-import',
785
            '#pluginID#' => $row['pluginID'],
786
            '#controller#' => 'Connect',
787
            '#onclick#' => '',
788
            '#action#' => 'Import'
789
        ]));
790
791
        $db->exec(strtr($insertSql, [
792
            '#parent#' => $row['parent'],
793
            '#name#' => 'Export',
794
            '#class#' => 'sc-icon-export',
795
            '#pluginID#' => $row['pluginID'],
796
            '#controller#' => 'Connect',
797
            '#onclick#' => '',
798
            '#action#' => 'Export'
799
        ]));
800
801
        $db->exec(strtr($insertSql, [
802
            '#parent#' => $row['parent'],
803
            '#name#' => 'Settings',
804
            '#class#' => 'sprite-gear',
805
            '#pluginID#' => $row['pluginID'],
806
            '#controller#' => 'Connect',
807
            '#onclick#' => '',
808
            '#action#' => 'Settings'
809
        ]));
810
811
        $db->exec(strtr($insertSql, [
812
            '#parent#' => $row['parent'],
813
            '#name#' => 'OpenConnect',
814
            '#class#' => 'connect-icon',
815
            '#pluginID#' => $row['pluginID'],
816
            '#controller#' => 'Connect',
817
            '#onclick#' => 'window.open("connect/autoLogin")',
818
            '#action#' => 'OpenConnect'
819
        ]));
820
    }
821
822
    /**
823
     * Helper that will assign a given mapping to all children of a given category
824
     *
825
     * @param $mapping string
826
     * @param $categoryId int
827
     * @throws \Exception
828
     */
829
    private function applyMappingToChildren($mapping, $categoryId)
830
    {
831
        $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...
832
        $ids = $this->getChildCategoriesIds($categoryId);
833
        $entityManager = $this->getModelManager();
834
835
836
        if (!$categoryId) {
837
            throw new \RuntimeException("Category '{$categoryId}' not found");
838
        }
839
840
        // First of all try to save the mapping for the parent category. If that fails,
841
        // it mustn't be done for the child categories
842
        $parentCategory = $this->getCategoryModelById($categoryId);
843
        $parentCategory->getAttribute()->setConnectExportMapping($mapping);
844
        $entityManager->flush();
845
846
        // Don't set the children with models in order to speed things up
847
        $builder = $entityManager->createQueryBuilder();
848
        $builder->update('\Shopware\Models\Attribute\Category', 'categoryAttribute')
849
            ->set('categoryAttribute.connectExportMapping', $builder->expr()->literal($mapping))
850
            ->where($builder->expr()->in('categoryAttribute.categoryId', $ids));
851
852
        $builder->getQuery()->execute();
853
    }
854
855
    /**
856
     * Helper function which returns the IDs of the child categories of a given parent category
857
     *
858
     * @param $parentId int
859
     * @return array
860
     */
861
    private function getChildCategoriesIds($parentId)
862
    {
863
        $query = $this->getModelManager()->createQuery('SELECT c.id from Shopware\Models\Category\Category c WHERE c.path LIKE ?1 ');
864
        $query->setParameter(1, ["%|{$parentId}|%"]);
865
        $result = $query->getResult(\Doctrine\ORM\AbstractQuery::HYDRATE_ARRAY);
866
867
        // Pop IDs from result rows
868
        return array_map(
869
            function ($row) {
870
                return array_pop($row);
871
            },
872
            $result
873
        );
874
    }
875
876
    /**
877
     * get the amount of products that can be exported
878
     */
879
    public function getArticleCountAction()
880
    {
881
        try {
882
            $count = $this->getExportAssignmentService()->getCountOfAllExportableArticles();
883
            $this->View()->assign([
884
                'success' => true,
885
                'count' => $count
886
            ]);
887
        } catch (\Exception $e) {
888
            $this->View()->assign([
889
                'success' => false,
890
                'message' => $e->getMessage()
891
            ]);
892
        }
893
    }
894
895
    public function exportAllProductsAction()
896
    {
897
        try {
898
            $this->updatePurchasePriceField();
899
900
            $offset = $this->Request()->getPost('offset', []);
901
            $batchSize = $this->Request()->getPost('batchSize', []);
902
903
            $errors = $this->getExportAssignmentService()->exportBatchOfAllProducts($offset, $batchSize);
904
905 View Code Duplication
            if (!empty($errors)) {
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...
906
                $this->View()->assign([
907
                    'success' => false,
908
                    'messages' => $errors
909
                ]);
910
911
                return;
912
            }
913
        } catch (\Exception $e) {
914
            $this->View()->assign([
915
                'success' => false,
916
                'message' => $e->getMessage()
917
            ]);
918
        }
919
        $this->View()->assign([
920
            'success' => true,
921
        ]);
922
    }
923
924
    /**
925
     * Collect all source ids by given article ids
926
     */
927
    public function getArticleSourceIdsAction()
928
    {
929
        try {
930
            $articleIds = $this->Request()->getPost('ids', []);
931
932
            if (!is_array($articleIds)) {
933
                $articleIds = [$articleIds];
934
            }
935
936
            $sourceIds = $this->getHelper()->getArticleSourceIds($articleIds);
937
938
            $this->View()->assign([
939
                'success' => true,
940
                'sourceIds' => $sourceIds
941
            ]);
942
        } catch (\Exception $e) {
943
            $this->View()->assign([
944
                'success' => false,
945
                'message' => $e->getMessage()
946
            ]);
947
        }
948
    }
949
950
    /**
951
     * Called when ConnectCategories have to be recreated
952
     */
953 View Code Duplication
    public function applyConnectCategoriesRecoveryAction()
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...
954
    {
955
        $batchsize = $this->Request()->getPost('batchsize');
956
        $offset = $this->Request()->getPost('offset');
957
        try {
958
            $this->getHelper()->recreateConnectCategories((int) $offset, (int) $batchsize);
959
            $this->View()->assign([
960
                'success' => true,
961
            ]);
962
        } catch (Exception $e) {
963
            $this->View()->assign([
964
                'success' => false,
965
                'messages' => [ErrorHandler::TYPE_DEFAULT_ERROR => [$e->getMessage()]]
966
            ]);
967
        }
968
    }
969
970
    /**
971
     * Called when ShopId has to be added to ConnectCategories
972
     */
973 View Code Duplication
    public function addShopIdToConnectCategoriesAction()
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...
974
    {
975
        $batchsize = $this->Request()->getPost('batchsize');
976
        $offset = $this->Request()->getPost('offset');
977
        try {
978
            $this->getHelper()->addShopIdToConnectCategories((int) $offset, (int) $batchsize);
979
            $this->View()->assign([
980
                'success' => true,
981
            ]);
982
        } catch (Exception $e) {
983
            $this->View()->assign([
984
                'success' => false,
985
                'messages' => [ErrorHandler::TYPE_DEFAULT_ERROR => [$e->getMessage()]]
986
            ]);
987
        }
988
    }
989
990
    /**
991
     * Called when a product variants were marked for update in the connect backend module
992
     */
993
    public function insertOrUpdateProductAction()
994
    {
995
        // if priceType comes from SN and shopware version is 5.2
996
        // priceFieldForPurchasePriceExport is empty
997
        // we need to set it because there isn't customer groups
998
        // purchasePrice is stored always in article detail
999
        $this->updatePurchasePriceField();
1000
        $sourceIds = $this->Request()->getPost('sourceIds');
1001
        $connectExport = $this->getConnectExport();
1002
1003
        try {
1004
            $errors = $connectExport->export($sourceIds);
1005
        } catch (\RuntimeException $e) {
1006
            $this->View()->assign([
1007
                'success' => false,
1008
                'messages' => [ErrorHandler::TYPE_DEFAULT_ERROR => [$e->getMessage()]]
1009
            ]);
1010
1011
            return;
1012
        }
1013
1014 View Code Duplication
        if (!empty($errors)) {
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...
1015
            $this->View()->assign([
1016
                'success' => false,
1017
                'messages' => $errors
1018
            ]);
1019
1020
            return;
1021
        }
1022
1023
        $this->View()->assign([
1024
            'success' => true
1025
        ]);
1026
    }
1027
1028
    /**
1029
     * Delete a product from connect export
1030
     */
1031
    public function deleteProductAction()
1032
    {
1033
        $sdk = $this->getSDK();
1034
        $ids = $this->Request()->getPost('ids');
1035
        foreach ($ids as $id) {
1036
            /** @var \Shopware\Models\Article\Article $model */
1037
            $model = $this->getConnectExport()->getArticleModelById($id);
1038
            if ($model === null) {
1039
                continue;
1040
            }
1041
            /** @var \Shopware\Models\Article\Detail $detail */
1042
            foreach ($model->getDetails() as $detail) {
1043
                $attribute = $this->getHelper()->getConnectAttributeByModel($detail);
1044
                $sdk->recordDelete($attribute->getSourceId());
1045
                $attribute->setExportStatus(Attribute::STATUS_DELETE);
1046
                $attribute->setExported(false);
1047
            }
1048
        }
1049
        Shopware()->Models()->flush();
1050
    }
1051
1052
    /**
1053
     * Verify a given api key against the connect server
1054
     */
1055
    public function verifyApiKeyAction()
1056
    {
1057
        $sdk = $this->getSDK();
1058
        try {
1059
            $key = $this->Request()->getPost('apiKey');
1060
            $sdk->verifyKey($key);
1061
            $this->View()->assign([
1062
                'success' => true
1063
            ]);
1064
            $this->getConfigComponent()->setConfig('apiKeyVerified', true);
1065
            $marketplaceSettings = $sdk->getMarketplaceSettings();
1066
            $this->getMarketplaceApplier()->apply(new MarketplaceSettings($marketplaceSettings));
1067
        } catch (\Exception $e) {
1068
            $this->View()->assign([
1069
                'message' => $e->getMessage(),
1070
                'success' => false
1071
            ]);
1072
            $this->getConfigComponent()->setConfig('apiKeyVerified', false);
1073
        }
1074
1075
        $this->getModelManager()->flush();
1076
    }
1077
1078
    /**
1079
     * Returns the connectAttribute data for a given articleId
1080
     *
1081
     * @throws RuntimeException
1082
     * @throws Exception
1083
     */
1084
    public function getConnectDataAction()
1085
    {
1086
        $articleId = $this->Request()->getParam('articleId');
1087
1088
        if (!$articleId) {
1089
            throw new \Exception('Connect: ArticleId empty');
1090
        }
1091
1092
        /** @var Article $articleModel */
1093
        $articleModel = $this->getArticleRepository()->find($articleId);
1094
1095
        if (!$articleModel) {
1096
            throw new \RuntimeException("Could not find model for article with id {$articleId}");
1097
        }
1098
1099
        $data = $this->getHelper()->getOrCreateConnectAttributes($articleModel);
1100
1101
        $data = $this->getModelManager()->toArray($data);
1102
        if (isset($data['articleId'])) {
1103
            $data = [$data];
1104
        }
1105
1106
        $this->View()->assign([
1107
            'success' => true,
1108
            'data' => $data
1109
        ]);
1110
    }
1111
1112
    /**
1113
     * Save a given connect attribute
1114
     */
1115
    public function saveConnectAttributeAction()
1116
    {
1117
        $data = $this->Request()->getParams();
1118
1119
        /** @var \Shopware\CustomModels\Connect\Attribute $connectAttribute */
1120
        $connectAttribute = $this->getModelManager()->find('Shopware\CustomModels\Connect\Attribute', $data['id']);
1121
        if (!$connectAttribute) {
1122
            throw new \RuntimeException("Could not find connect attribute with id {$data['id']}");
1123
        }
1124
1125
        /** @var \Shopware\Models\Article\Detail $detail */
1126
        foreach ($connectAttribute->getArticle()->getDetails() as $detail) {
1127
            $connectAttribute = $this->getHelper()->getConnectAttributeByModel($detail);
1128
            // Only allow changes in the fixedPrice field if this is a local product
1129
            if (!$connectAttribute->getShopId()) {
1130
                $connectAttribute->setFixedPrice($data['fixedPrice']);
1131
            }
1132
            // Save the update fields
1133
            foreach ($data as $key => $value) {
1134
                if (strpos($key, 'update') === 0) {
1135
                    $setter = 'set' . ucfirst($key);
1136
                    $connectAttribute->$setter($value);
1137
                }
1138
            }
1139
            $this->getModelManager()->persist($connectAttribute);
1140
        }
1141
1142
        $this->getModelManager()->flush();
1143
1144
        $this->View()->assign(['success' => true]);
1145
    }
1146
1147
    /**
1148
     * Saves the changed "connectAllowed" attribute. Saving this attribute should be done
1149
     * by the shipping-module on its own, right now (as of SW 4.2.0) it does not do so.
1150
     *
1151
     * todo: Once the shipping module is fixed, increase the required version of this plugin
1152
     * and remove this and the unnecessary ExtJS extensions
1153
     */
1154
    public function saveShippingAttributeAction()
1155
    {
1156
        $shippingId = $this->Request()->getParam('shippingId');
1157
        $connectAllowed = $this->Request()->getParam('connectAllowed', true);
1158
1159
        if (!$shippingId) {
1160
            return;
1161
        }
1162
1163
        $shippingRepo = $this->getModelManager()->getRepository('\Shopware\Models\Dispatch\Dispatch');
1164
        /** @var \Shopware\Models\Dispatch\Dispatch $shipping */
1165
        $shipping = $shippingRepo->find($shippingId);
1166
1167
        if (!$shipping) {
1168
            return;
1169
        }
1170
1171
        $attribute = $shipping->getAttribute();
1172
1173
        if (!$attribute) {
1174
            $attribute = new \Shopware\Models\Attribute\Dispatch();
1175
            $attribute->setDispatch($shipping);
1176
            $shipping->setAttribute($attribute);
1177
            $this->getModelManager()->persist($attribute);
1178
        }
1179
1180
        $attribute->setConnectAllowed($connectAllowed);
1181
1182
        $this->getModelManager()->flush();
1183
1184
        $this->View()->assign('success', true);
1185
    }
1186
1187
    /**
1188
     * Lists all logs
1189
     */
1190
    public function getLogsAction()
1191
    {
1192
        $params = $this->Request()->getParams();
1193
        $order = $this->Request()->getParam('sort', [['property' => 'time', 'direction' => 'DESC']]);
1194
        $filters = $this->Request()->getParam('filter');
1195
1196
        $commandFilters = [];
1197
        foreach ($params as $key => $param) {
1198
            if (strpos($key, 'commandFilter_') !== false && $param == 'true') {
1199
                $commandFilters[] = str_replace('commandFilter_', '', $key);
1200
            }
1201
        }
1202
1203
        if (empty($commandFilters)) {
1204
            return;
1205
        }
1206
1207
        foreach ($order as &$rule) {
1208
            if ($rule['property'] == 'time') {
1209
                $rule['property'] = 'id';
1210
            }
1211
            $rule['property'] = 'logs.' . $rule['property'];
1212
        }
1213
1214
        $builder = $this->getModelManager()->createQueryBuilder();
1215
        $builder->select('logs');
1216
        $builder->from('Shopware\CustomModels\Connect\Log', 'logs')
1217
            ->addOrderBy($order)
1218
            ->where('logs.command IN (:commandFilter)')
1219
            ->setParameter('commandFilter', $commandFilters);
1220
1221
        foreach ($filters as $filter) {
1222
            switch ($filter['property']) {
1223
                case 'search':
1224
                    $builder->andWhere(
1225
                        'logs.request LIKE :search OR logs.response LIKE :search'
1226
                    );
1227
                    $builder->setParameter('search', $filter['value']);
1228
                    break;
1229
            }
1230
        }
1231
1232
        switch ($this->Request()->getParam('errorFilter', -1)) {
1233
            case 0:
1234
                $builder->andWhere('logs.isError = 1');
1235
                break;
1236
            case 1:
1237
                $builder->andWhere('logs.isError = 0');
1238
                break;
1239
        }
1240
1241
        $query = $builder->getQuery()
1242
            ->setFirstResult($this->Request()->getParam('start', 0))
1243
            ->setMaxResults($this->Request()->getParam('limit', 25));
1244
1245
        $total = Shopware()->Models()->getQueryCount($query);
1246
        $data = $query->getArrayResult();
1247
1248
        $this->View()->assign([
1249
            'success' => true,
1250
            'data' => $data,
1251
            'total' => $total
1252
        ]);
1253
    }
1254
1255
    /**
1256
     * Get a list of log commands
1257
     */
1258
    public function getLogCommandsAction()
1259
    {
1260
        $data = $this->getModelManager()->getConnection()->fetchAll(
1261
            'SELECT DISTINCT `command` FROM `s_plugin_connect_log`'
1262
        );
1263
1264
        $data = array_map(function ($column) {
1265
            return $column['command'];
1266
        }, $data);
1267
1268
        // Enforce these fields
1269
        foreach (['fromShop', 'toShop', 'getLastRevision', 'update', 'checkProducts', 'buy', 'reserveProducts', 'confirm'] as $value) {
1270
            if (!in_array($value, $data)) {
1271
                $data[] = $value;
1272
            }
1273
        }
1274
1275
        $this->View()->assign([
1276
            'success' => true,
1277
            'data' => $data
1278
        ]);
1279
    }
1280
1281
    /**
1282
     * Delete all log entries
1283
     */
1284
    public function clearLogAction()
1285
    {
1286
        $connection = $this->getModelManager()->getConnection();
1287
        $connection->exec('TRUNCATE `s_plugin_connect_log`;');
1288
    }
1289
1290
    /**
1291
     * @return ConnectExport
1292
     */
1293 View Code Duplication
    public function getConnectExport()
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...
1294
    {
1295
        return new ConnectExport(
1296
            $this->getHelper(),
1297
            $this->getSDK(),
1298
            $this->getModelManager(),
1299
            new ProductsAttributesValidator(),
1300
            $this->getConfigComponent(),
1301
            new ErrorHandler(),
1302
            $this->container->get('events')
1303
        );
1304
    }
1305
1306
    /**
1307
     * @return Config
1308
     */
1309
    public function getConfigComponent()
1310
    {
1311
        if ($this->configComponent === null) {
1312
            $this->configComponent = ConfigFactory::getConfigInstance();
1313
        }
1314
1315
        return $this->configComponent;
1316
    }
1317
1318
    /**
1319
     * Saves the dynamic streams to the db.
1320
     * Cronjob will looks for those streams to process them
1321
     */
1322
    public function prepareDynamicStreamsAction()
1323
    {
1324
        $streamIds = $this->Request()->getParam('streamIds', []);
1325
1326
        try {
1327
            $streamService = $this->getProductStreamService();
1328
            $streams = $streamService->findStreams($streamIds);
1329
1330
            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...
1331
                $message = Shopware()->Snippets()->getNamespace('backend/connect/view/main')->get(
1332
                    'export/message/error_no_stream_selected',
1333
                    'No streams were selected',
1334
                    true
1335
                );
1336
                throw new \Exception($message);
1337
            }
1338
1339
            $modelManager = $this->getModelManager();
1340
1341
            foreach ($streams as $stream) {
1342
                $streamAttr = $streamService->createStreamAttribute($stream);
1343
1344
                if (!$streamAttr->getExportStatus()) {
1345
                    $streamAttr->setExportStatus(ProductStreamService::STATUS_PENDING);
1346
                }
1347
1348
                $modelManager->persist($streamAttr);
1349
            }
1350
1351
            $modelManager->flush();
1352
        } catch (\Exception $e) {
1353
            $this->View()->assign([
1354
                'success' => false,
1355
                'message' => $e->getMessage()
1356
            ]);
1357
        }
1358
1359
        $this->View()->assign([
1360
            'success' => true,
1361
        ]);
1362
    }
1363
1364
    /**
1365
     * Add given category to products
1366
     */
1367
    public function assignProductsToCategoryAction()
1368
    {
1369
        $articleIds = $this->Request()->getParam('ids');
1370
        $categoryId = (int) $this->Request()->getParam('category');
1371
1372
        /** @var Category $category */
1373
        $category = $this->getCategoryModelById($categoryId);
1374
        if (!is_null($category)) {
1375
            foreach ($articleIds as $id) {
1376
                /** @var \Shopware\Models\Article\Article $article */
1377
                $article = $this->getConnectExport()->getArticleModelById($id);
1378
                if (is_null($article)) {
1379
                    continue;
1380
                }
1381
                $article->addCategory($category);
1382
                $this->getModelManager()->persist($article);
1383
            }
1384
            $this->getModelManager()->flush();
1385
        }
1386
1387
        $this->View()->assign(
1388
            [
1389
                'success' => true
1390
            ]
1391
        );
1392
    }
1393
1394
    public function getStreamListAction()
1395
    {
1396
        $productStreamService = $this->getProductStreamService();
1397
1398
        $result = $productStreamService->getList(
1399
            $this->Request()->getParam('start', 0),
1400
            $this->Request()->getParam('limit', 20)
1401
        );
1402
1403
        $this->View()->assign(
1404
            [
1405
                'success' => true,
1406
                'data' => $result['data'],
1407
                'total' => $result['total'],
1408
            ]
1409
        );
1410
    }
1411
1412
    public function getStreamProductsCountAction()
1413
    {
1414
        $streamIds = $this->request->getParam('ids', []);
1415
1416
        if (empty($streamIds)) {
1417
            $this->View()->assign([
1418
                'success' => false,
1419
                'message' => 'No stream selected'
1420
            ]);
1421
        }
1422
1423
        $sourceIdsCount = $this->getProductStreamService()->countProductsInStaticStream($streamIds);
1424
1425
        $this->View()->assign([
1426
            'success' => true,
1427
            'sourceIdsCount' => $sourceIdsCount
1428
        ]);
1429
    }
1430
1431
    public function exportStreamAction()
1432
    {
1433
        $streamIds = $this->request->getParam('streamIds', []);
1434
        $currentStreamIndex = $this->request->getParam('currentStreamIndex', 0);
1435
        $offset = $this->request->getParam('offset', 0);
1436
        $limit = $this->request->getParam('limit', 1);
1437
1438
        $streamId = $streamIds[$currentStreamIndex];
1439
1440
        $productStreamService = $this->getProductStreamService();
1441
1442
        $streamsAssignments = $this->getStreamAssignments($streamId);
1443
1444
        if (!$streamsAssignments) {
1445
            return;
1446
        }
1447
1448
        $sourceIds = $this->getHelper()->getArticleSourceIds($streamsAssignments->getArticleIds());
1449
        $sliced = array_slice($sourceIds, $offset, $limit);
1450
1451
        $exported = $this->exportStreamProducts($streamId, $sliced, $streamsAssignments);
1452
1453
        if (!$exported) {
1454
            return;
1455
        }
1456
1457
        $nextStreamIndex = $currentStreamIndex;
1458
        $newOffset = $offset + $limit;
1459
        $hasMoreIterations = true;
1460
1461
        $processedStreams = $currentStreamIndex;
1462
        $sourceIdsCount = $this->getProductStreamService()->countProductsInStaticStream($streamIds);
1463
1464
        //In this case all the products from a single stream were exported successfully but there are still more streams to be processed.
1465
        if ($newOffset > $sourceIdsCount && $currentStreamIndex + 1 <= (count($streamIds) - 1)) {
1466
            $productStreamService->changeStatus($streamId, ProductStreamService::STATUS_EXPORT, 'Success');
1467
            $nextStreamIndex = $currentStreamIndex + 1;
1468
1469
            $streamsAssignments = $this->getStreamAssignments($streamIds[$nextStreamIndex]);
1470
1471
            if (!$streamsAssignments) {
1472
                return;
1473
            }
1474
1475
            $newOffset = 0;
1476
        }
1477
1478
        //In this case all the products from all streams were exported successfully.
1479
        if ($newOffset > $sourceIdsCount && $currentStreamIndex + 1 > (count($streamIds) - 1)) {
1480
            $productStreamService->changeStatus($streamId, ProductStreamService::STATUS_EXPORT);
1481
            $hasMoreIterations = false;
1482
            $newOffset = $sourceIdsCount;
1483
            $processedStreams = count($streamIds);
1484
        }
1485
1486
1487
        $this->View()->assign([
1488
            'success' => true,
1489
            'nextStreamIndex' => $nextStreamIndex,
1490
            'newOffset' => $newOffset,
1491
            'hasMoreIterations' => $hasMoreIterations,
1492
            'processedStreams' => $processedStreams,
1493
        ]);
1494
    }
1495
1496
    public function hasManyVariantsAction()
1497
    {
1498
        $streamId = $this->request->getParam('streamId');
1499
        $articleId = $this->request->getParam('articleId');
1500
1501
        if (!$this->getProductStreamService()->isStreamExported($streamId)) {
1502
            return;
1503
        }
1504
1505
        $sourceIds = $this->getHelper()->getSourceIdsFromArticleId($articleId);
1506
1507
        $hasManyVariants = false;
1508
1509
        if (count($sourceIds) > ProductStreamService::PRODUCT_LIMIT) {
1510
            $hasManyVariants = true;
1511
        }
1512
1513
        $this->View()->assign([
1514
            'success' => true,
1515
            'hasManyVariants' => $hasManyVariants
1516
        ]);
1517
    }
1518
1519
    public function exportAllWithCronAction()
1520
    {
1521
        try {
1522
            $db = Shopware()->Db();
1523
            $this->getConfigComponent()->setConfig('autoUpdateProducts', 2, null, 'export');
1524
1525
            $db->update(
1526
                's_crontab',
1527
                ['active' => 1],
1528
                "action = 'ShopwareConnectUpdateProducts' OR action = 'Shopware_CronJob_ShopwareConnectUpdateProducts'"
1529
            );
1530
1531
            $db->update(
1532
                's_plugin_connect_items',
1533
                ['cron_update' => 1, 'export_status' => Attribute::STATUS_UPDATE],
1534
                'shop_id IS NULL'
1535
            );
1536
1537
            $this->View()->assign([
1538
                'success' => true,
1539
            ]);
1540
        } catch (\Exception $e) {
1541
            $this->View()->assign([
1542
                'success' => false,
1543
                'message' => $e->getMessage()
1544
            ]);
1545
        }
1546
    }
1547
1548
    /**
1549
     * @param int $streamId
1550
     * @return bool|ProductStreamsAssignments
1551
     */
1552
    private function getStreamAssignments($streamId)
1553
    {
1554
        $productStreamService = $this->getProductStreamService();
1555
1556
        try {
1557
            $streamsAssignments = $productStreamService->prepareStreamsAssignments($streamId);
1558
        } catch (\Exception $e) {
1559
            $this->View()->assign([
1560
                'success' => false,
1561
                'messages' => [ErrorHandler::TYPE_DEFAULT_ERROR => [$e->getMessage()]]
1562
            ]);
1563
1564
            return false;
1565
        }
1566
1567
        return $streamsAssignments;
1568
    }
1569
1570
    /**
1571
     * @param int $streamId
1572
     * @param array $sourceIds
1573
     * @param ProductStreamsAssignments $streamsAssignments
1574
     * @return bool
1575
     */
1576
    private function exportStreamProducts($streamId, array $sourceIds, $streamsAssignments)
1577
    {
1578
        $productStreamService = $this->getProductStreamService();
1579
        $connectExport = $this->getConnectExport();
1580
1581
        try {
1582
            $errorMessages = $connectExport->export($sourceIds, $streamsAssignments);
1583
        } catch (\RuntimeException $e) {
1584
            $productStreamService->changeStatus($streamId, ProductStreamService::STATUS_ERROR);
1585
            $this->View()->assign([
1586
                'success' => false,
1587
                'messages' => [ErrorHandler::TYPE_DEFAULT_ERROR => [$e->getMessage()]]
1588
            ]);
1589
1590
            return false;
1591
        }
1592
1593
        if (!empty($errorMessages)) {
1594
            $errorMessagesText = '';
1595
            $displayedErrorTypes = [
1596
                ErrorHandler::TYPE_DEFAULT_ERROR,
1597
                ErrorHandler::TYPE_PRICE_ERROR
1598
            ];
1599
1600
            foreach ($displayedErrorTypes as $displayedErrorType) {
1601
                $errorMessagesText .= implode('\n', $errorMessages[$displayedErrorType]);
1602
            }
1603
1604
            $productStreamService->changeStatus($streamId, ProductStreamService::STATUS_ERROR, $errorMessagesText);
1605
1606
            $this->View()->assign([
1607
                'success' => false,
1608
                'messages' => $errorMessages
1609
            ]);
1610
1611
            return false;
1612
        }
1613
1614
        return true;
1615
    }
1616
1617
    /**
1618
     * Deletes products from connect stream export
1619
     */
1620
    public function removeStreamsAction()
1621
    {
1622
        $streamIds = $this->request->getParam('ids', []);
1623
1624
        $productStreamService = $this->getProductStreamService();
1625
        $connectExport = $this->getConnectExport();
1626
1627
        $filteredStreamIds = $productStreamService->filterExportedStreams($streamIds);
1628
1629
        foreach ($filteredStreamIds as $streamId) {
1630
            try {
1631
                $removedRecords = [];
1632
1633
                $assignments = $productStreamService->getStreamAssignments($streamId);
1634
                $sourceIds = $this->getHelper()->getArticleSourceIds($assignments->getArticleIds());
1635
                $items = $connectExport->fetchConnectItems($sourceIds, false);
1636
1637
                foreach ($items as $item) {
1638
                    if ($productStreamService->allowToRemove($assignments, $streamId, $item['articleId'])) {
1639
                        $this->getSDK()->recordDelete($item['sourceId']);
1640
                        $removedRecords[] = $item['sourceId'];
1641 View Code Duplication
                    } else {
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...
1642
                        //updates items with the new streams
1643
                        $streamCollection = $assignments->getStreamsByArticleId($item['articleId']);
1644
                        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...
1645
                            continue;
1646
                        }
1647
1648
                        //removes current stream from the collection
1649
                        unset($streamCollection[$streamId]);
1650
1651
                        $this->getSDK()->recordStreamAssignment(
1652
                            $item['sourceId'],
1653
                            $streamCollection,
1654
                            $item['groupId']
1655
                        );
1656
                    }
1657
                }
1658
1659
                $connectExport->updateConnectItemsStatus($removedRecords, Attribute::STATUS_DELETE);
1660
                $this->getSDK()->recordStreamDelete($streamId);
1661
                $productStreamService->changeStatus($streamId, ProductStreamService::STATUS_DELETE);
1662
            } catch (\Exception $e) {
1663
                $productStreamService->changeStatus($streamId, ProductStreamService::STATUS_ERROR);
1664
                $this->View()->assign([
1665
                    'success' => false,
1666
                    'message' => $e->getMessage()
1667
                ]);
1668
1669
                return;
1670
            }
1671
        }
1672
    }
1673
1674 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...
1675
    {
1676
        if (!$this->marketplaceSettingsApplier) {
1677
            $this->marketplaceSettingsApplier = new MarketplaceSettingsApplier(
1678
                $this->getConfigComponent(),
1679
                Shopware()->Models(),
1680
                Shopware()->Db()
1681
            );
1682
        }
1683
1684
        return $this->marketplaceSettingsApplier;
1685
    }
1686
1687
    /**
1688
     * @return SnHttpClient
1689
     */
1690 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...
1691
    {
1692
        if (!$this->snHttpClient) {
1693
            $this->snHttpClient = new SnHttpClient(
1694
                $this->get('http_client'),
1695
                new \Shopware\Connect\Gateway\PDO(Shopware()->Db()->getConnection()),
1696
                $this->getConfigComponent()
1697
            );
1698
        }
1699
1700
        return $this->snHttpClient;
1701
    }
1702
1703
    /**
1704
     * @return string
1705
     */
1706
    protected function getHost()
1707
    {
1708
        $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...
1709
        if (!$host || $host == '') {
1710
            $host = $this->getConfigComponent()->getMarketplaceUrl();
1711
        }
1712
1713
        return $host;
1714
    }
1715
1716
    /**
1717
     * @return ProductStreamService
1718
     */
1719
    protected function getProductStreamService()
1720
    {
1721
        if ($this->productStreamService === null) {
1722
            $this->productStreamService = $this->get('swagconnect.product_stream_service');
1723
        }
1724
1725
        return $this->productStreamService;
1726
    }
1727
1728
    /**
1729
     * {@inheritdoc}
1730
     */
1731
    public function getWhitelistedCSRFActions()
1732
    {
1733
        return ['login', 'autoLogin'];
1734
    }
1735
1736
    public function checkPluginVersionAction()
1737
    {
1738
        $info = json_decode(file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'plugin.json'), true);
1739
1740
        // URL: https://api.shopware.com/pluginStore/updates?pluginNames%5B0%5D=SwagBepado&shopwareVersion=5.0.0
1741
        //This url will return plugin version information only if shopware version is 5.2.* or higher
1742
        $baseUrl = 'https://api.shopware.com/pluginStore/updates';
1743
        $shopVersion = Shopware::VERSION;
1744
        $shopVersion = $shopVersion === '___VERSION___' ? '5.0.0' : $shopVersion;
1745
        $pluginName = 'SwagConnect';
1746
        $apiResponse = json_decode(
1747
            file_get_contents($baseUrl . '?pluginNames[0]=' . $pluginName . '&shopwareVersion=' . $shopVersion)
1748
        )[0];
1749
        $updateAvailable = false;
1750
1751
        if (version_compare($apiResponse->highestVersion, $info['currentVersion'], '>') && $this->userHasPermissions()) {
1752
            $updateAvailable = true;
1753
        }
1754
1755
        $this->View()->assign([
1756
            'success' => true,
1757
            'updateAvailable' => $updateAvailable
1758
        ]);
1759
    }
1760
1761
    private function userHasPermissions()
1762
    {
1763
        return Shopware()->Plugins()->Backend()->Auth()->isAllowed([
1764
            'privilege' => 'update',
1765
            'resource' => 'pluginmanager',
1766
            'role' => null
1767
        ]);
1768
    }
1769
}
1770