• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

waynestate / base-site / 28397477017

29 Jun 2026 07:32PM UTC coverage: 99.612% (-0.4%) from 100.0%
28397477017

Pull #1005

github

web-flow
Merge 3cd36ecc4 into 9dbc144f0
Pull Request #1005: Feature | Fix cross-site modular promo links

19 of 24 new or added lines in 1 file covered. (79.17%)

3 existing lines in 1 file now uncovered.

1283 of 1288 relevant lines covered (99.61%)

10.66 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.01
/app/Repositories/ModularPageRepository.php
1
<?php
2

3
namespace App\Repositories;
4

5
use Contracts\Repositories\ModularPageRepositoryContract;
6
use Contracts\Repositories\EventRepositoryContract;
7
use Contracts\Repositories\ArticleRepositoryContract;
8
use Illuminate\Cache\Repository;
9
use Illuminate\Support\Str;
10
use Waynestate\Api\Connector;
11
use Waynestate\Promotions\ParsePromos;
12

13
class ModularPageRepository implements ModularPageRepositoryContract
14
{
15
    /** @var Connector */
16
    protected $wsuApi;
17

18
    /** @var ParsePromos */
19
    protected $parsePromos;
20

21
    /** @var Repository */
22
    protected $cache;
23

24
    /** @var ArticleRepositoryContract */
25
    protected $article;
26

27
    /** @var EventRepositoryContract */
28
    protected $event;
29

30
    /**
31
     * Construct the repository.
32
     *
33
     * @param Connector $wsuApi
34
     * @param ParsePromos $parsePromos
35
     * @param Repository $cache
36
     * @param ArticleRepositoryContract $article
37
     * @param EventRepositoryContract $event
38
     *
39
     */
40
    public function __construct(
41
        Connector $wsuApi,
42
        ParsePromos $parsePromos,
43
        Repository $cache,
44
        ArticleRepositoryContract $article,
45
        EventRepositoryContract $event
46
    ) {
47
        $this->wsuApi = $wsuApi;
88✔
48
        $this->parsePromos = $parsePromos;
88✔
49
        $this->cache = $cache;
88✔
50
        $this->article = $article;
88✔
51
        $this->event = $event;
88✔
52
    }
53

54
    /**
55
     * {@inheritdoc}
56
     */
57
    public function getModularComponents(array $data): array
58
    {
59
        if (empty($data['data'])) {
50✔
60
            return [];
22✔
61
        }
62

63
        $components = [];
30✔
64

65
        $data = $this->legacyPageFieldSupport($data);
30✔
66

67
        $rawComponents = $this->parseComponentJSON($data);
30✔
68

69
        $promos = $this->getPromos($rawComponents, $data['site']['id'] ?? '');
30✔
70

71
        $components = $this->configureComponents($rawComponents, $promos, $data);
30✔
72

73
        $components = $this->componentClasses($components);
30✔
74

75
        $components = $this->componentStyles($components);
30✔
76

77
        return $components;
30✔
78
    }
79

80
    /**
81
     * {@inheritdoc}
82
     */
83
    public function cleanComponentJSON($componentJSON): string
84
    {
85
        // Remove all spaces and line breaks
86
        $componentJSON = preg_replace('/\s*\R\s*/', '', $componentJSON);
27✔
87

88
        // Remove trailing comma
89
        $componentJSON = preg_replace('(,})', '}', $componentJSON);
27✔
90

91
        return $componentJSON;
27✔
92
    }
93

94
    /**
95
     * {@inheritdoc}
96
     */
97
    public function parseComponentJSON(array $data)
98
    {
99
        // Get data to send through parsePromos
100
        // Preserve component data
101
        $components = [];
30✔
102
        $group_reference = [];
30✔
103
        $group_config = [];
30✔
104

105
        foreach ($data['data'] as $page_field_label => $page_field_data) {
30✔
106
            if (Str::startsWith($page_field_label, 'modular-')) {
30✔
107

108
                // Match component name to filename
109
                $name = Str::replaceFirst('modular-', '', $page_field_label);
26✔
110

111
                // Remove spaces, breaks, trailing commas
112
                $page_field_data = $this->cleanComponentJSON($page_field_data);
26✔
113

114
                // Make sure this is JSON data
115
                if (Str::startsWith($page_field_data, '{')) {
26✔
116

117
                    // Turn JSON into a PHP array
118
                    $components[$name] = json_decode($page_field_data, true);
25✔
119

120
                    // Set up expected parsePromos group_config
121
                    if (!empty($components[$name]['config'])) {
25✔
122
                        $promo_config = explode('|', $components[$name]['config']);
8✔
123

124
                        // Add youtube
125
                        if (strpos($components[$name]['config'], 'youtube') === false) {
8✔
126
                            array_push($promo_config, 'youtube');
8✔
127
                        }
128

129
                        foreach ($promo_config as $key => $config_item) {
8✔
130
                            // Inject page_id
131
                            if (Str::startsWith($config_item, 'page_id')) {
8✔
132
                                $promo_config[$key] = 'page_id:'.$data['page']['id'];
3✔
133
                            }
134
                            // Component loop expects the return being a multi-dimensional array
135
                            if (Str::startsWith($config_item, 'first')) {
8✔
136
                                unset($promo_config[$key]);
4✔
137
                            }
138
                        }
139

140
                        // Poof! Concatenate config to use with parsePromos
141
                        $components[$name]['config'] = implode('|', $promo_config);
8✔
142
                    }
143

144
                    // Remove integers from page fields to match the component filename
145
                    $components[$name]['filename'] = preg_replace('/-\d+$/', '', $name);
25✔
146
                } else {
147
                    // If only an ID is provided without JSON, use it
148
                    $components[$name]['id'] = (int)$page_field_data;
1✔
149

150
                    // Remove integers from page fields to match the component filename
151
                    $components[$name]['filename'] = preg_replace('/-\d+$/', '', $name);
1✔
152
                }
153

154
                // Build the parsePromos group_reference and group config
155
                // Exclude news and events app IDs
156
                if (!Str::contains($name, ['events', 'news']) && !empty($components[$name]['id'])) {
26✔
157
                    $group_reference[$components[$name]['id']] = $name;
14✔
158
                    if (!empty($components[$name]['config'])) {
14✔
159
                        $group_config[$name] = $components[$name]['config'];
8✔
160
                    }
161
                }
162
            }
163
        }
164

165
        return [
30✔
166
            'components' => $components,
30✔
167
            'group_reference' => $group_reference,
30✔
168
            'group_config' => $group_config,
30✔
169
        ];
30✔
170
    }
171

172
    /**
173
     * {@inheritdoc}
174
     */
175
    public function getPromos($components, $site_id)
176
    {
177
        $params = [
30✔
178
            'method' => 'cms.promotions.listing',
30✔
179
            'promo_group_id' => array_keys($components['group_reference']),
30✔
180
            'filename_url' => true,
30✔
181
            'is_active' => '1',
30✔
182
        ];
30✔
183

184
        $promos = $this->cache->remember($params['method'] . md5(serialize($params)), config('cache.ttl'), function () use ($params) {
30✔
185
            return $this->wsuApi->sendRequest($params['method'], $params);
30✔
186
        });
30✔
187

188
        // Use another site's promo items
189
        if (!empty($promos['promotions'])) {
30✔
190
            $promos['promotions'] = collect($promos['promotions'])->map(function ($promo) use ($site_id) {
13✔
191
                if (!empty($promo['site']) && !empty($promo['site']['site_id']) && $promo['site']['site_id'] != $site_id) {
13✔
192
                    if (!empty($promo['filename_url'])) {
1✔
193
                        $promo['relative_url'] = $promo['filename_url'];
1✔
194
                    }
195

196
                    if (!empty($promo['secondary_filename_url'])) {
1✔
197
                        $promo['secondary_relative_url'] = $promo['secondary_filename_url'];
1✔
198
                    }
199

200
                    if (!empty($promo['link'])) {
1✔
201
                        $promo['link'] = $this->externalPromoLink($promo['link'], $promo['site']);
1✔
202
                    }
203
                }
204

205
                return $promo;
13✔
206
            })->toArray();
13✔
207
        }
208
        $promos = $this->parsePromos->parse($promos, $components['group_reference'], $components['group_config']);
30✔
209

210
        foreach ($promos as $name => $data) {
30✔
211
            // Adjust promo data
212
            $data = collect($data)->map(function ($item) use ($components, $name) {
14✔
213
                return $this->adjustPromoData($item, $components['components'][$name]);
13✔
214
            })->toArray();
14✔
215

216
            // Organize by option
217
            if (!empty($components['components'][$name]['groupByOptions']) && $components['components'][$name]['groupByOptions'] === true && Str::startsWith($name, 'catalog')) {
14✔
218
                $data = $this->organizePromoItemsByOption($data);
1✔
219
            }
220

221
            // Build the return
222
            $promos[$name] = [
14✔
223
                'data' => $data,
14✔
224
                'component' => $components['components'][$name],
14✔
225
            ];
14✔
226
        }
227

228
        return $promos;
30✔
229
    }
230

231
    public function externalPromoLink(string $link, array $site): string
232
    {
233
        if ($this->isAbsolutePromoLink($link)) {
1✔
NEW
234
            return $link;
×
235
        }
236

237
        $site_url = $this->promoSiteUrl($site);
1✔
238

239
        if (empty($site_url)) {
1✔
NEW
240
            return $link;
×
241
        }
242

243
        return rtrim($site_url, '/').'/'.ltrim($link, '/');
1✔
244
    }
245

246
    protected function isAbsolutePromoLink(string $link): bool
247
    {
248
        return Str::startsWith($link, ['#', '//']) || parse_url($link, PHP_URL_SCHEME) !== null;
1✔
249
    }
250

251
    protected function promoSiteUrl(array $site): ?string
252
    {
253
        $site_url = $site['url'] ?? $site['domain'] ?? $site['hostname'] ?? $site['host'] ?? null;
1✔
254

255
        if (empty($site_url)) {
1✔
NEW
UNCOV
256
            return null;
×
257
        }
258

259
        if (Str::startsWith($site_url, '//')) {
1✔
NEW
UNCOV
260
            return 'https:'.$site_url;
×
261
        }
262

263
        if (parse_url($site_url, PHP_URL_SCHEME) === null) {
1✔
NEW
UNCOV
264
            return 'https://'.$site_url;
×
265
        }
266

267
        return $site_url;
1✔
268
    }
269

270
    /**
271
     * {@inheritdoc}
272
     */
273
    public function configureComponents(array $components, array $promos, array $base): array
274
    {
275
        $modularComponents = [];
30✔
276

277
        foreach ($components['components'] as $name => $component) {
30✔
278
            if (Str::contains($name, 'events') || Str::contains($name, 'news')) {
26✔
279
                if (Str::contains($name, 'events')) {
10✔
280
                    $components['components'][$name]['id'] = $component['events_id'] ?? $component['id'] ?? $base['site']['id'];
8✔
281

282
                    // Restrict events row to 3 items
283
                    if (Str::contains($name, 'events-row') && !Str::contains($name, 'featured-events-row')) {
8✔
284
                        $limit = $components['components'][$name]['limit'] ?? 3;
4✔
285
                    } else {
286
                        $limit = $components['components'][$name]['limit'] ?? 4;
4✔
287
                    }
288

289
                    // Use full listing if the name contains featured, or events-row
290
                    // TODO Find better naming convention
291
                    if (!Str::contains($name, 'featured') && (Str::contains($name, 'column') || Str::contains($name, 'news-and-events'))) {
8✔
292
                        $events = $this->event->getEvents(
7✔
293
                            $components['components'][$name]['id'] ?? $base['site']['id'],
7✔
294
                            $limit,
7✔
295
                            audience_id: $components['components'][$name]['audience_id'] ?? null,
7✔
296
                            is_featured: $components['components'][$name]['is_featured'] ?? null,
7✔
297
                            featured_images_only: $components['components'][$name]['featured_images_only'] ?? null,
7✔
298
                        );
7✔
299
                    } else {
300
                        $events = $this->event->getEventsFullListing(
1✔
301
                            $components['components'][$name]['id'] ?? $base['site']['id'],
1✔
302
                            $limit,
1✔
303
                            audience_id: $components['components'][$name]['audience_id'] ?? null,
1✔
304
                            is_featured: $components['components'][$name]['is_featured'] ?? null,
1✔
305
                            featured_images_only: $components['components'][$name]['featured_images_only'] ?? null,
1✔
306
                        );
1✔
307
                    }
308

309
                    // Special data structure for news-and-events component
310
                    if (Str::contains($name, 'news-and-events')) {
8✔
311
                        $modularComponents[$name]['data']['events'] = $events['events'] ?? [];
4✔
312
                    } else {
313
                        $modularComponents[$name]['data'] = $events['events'] ?? [];
4✔
314
                    }
315

316
                    // Provide a default Events heading
317
                    if (!array_key_exists('heading', $component)) {
8✔
318
                        $components['components'][$name]['heading'] = 'Events';
8✔
319
                    }
320

321
                    // Assign the component data
322
                    $modularComponents[$name]['component'] = $components['components'][$name];
8✔
323

324
                    // Set featured events default columns
325
                    if (Str::contains($name, 'events-featured-row')) {
8✔
326
                        if (empty($components['components'][$name]['columns'])) {
1✔
327
                            $modularComponents[$name]['component']['columns'] = 4;
1✔
328
                        }
329
                    }
330

331
                    // Set the calendar link
332
                    if (empty($modularComponents[$name]['component']['cal_name']) && !empty($base['site']['events']['path'])) {
8✔
333
                        $modularComponents[$name]['component']['cal_name'] = $base['site']['events']['path'];
4✔
334
                    }
335
                }
336
                if (Str::contains($name, 'news')) {
10✔
337
                    $components['components'][$name]['id'] = $component['news_id'] ?? $component['id'] ?? $base['site']['news']['application_id'];
6✔
338
                    $limit = $component['limit'] ?? 4;
6✔
339

340
                    // Set the news route
341
                    $components['components'][$name]['news_route'] = $component['news_route'] ?? config('base.news_listing_route');
6✔
342

343
                    // Use featured news
344
                    if (!empty($component['featured']) && $component['featured'] === true) {
6✔
345
                        $articles = $this->article->listing($components['components'][$name]['id'], 50, 1, $component['topics'] ?? []);
1✔
346
                        $articles['articles']['data'] = collect($articles['articles']['data'])->filter(function ($article) {
1✔
347
                            return !empty($article['featured']['featured']) && $article['featured']['featured'] === 1;
1✔
348
                        })->take($limit)->toArray();
1✔
349
                    } else {
350
                        $articles = $this->article->listing($components['components'][$name]['id'], $limit, 1, $component['topics'] ?? []);
5✔
351
                    }
352

353
                    // Special data structure for news-and-events component
354
                    if (Str::contains($name, 'news-and-events')) {
6✔
355
                        $modularComponents[$name]['data']['news'] = $articles['articles']['data'] ?? [];
4✔
356
                    } else {
357
                        $modularComponents[$name]['data'] = $articles['articles']['data'] ?? [];
2✔
358
                    }
359

360
                    // Provide a default News heading
361
                    if (!array_key_exists('heading', $component)) {
6✔
362
                        $components['components'][$name]['heading'] = 'News';
6✔
363
                    }
364

365
                    // Assign the component data
366
                    $modularComponents[$name]['component'] = $components['components'][$name];
6✔
367
                    $modularComponents[$name]['meta'] = $articles['articles']['meta'] ?? [];
6✔
368

369
                    if (Str::startsWith($name, 'news-and-events')) {
6✔
370
                        // Clear any set heading
371
                        // Headings are set in the component blade
372
                        $modularComponents[$name]['component']['heading'] = '';
4✔
373
                    }
374
                }
375
            } elseif (Str::startsWith($name, 'page-content') || Str::startsWith($name, 'heading') || Str::contains($name, 'page-config')) {
17✔
376
                //} elseif (Str::startsWith($name, ['page-content', 'heading', 'layout'])) {
377
                // If there's JSON but no news, events or promo data, assign the component array as data
378
                // Page-content and heading components
379
                $modularComponents[$name]['data'][] = $components['components'][$name] ?? [];
2✔
380
                $modularComponents[$name]['component'] = $components['components'][$name] ?? [];
2✔
381
                unset($modularComponents[$name]['component']['heading']);
2✔
382
            } else {
383
                $modularComponents[$name]['data'] = $promos[$name]['data'] ?? [];
15✔
384
                $modularComponents[$name]['component'] = $promos[$name]['component'] ?? [];
15✔
385
            }
386
        }
387

388
        return $modularComponents;
30✔
389
    }
390

391
    public function adjustPromoData($data, $component)
392
    {
393
        if (isset($component['singlePromoView']) && $component['singlePromoView'] === true) {
13✔
394
            $data['link'] = 'view/'.Str::slug($data['title']).'-'.$data['promo_item_id'];
4✔
395
        }
396

397
        if (isset($component['showExcerpt']) && $component['showExcerpt'] === false) {
13✔
398
            unset($data['excerpt']);
1✔
399
        }
400

401
        if (isset($component['showDescription']) && $component['showDescription'] === false) {
13✔
402
            unset($data['description']);
1✔
403
        }
404

405
        if (isset($component['showTitle']) && $component['showTitle'] === false) {
13✔
406
            $data['title'] = '';
1✔
407
        }
408

409
        // Override promo item option with component option
410
        if (isset($component['option'])) {
13✔
411
            $data['option'] = $component['option'];
1✔
412
        }
413

414
        return $data;
13✔
415
    }
416

417
    /**
418
    * {@inheritdoc}
419
    */
420
    public function organizePromoItemsByOption(array $data)
421
    {
422
        $options_exist = collect($data)->filter(function ($item) {
3✔
423
            return !empty($item['option']);
2✔
424
        })->isNotEmpty();
3✔
425

426
        if ($options_exist === true) {
3✔
427
            $data = collect($data)->groupBy('option')->toArray();
2✔
428

429
            if (!empty($data[''])) {
2✔
430
                $no_option_moved_to_bottom = $data[''];
2✔
431
                unset($data['']);
2✔
432
                $data[''] = $no_option_moved_to_bottom;
2✔
433
            }
434
        }
435

436
        return $data;
3✔
437
    }
438

439
    public function componentClasses($components)
440
    {
441
        foreach ($components as $componentName => $component) {
35✔
442
            // Establishing final arrays so they will always exist
443
            $components[$componentName]['component']['containerClass'] = $component['component']['containerClass'] ?? [];
31✔
444
            $components[$componentName]['component']['backgroundClass'] = $component['component']['backgroundClass'] ?? [];
31✔
445
            $components[$componentName]['component']['componentClass'] = $component['component']['componentClass'] ?? [];
31✔
446

447
            // containerClass => filename
448
            if (!empty($component['component']['filename'])) {
31✔
449
                $components[$componentName]['component']['containerClass'][] = $component['component']['filename'];
30✔
450
            }
451

452
            // containerClass => columnSpan
453
            if (!empty($component['component']['filename']) && $component['component']['filename'] != 'hero') {
31✔
454
                if (!empty($component['component']['columnSpan'])) {
29✔
455
                    // Inject the column span class
456
                    array_push($components[$componentName]['component']['containerClass'], 'px-4', 'mt:colspan-'.$component['component']['columnSpan']);
2✔
457
                } elseif (!empty($component['component']['filename']) && strpos($component['component']['filename'], 'column') !== false) {
29✔
458
                    // Inject the column span class
459
                    array_push($components[$componentName]['component']['containerClass'], 'px-4', 'mt:colspan-6');
12✔
460
                } else {
461
                    // Default width
462
                    $components[$componentName]['component']['containerClass'][] = 'px-container';
24✔
463
                }
464
            }
465

466
            // Collect all legacy class names
467
            $classes = trim(($component['component']['sectionClass'] ?? '').' '.($component['component']['componentClass'] ?? '').' '.($component['component']['classes'] ?? ''));
31✔
468

469
            // Group the classes based on the container they will be applied to
470
            // Set backgroundClass, containerClass, componentClass
471
            if ($classes !== '') {
31✔
472
                $classes = explode(' ', $classes);
4✔
473

474
                foreach ($classes as $class) {
4✔
475
                    if (strpos($class, 'bg-') !== false) {
4✔
476
                        // backgroundClass
477
                        $components[$componentName]['component']['backgroundClass'][] = $class;
2✔
478
                    } elseif (
479
                        strpos($class, 'my-') !== false |
4✔
480
                        strpos($class, 'mt-') !== false |
4✔
481
                        strpos($class, 'mb-') !== false |
4✔
482
                        strpos($class, 'end') !== false |
4✔
483
                        strpos($class, 'left') !== false |
4✔
484
                        strpos($class, 'right') !== false |
4✔
485
                        strpos($class, 'order-') !== false
4✔
486
                    ) {
487
                        // containerClass
488
                        $components[$componentName]['component']['containerClass'][] = $class;
4✔
489
                    } else {
490
                        // componentClass
491
                        $components[$componentName]['component']['componentClass'][] = $class;
2✔
492
                    }
493
                }
494
            }
495

496
            // Default background image positioning classes, won't overwrite existing backgroundClass values
497
            if (!empty($component['component']['backgroundImageUrl']) && empty($component['component']['backgroundClass'])) {
31✔
498
                $components[$componentName]['component']['backgroundClass'] = ['bg-cover', 'bg-top'];
2✔
499
            }
500

501
            // Section gutters, bottom padding
502
            // - No gutter if component uses margin-bottom class
503
            // - No gutter on heading component
504
            if (empty(preg_grep('/mb-/', $components[$componentName]['component']['containerClass']))
31✔
505
                && !Str::contains($componentName, 'heading') && !Str::contains($componentName, 'hero')
31✔
506
            ) {
507
                $components[$componentName]['component']['containerClass'][] = 'mb-gutter';
30✔
508
            }
509

510
            // Implode party, assign all classes to their respective container
511
            $components[$componentName]['component']['containerClass'] = implode(' ', $components[$componentName]['component']['containerClass']);
31✔
512
            $components[$componentName]['component']['backgroundClass'] = implode(' ', $components[$componentName]['component']['backgroundClass']);
31✔
513
            $components[$componentName]['component']['componentClass'] = implode(' ', $components[$componentName]['component']['componentClass']);
31✔
514
        }
515

516
        return $components;
35✔
517
    }
518

519
    public function componentStyles($components)
520
    {
521
        $expected_styles = [
35✔
522
            'backgroundImageUrl',
35✔
523
            //'sectionStyle',
35✔
524
        ];
35✔
525

526
        foreach ($components as $componentName => $component) {
35✔
527
            if (!empty($component['component']['backgroundImageUrl'])) {
31✔
528
                //$component['component']['backgroundImageUrl'] = "background-image:url('".$component['component']['backgroundImageUrl']."');";
529
                $components[$componentName]['component']['backgroundImageUrl'] = "style=\"background-image:url('".$component['component']['backgroundImageUrl']."');\"";
2✔
530
            }
531

532
            // Forcing a space delimeter
533
            foreach ($component['component'] as $option => $style) {
31✔
534
                if (in_array($option, $expected_styles)) {
31✔
535
                    $styles[$componentName][] = $style;
2✔
536
                    //$components[$componentName]['component']['componentStyle'] = "style=\"".implode(' ', $styles[$componentName])."\"";
537
                }
538
            }
539
        }
540

541
        return $components;
35✔
542
    }
543

544
    /**
545
     * {@inheritdoc}
546
     */
547
    public function legacyPageFieldSupport(array $data)
548
    {
549
        // Legacy support for accordion
550
        if (!empty($data['data']['accordion_promo_group_id'])) {
30✔
551
            $data['data']['modular-accordion-999'] = json_encode([
1✔
552
                'id' => $data['data']['accordion_promo_group_id']
1✔
553
            ]);
1✔
554
        }
555

556
        // Legacy support for listing
557
        if (!empty($data['data']['listing_promo_group_id'])) {
30✔
558
            if (!empty($data['data']['promotion_view_boolean']) && $data['data']['promotion_view_boolean'] === "true") {
2✔
559
                $data['data']['modular-catalog-998'] = json_encode([
1✔
560
                    'id' => $data['data']['listing_promo_group_id'],
1✔
561
                    'columns' => 1,
1✔
562
                    'singlePromoView' => true
1✔
563
                ]);
1✔
564
            } else {
565
                $data['data']['modular-catalog-998'] = json_encode([
1✔
566
                    'id' => $data['data']['listing_promo_group_id'],
1✔
567
                    'columns' => 1
1✔
568
                ]);
1✔
569
            }
570
        }
571

572
        // Legacy support for grid
573
        if (!empty($data['data']['grid_promo_group_id'])) {
30✔
574
            if (!empty($data['data']['promotion_view_boolean']) && $data['data']['promotion_view_boolean'] === "true") {
2✔
575
                $data['data']['modular-catalog-999'] = json_encode([
1✔
576
                    'id' => $data['data']['grid_promo_group_id'],
1✔
577
                    'columns' => 3,
1✔
578
                    'singlePromoView' => true
1✔
579
                ]);
1✔
580
            } else {
581
                $data['data']['modular-catalog-999'] = json_encode([
1✔
582
                    'id' => $data['data']['grid_promo_group_id'],
1✔
583
                    'columns' => 3
1✔
584
                ]);
1✔
585
            }
586
        }
587

588
        return $data;
30✔
589
    }
590
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc