Issues (3099)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

Command/KunstmaanGenerateCommand.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Kunstmaan\GeneratorBundle\Command;
4
5
use Kunstmaan\AdminBundle\Form\WysiwygType;
6
use Kunstmaan\GeneratorBundle\Helper\CommandAssistant;
7
use Kunstmaan\GeneratorBundle\Helper\GeneratorUtils;
8
use Kunstmaan\GeneratorBundle\Helper\Sf4AppBundle;
9
use Kunstmaan\MediaBundle\Form\Type\MediaType;
10
use Kunstmaan\NodeBundle\Form\Type\URLChooserType;
11
use Sensio\Bundle\GeneratorBundle\Command\GenerateDoctrineCommand;
12
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
13
use Symfony\Component\Console\Input\InputInterface;
14
use Symfony\Component\Console\Output\OutputInterface;
15
use Symfony\Component\DependencyInjection\Container;
16
use Symfony\Component\Finder\Finder;
17
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
18
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
19
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
20
use Symfony\Component\Form\Extension\Core\Type\NumberType;
21
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
22
use Symfony\Component\Form\Extension\Core\Type\TextType;
23
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
24
use Symfony\Component\HttpKernel\Kernel;
25
use Symfony\Component\Yaml\Exception\ParseException;
26
use Symfony\Component\Yaml\Yaml;
27
28
abstract class KunstmaanGenerateCommand extends GenerateDoctrineCommand
29
{
30
    /**
31
     * @var CommandAssistant
32
     */
33
    protected $assistant;
34
35
    /**
36
     * Interacts with the user.
37
     *
38
     * @param InputInterface  $input  An InputInterface instance
39
     * @param OutputInterface $output An OutputInterface instance
40
     */
41
    protected function interact(InputInterface $input, OutputInterface $output)
42
    {
43
        $this->setInputAndOutput($input, $output);
44
45
        $welcomeText = $this->getWelcomeText();
46
        if (!empty($welcomeText)) {
47
            $this->assistant->writeSection($this->getWelcomeText());
48
        }
49
50
        $this->doInteract();
51
    }
52
53
    /**
54
     * @param InputInterface  $input
55
     * @param OutputInterface $output
56
     *
57
     * @return int|null
58
     */
59
    protected function execute(InputInterface $input, OutputInterface $output)
60
    {
61
        $this->setInputAndOutput($input, $output);
62
63
        return $this->doExecute();
64
    }
65
66
    /**
67
     * Create the CommandAssistant.
68
     *
69
     * @param InputInterface  $input
70
     * @param OutputInterface $output
71
     */
72 View Code Duplication
    private function setInputAndOutput(InputInterface $input, OutputInterface $output)
73
    {
74
        if (is_null($this->assistant)) {
75
            $this->assistant = new CommandAssistant();
76
            $this->assistant->setQuestionHelper($this->getQuestionHelper());
77
            $this->assistant->setKernel($this->getApplication()->getKernel());
78
        }
79
        $this->assistant->setOutput($output);
80
        $this->assistant->setInput($input);
81
    }
82
83
    /**
84
     * Do the interaction with the end user.
85
     */
86
    abstract protected function doInteract();
87
88
    /**
89
     * This function implements the final execution of the Generator.
90
     * It calls the execute function with the correct parameters.
91
     */
92
    abstract protected function doExecute();
93
94
    /**
95
     * The text to be displayed on top of the generator.
96
     *
97
     * @return string|array
98
     */
99
    abstract protected function getWelcomeText();
100
101
    /**
102
     * Get an array with all the bundles the user has created.
103
     *
104
     * @return array
105
     */
106
    protected function getOwnBundles()
107
    {
108
        $bundles = [];
109
        $counter = 1;
110
        $dir = dirname($this->getContainer()->getParameter('kernel.root_dir').'/').'/src/';
111
        $finder = new Finder();
112
        $finder->in($dir)->name('*Bundle.php');
113
114
        foreach ($finder as $file) {
115
            $bundles[$counter++] = [
116
                'name' => basename($file->getFilename(), '.php'),
117
                'namespace' => $file->getRelativePath(),
118
                'dir' => $file->getPath(),
119
            ];
120
        }
121
122
        return $bundles;
123
    }
124
125
    /**
126
     * Check that a bundle is available (loaded in AppKernel)
127
     *
128
     * @param string $bundleName
129
     *
130
     * @return bool
131
     */
132
    protected function isBundleAvailable($bundleName)
133
    {
134
        $allBundles = array_keys($this->assistant->getKernel()->getBundles());
135
136
        return in_array($bundleName, $allBundles);
137
    }
138
139
    /**
140
     * Asks for the prefix and sets it on the InputInterface as the 'prefix' option, if this option is not set yet.
141
     * Will set the default to a snake_cased namespace when the namespace has been set on the InputInterface.
142
     *
143
     * @param array  $text      What you want printed before the prefix is asked. If null is provided it'll write a default text.
144
     * @param string $namespace An optional namespace. If this is set it'll create the default based on this prefix.
145
     *                          If it's not provided it'll check if the InputInterface already has the namespace option.
146
     *
147
     * @return string The prefix. But it's also been set on the InputInterface.
148
     */
149
    protected function askForPrefix(array $text = null, $namespace = null)
150
    {
151
        $prefix = $this->assistant->getOptionOrDefault('prefix', null);
152
153
        if (is_null($text)) {
154
            $text = [
155
                'You can add a prefix to the table names of the generated entities for example: '.
156
                '<comment>projectname_bundlename_</comment>',
157
                'Enter an underscore \'_\' if you don\'t want a prefix.',
158
                '',
159
            ];
160
        }
161
162
        while (is_null($prefix)) {
163
            if (count($text) > 0) {
164
                $this->assistant->writeLine($text);
165
            }
166
167
            if (is_null($namespace) || empty($namespace)) {
168
                $namespace = $this->assistant->getOption('namespace');
169
            } else {
170
                $namespace = $this->fixNamespace($namespace);
171
            }
172
            $defaultPrefix = GeneratorUtils::cleanPrefix($this->convertNamespaceToSnakeCase($namespace));
173
            $prefix = GeneratorUtils::cleanPrefix($this->assistant->ask('Tablename prefix', $defaultPrefix));
174
175
            if ($prefix == '') {
176
                break;
177
            }
178
179
            $output = $this->assistant->getOutput();
180 View Code Duplication
            if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $prefix)) {
181
                $output->writeln(sprintf('<bg=red> "%s" contains invalid characters</>', $prefix));
182
                $prefix = $text = null;
183
184
                continue;
185
            }
186
187
            $this->assistant->setOption('prefix', $prefix);
188
        }
189
190
        return $prefix;
191
    }
192
193
    /**
194
     * Converts something like Namespace\BundleNameBundle to namespace_bundlenamebundle.
195
     *
196
     * @param string $namespace
197
     *
198
     * @return string
199
     */
200 View Code Duplication
    private function convertNamespaceToSnakeCase($namespace)
201
    {
202
        if (is_null($namespace)) {
203
            return null;
204
        }
205
206
        return str_replace('/', '_', strtolower($this->fixNamespace($namespace)));
207
    }
208
209
    /**
210
     * Replaces '\' with '/'.
211
     *
212
     * @param $namespace
213
     *
214
     * @return mixed
215
     */
216
    private function fixNamespace($namespace)
217
    {
218
        return str_replace('\\', '/', $namespace);
219
    }
220
221
    /**
222
     * Ask for which bundle we need to generate something. It there is only one custom bundle
223
     * created by the user, we don't ask anything and just use that bundle. If the user provided
224
     * a namespace as input option, we try to get that bundle first.
225
     *
226
     * @param string      $objectName          The thing we are going to create (pagepart, bundle, layout, ...)
227
     * @param string|null $namespace           The namespace provided as input option
228
     * @param string      $questionMoreBundles
229
     * @param string      $questionOneBundle
230
     *
231
     * @return BundleInterface
232
     */
233
    protected function askForBundleName(
234
        $objectName,
235
        $namespace = null,
236
        $questionMoreBundles = "\nIn which bundle do you want to create the %s",
237
        $questionOneBundle = "The %s will be created for the <comment>%s</comment> bundle.\n"
238
    ) {
239
        if (Kernel::VERSION_ID >= 40000) {
240
            return new Sf4AppBundle($this->getContainer()->getParameter('kernel.project_dir'));
241
        }
242
243
        $ownBundles = $this->getOwnBundles();
244
        if (count($ownBundles) <= 0) {
245
            $this->assistant->writeError("Looks like you don't have created any bundles for your project...", true);
246
        }
247
248
        // If the user provided the namespace as input option
249
        if (!is_null($namespace)) {
250
            foreach ($ownBundles as $key => $bundleInfo) {
251
                if (GeneratorUtils::fixNamespace($namespace) == GeneratorUtils::fixNamespace(
252
                        $bundleInfo['namespace']
253
                    )
254
                ) {
255
                    $bundleName = $bundleInfo['name'];
256
257
                    break;
258
                }
259
            }
260
261
            // When the provided namespace was not found, we show an error on the screen and ask the bundle again
262
            if (empty($bundleName)) {
263
                $this->assistant->writeError("The provided bundle namespace '$namespace' was not found...");
264
            }
265
        }
266
267
        if (empty($bundleName)) {
268
            // If we only have 1 bundle, we don't need to ask
269
            if (count($ownBundles) > 1) {
270
                $bundleSelect = [];
271
                foreach ($ownBundles as $key => $bundleInfo) {
272
                    $bundleSelect[$key] = $bundleInfo['name'];
273
                }
274
                $bundleId = $this->assistant->askSelect(
275
                    sprintf($questionMoreBundles, $objectName),
276
                    $bundleSelect
277
                );
278
                $bundleName = $ownBundles[$bundleId]['name'];
279
280
                $this->assistant->writeLine('');
281
            } else {
282
                $bundleName = $ownBundles[1]['name'];
283
                $this->assistant->writeLine(
284
                    [sprintf($questionOneBundle, $objectName, $bundleName)]
285
                );
286
            }
287
        }
288
289
        $bundle = $this->assistant->getKernel()->getBundle($bundleName);
290
291
        return $bundle;
292
    }
293
294
    /**
295
     * Ask the end user to select one (or more) section configuration(s).
296
     *
297
     * @param string          $question
298
     * @param BundleInterface $bundle
299
     * @param bool            $multiple
300
     * @param string|null     $context
301
     * @param array           $defaultSections
302
     *
303
     * @return array|null
304
     */
305
    protected function askForSections(
306
        $question,
307
        BundleInterface $bundle,
308
        $multiple = false,
309
        $context = null,
310
        $defaultSections = []
311
    ) {
312
        $allSections = $this->getAvailableSections($bundle, $context, $defaultSections);
313
        $sections = [];
314
315
        // If there are more options to choose from, we ask the end user
316
        if (count($allSections) > 0) {
317
            $sectionSelect = [];
318
            foreach ($allSections as $key => $sectionInfo) {
319
                $sectionSelect[$key] = $sectionInfo['name'].' ('.$sectionInfo['file'].')';
320
            }
321
            $this->assistant->writeLine('');
322
            $sectionIds = $this->assistant->askSelect($question, $sectionSelect, null, $multiple);
323
            if (is_array($sectionIds)) {
324
                foreach ($sectionIds as $id) {
325
                    $sections[] = $allSections[$id]['file'];
326
                }
327
            } else {
328
                $sections[] = $allSections[$sectionIds]['file'];
329
            }
330
        }
331
332
        if ($multiple) {
333
            return $sections;
334
        } else {
335
            return count($sections) > 0 ? $sections[0] : null;
336
        }
337
    }
338
339
    /**
340
     * Get an array with the available page sections. We also parse the yaml files to get more information about
341
     * the sections.
342
     *
343
     * @param BundleInterface $bundle          The bundle for which we want to get the section configuration
344
     * @param string|null     $context         If provided, only return configurations with this context
345
     * @param array           $defaultSections The default section configurations that are always available
346
     *
347
     * @return array
348
     */
349
    protected function getAvailableSections(BundleInterface $bundle, $context = null, $defaultSections = [])
350
    {
351
        $configs = [];
352
        $counter = 1;
353
354
        // Get the available sections from disc
355
        $dir = Kernel::VERSION_ID >= 40000 ?
356
            $this->getContainer()->getParameter('kernel.project_dir').'/config/kunstmaancms/pageparts/' :
357
            $bundle->getPath().'/Resources/config/pageparts/'
358
        ;
359
        if (file_exists($dir) && is_dir($dir)) {
360
            $finder = new Finder();
361
            $finder->files()->in($dir)->depth('== 0');
362
            foreach ($finder as $file) {
363
                $info = $this->getSectionInfo($dir, $file->getFileName());
364
365
                if (is_array($info) && (is_null($context) || $info['context'] == $context)) {
366
                    $configs[$counter++] = $info;
367
                    if (array_key_exists($info['file'], $defaultSections)) {
368
                        unset($defaultSections[$info['file']]);
369
                    }
370
                }
371
            }
372
        }
373
374
        // Add the default sections
375
        foreach ($defaultSections as $file => $info) {
376
            if (is_null($context) || $info['context'] == $context) {
377
                $configs[$counter++] = $info;
378
            }
379
        }
380
381
        return $configs;
382
    }
383
384
    /**
385
     * Get the information about a pagepart section configuration file.
386
     *
387
     * @param string $dir
388
     * @param string $file
389
     *
390
     * @return array|null
391
     */
392
    private function getSectionInfo($dir, $file)
393
    {
394
        $info = null;
395
396
        try {
397
            $data = Yaml::parse(file_get_contents($dir.$file));
398
399 View Code Duplication
            if (array_key_exists('kunstmaan_page_part', $data)) {
400
                //Get rid of the bundle config lines
401
                $data = array_values(array_values(array_values($data)[0])[0])[0];
402
            }
403
404
            $info = [
405
                'name' => $data['name'],
406
                'context' => $data['context'],
407
                'file' => $file,
408
                //'file_clean' => substr($file, 0, strlen($file)-4)
409
            ];
410
        } catch (ParseException $e) {
411
        }
412
413
        return $info;
414
    }
415
416
    /**
417
     * Get an array of fields that need to be added to the entity.
418
     *
419
     * @param BundleInterface $bundle
420
     * @param array           $reservedFields
421
     *
422
     * @return array
423
     */
424
    protected function askEntityFields(BundleInterface $bundle, array $reservedFields = ['id'])
425
    {
426
        $this->assistant->writeLine('<info>Available field types:</info> ');
427
        $typeSelect = $this->getTypes(true);
428
        foreach ($typeSelect as $type) {
429
            $this->assistant->writeLine(sprintf('<comment>- %s</comment>', $type));
430
        }
431
432
        $fields = [];
433
        $typeStrings = $this->getTypes();
434
        $mediaTypeSelect = $this->getMediaTypes();
435
        $generator = $this->getGenerator();
436
        $container = $this->getContainer();
437
438
        while (true) {
439
            $this->assistant->writeLine('');
440
441
            $fieldName = $this->assistant->askAndValidate(
442
                'New field name (press <return> to stop adding fields)',
443
                function ($name) use ($fields, $reservedFields, $generator) {
444
                    // The fields cannot exist in the reserved field list
445
                    if (in_array($name, $reservedFields)) {
446
                        throw new \InvalidArgumentException(sprintf('Field "%s" is already defined in the parent class', $name));
447
                    }
448
449
                    // The fields cannot exist already
450
                    if (isset($fields[$name])) {
451
                        throw new \InvalidArgumentException(sprintf('Field "%s" is already defined', $name));
452
                    }
453
454
                    // Check reserved words
455
                    if ($generator->isReservedKeyword($name)) {
456
                        throw new \InvalidArgumentException(sprintf('Name "%s" is a reserved word', $name));
457
                    }
458
459
                    // Only accept a-z
460
                    if (!preg_match('/^[a-zA-Z][a-zA-Z_0-9]+$/', $name) && $name != '') {
461
                        throw new \InvalidArgumentException(sprintf('Name "%s" is invalid', $name));
462
                    }
463
464
                    return $name;
465
                }
466
            );
467
468
            // When <return> is entered
469
            if (!$fieldName) {
470
                break;
471
            }
472
473
            $typeId = $this->assistant->askSelect('Field type', $typeSelect);
474
475
            // If single -or multipe entity reference in chosen, we need to ask for the entity name
476
            if (in_array($typeStrings[$typeId], ['single_ref', 'multi_ref'])) {
477
                $bundleName = $bundle->getName();
478
                $egName = $this->isSymfony4() ? 'App\Entity\FaqItem' : "$bundleName:FaqItem, $bundleName:Blog/Comment";
479
                $question = sprintf('Reference entity name (eg. %s)', $egName);
480
                $name = $this->assistant->askAndValidate(
481
                    $question,
482
                    function ($name) use ($generator, $container) {
483
                        /*
484
                         * Replace slash to backslash. Eg: CmsBundle:Blog/Comment --> CmsBundle:Blog\Comment
485
                         *
486
                         * @see \Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory::getMetadataFor()
487
                         * @see \Doctrine\ORM\Mapping\ClassMetadataFactory::getFqcnFromAlias()
488
                         */
489
                        if (!$this->isSymfony4()) {
490
                            $name = strtr($name, '/', '\\');
491
492
                            $parts = explode(':', $name);
493
494
                            // Should contain colon
495
                            if (count($parts) != 2) {
496
                                throw new \InvalidArgumentException(sprintf('"%s" is an invalid entity name', $name));
497
                            }
498
                        } else {
499
                            $parts = explode('\\', $name);
500
                        }
501
502
                        // Check reserved words
503
                        if ($generator->isReservedKeyword(end($parts))) {
504
                            throw new \InvalidArgumentException(sprintf('"%s" contains a reserved word', $name));
505
                        }
506
507
                        $em = $container->get('doctrine')->getManager();
508
509
                        try {
510
                            $em->getClassMetadata($name);
511
                        } catch (\Exception $e) {
512
                            throw new \InvalidArgumentException(sprintf('Entity "%s" not found', $name));
513
                        }
514
515
                        return $name;
516
                    },
517
                    null,
518
                    [$bundleName]
519
                );
520
521
                $extra = $name;
522
            } else {
523
                $extra = null;
524
            }
525
526
            // If image type, force image media filter
527
            if ($typeStrings[$typeId] == 'image') {
528
                $extra = 'image';
529
            }
530
531
            // If media type, ask for media filter
532
            if ($typeStrings[$typeId] == 'media') {
533
                $mediaTypeId = $this->assistant->askSelect('Media filter', $mediaTypeSelect);
534
                $extra = strtolower($mediaTypeSelect[$mediaTypeId]);
535
            }
536
537
            if ($typeStrings[$typeId] == 'image' || $typeStrings[$typeId] == 'media') {
538
                // Ask the allowed mimetypes for the media ojbect
539
                $mimeTypes = $this->assistant->ask('Do you want to limit the possible file types? Then specify a comma-seperated list of types (example: image/png,image/svg+xml), otherwise press ENTER',
540
                    null
541
                );
542
                if (isset($mimeTypes)) {
543
                    $mimeTypes = explode(',', $mimeTypes);
544
                }
545
                $data = [
546
                    'name' => $fieldName,
547
                    'type' => $typeStrings[$typeId],
548
                    'extra' => $extra,
549
                    'mimeTypes' => $mimeTypes,
550
                    'minHeight' => null,
551
                    'maxHeight' => null,
552
                    'minWidth' => null,
553
                    'maxWidth' => null,
554
                ];
555
556
                if ($extra == 'image') {
557
                    $minHeight = $maxHeight = $minWidth = $maxWidth = null;
558
                    if ($this->assistant->askConfirmation('Do you want to add validation of the dimensions of the media object? (y/n)',
559
                        'n',
560
                        '?',
561
                        false
562
                    )) {
563
                        // Ask the minimum height allowed for the image
564
                        $lengthValidation = function ($length) {
565
                            if ((is_numeric($length) && $length < 0) || (!is_numeric($length) && !empty($length))) {
566
                                throw new \InvalidArgumentException(sprintf('"%s" is not a valid length', $length));
567
                            } else {
568
                                return $length;
569
                            }
570
                        };
571
572
                        $minHeight = $this->assistant->askAndValidate('What is the minimum height for the media object? (in pixels)',
573
                            $lengthValidation
574
                        );
575
576
                        // Ask the maximum height allowed for the image
577
                        $maxHeight = $this->assistant->askAndValidate('What is the maximum height for the media object? (in pixels)',
578
                            $lengthValidation
579
                        );
580
581
                        // Ask the minimum width allowed for the image
582
                        $minWidth = $this->assistant->askAndValidate('What is the minimum width for the media object? (in pixels)',
583
                            $lengthValidation
584
                        );
585
586
                        //Ask the maximum width allowed for the image
587
                        $maxWidth = $this->assistant->askAndValidate('What is the maximum width for the media object? (in pixels)',
588
                            $lengthValidation
589
                        );
590
                    }
591
                    $data = [
592
                        'name' => $fieldName,
593
                        'type' => 'image',
594
                        'extra' => $extra,
595
                        'minHeight' => $minHeight,
596
                        'maxHeight' => $maxHeight,
597
                        'minWidth' => $minWidth,
598
                        'maxWidth' => $maxWidth,
599
                        'mimeTypes' => $mimeTypes,
600
                    ];
601
                }
602
            } else {
603
                $data = [
604
                    'name' => $fieldName,
605
                    'type' => $typeStrings[$typeId],
606
                    'extra' => $extra,
607
                    'minHeight' => null,
608
                    'maxHeight' => null,
609
                    'minWidth' => null,
610
                    'maxWidth' => null,
611
                    'mimeTypes' => null,
612
                ];
613
            }
614
615
            $fields[$fieldName] = $data;
616
        }
617
618
        return $fields;
619
    }
620
621
    /**
622
     * Get all the available types.
623
     *
624
     * @param bool $niceNames
625
     *
626
     * @return array
627
     */
628
    private function getTypes($niceNames = false)
629
    {
630
        $counter = 1;
631
632
        $types = [];
633
        $types[$counter++] = $niceNames ? 'Single line text' : 'single_line';
634
        $types[$counter++] = $niceNames ? 'Multi line text' : 'multi_line';
635
        $types[$counter++] = $niceNames ? 'Wysiwyg' : 'wysiwyg';
636
        $types[$counter++] = $niceNames ? 'Link (url, text, new window)' : 'link';
637
        if ($this->isBundleAvailable('KunstmaanMediaPagePartBundle')) {
638
            $types[$counter++] = $niceNames ? 'Image (media, alt text)' : 'image';
639
            $types[$counter++] = $niceNames ? 'Media (File or Video or Slideshow)' : 'media';
640
        }
641
        $types[$counter++] = $niceNames ? 'Single entity reference' : 'single_ref';
642
        $types[$counter++] = $niceNames ? 'Multi entity reference' : 'multi_ref';
643
        $types[$counter++] = $niceNames ? 'Boolean' : 'boolean';
644
        $types[$counter++] = $niceNames ? 'Integer' : 'integer';
645
        $types[$counter++] = $niceNames ? 'Decimal number' : 'decimal';
646
        $types[$counter++] = $niceNames ? 'DateTime' : 'datetime';
647
648
        return $types;
649
    }
650
651
    /**
652
     * Get all available media types.
653
     *
654
     * @return array
655
     */
656
    private function getMediaTypes()
657
    {
658
        $counter = 1;
659
660
        $types = [];
661
        $types[$counter++] = 'None';
662
        $types[$counter++] = 'File';
663
        $types[$counter++] = 'Image';
664
        $types[$counter++] = 'Video';
665
666
        return $types;
667
    }
668
669
    /**
670
     * Get all the entity fields for a specific type.
671
     *
672
     * @param BundleInterface $bundle
673
     * @param                 $objectName
674
     * @param                 $prefix
675
     * @param                 $name
676
     * @param                 $type
677
     * @param null            $extra
678
     * @param bool            $allNullable
679
     * @param null            $minHeight
680
     * @param null            $maxHeight
681
     * @param null            $minWidth
682
     * @param null            $maxWidth
683
     * @param null            $mimeTypes
684
     *
685
     * @return array
686
     */
687
    protected function getEntityFields(
688
        BundleInterface $bundle,
689
        $objectName,
690
        $prefix,
691
        $name,
692
        $type,
693
        $extra = null,
694
        $allNullable = false,
695
        $minHeight = null,
696
        $maxHeight = null,
697
        $minWidth = null,
698
        $maxWidth = null,
699
        $mimeTypes = null
700
    ) {
701
        $fields = [];
702
        switch ($type) {
703 View Code Duplication
            case 'single_line':
704
                $fields[$type][] = [
705
                    'fieldName' => lcfirst(Container::camelize($name)),
706
                    'type' => 'string',
707
                    'length' => '255',
708
                    'formType' => TextType::class,
709
                    'nullable' => $allNullable,
710
                ];
711
712
                break;
713 View Code Duplication
            case 'multi_line':
714
                $fields[$type][] = [
715
                    'fieldName' => lcfirst(Container::camelize($name)),
716
                    'type' => 'text',
717
                    'formType' => TextareaType::class,
718
                    'nullable' => $allNullable,
719
                ];
720
721
                break;
722 View Code Duplication
            case 'wysiwyg':
0 ignored issues
show
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...
723
                $fields[$type][] = [
724
                    'fieldName' => lcfirst(Container::camelize($name)),
725
                    'type' => 'text',
726
                    'formType' => WysiwygType::class,
727
                    'nullable' => $allNullable,
728
                ];
729
730
                break;
731
            case 'link':
732
                foreach (['url', 'text'] as $subField) {
733
                    $fields[$type][$subField] = [
734
                        'fieldName' => lcfirst(Container::camelize($name.'_'.$subField)),
735
                        'type' => 'string',
736
                        'formType' => $subField == 'url' ? URLChooserType::class : TextType::class,
737
                        'nullable' => $allNullable,
738
                    ];
739
                }
740
                $fields[$type]['new_window'] = [
741
                    'fieldName' => lcfirst(Container::camelize($name.'_new_window')),
742
                    'type' => 'boolean',
743
                    'nullable' => true,
744
                    'formType' => CheckboxType::class,
745
                ];
746
747
                break;
748
            case 'image':
749
                $fields[$type]['image'] = [
750
                    'fieldName' => lcfirst(Container::camelize($name)),
751
                    'type' => 'image',
752
                    'formType' => MediaType::class,
753
                    'mediaType' => $extra,
754
                    'minHeight' => $minHeight,
755
                    'maxHeight' => $maxHeight,
756
                    'minWidth' => $minWidth,
757
                    'maxWidth' => $maxWidth,
758
                    'mimeTypes' => $mimeTypes,
759
                    'targetEntity' => 'Kunstmaan\MediaBundle\Entity\Media',
760
                    'joinColumn' => [
761
                        'name' => str_replace('.', '_', Container::underscore($name.'_id')),
762
                        'referencedColumnName' => 'id',
763
                    ],
764
                    'nullable' => $allNullable,
765
                ];
766
                $fields[$type]['alt_text'] = [
767
                    'fieldName' => lcfirst(Container::camelize($name.'_alt_text')),
768
                    'type' => 'text',
769
                    'nullable' => true,
770
                    'formType' => TextType::class,
771
                ];
772
773
                break;
774
            case 'media':
775
                $fields[$type][] = [
776
                    'fieldName' => lcfirst(Container::camelize($name)),
777
                    'type' => 'media',
778
                    'formType' => MediaType::class,
779
                    'mediaType' => $extra,
780
                    'mimeTypes' => $mimeTypes,
781
                    'targetEntity' => 'Kunstmaan\MediaBundle\Entity\Media',
782
                    'joinColumn' => [
783
                        'name' => str_replace('.', '_', Container::underscore($name.'_id')),
784
                        'referencedColumnName' => 'id',
785
                    ],
786
                    'nullable' => $allNullable,
787
                ];
788
789
                break;
790
            case 'single_ref':
791
                $em = $this->getContainer()->get('doctrine')->getManager();
792
                $entityName = $em->getClassMetadata($extra)->getName();
793
                $fields[$type][] = [
794
                    'fieldName' => lcfirst(Container::camelize($name)),
795
                    'type' => 'entity',
796
                    'formType' => EntityType::class,
797
                    'targetEntity' => $entityName,
798
                    'joinColumn' => [
799
                        'name' => str_replace('.', '_', Container::underscore($name.'_id')),
800
                        'referencedColumnName' => 'id',
801
                    ],
802
                    'nullable' => $allNullable,
803
                ];
804
805
                break;
806
            case 'multi_ref':
807
                $em = $this->getContainer()->get('doctrine')->getManager();
808
                $entityName = $em->getClassMetadata($extra)->getName();
809
                $parts = explode('\\', $entityName);
810
                $joinTableName = strtolower(
811
                    $prefix.Container::underscore($objectName).'_'.Container::underscore(
812
                        $parts[count($parts) - 1]
813
                    )
814
                );
815
                $fields[$type][] = [
816
                    'fieldName' => lcfirst(Container::camelize($name)),
817
                    'type' => 'entity',
818
                    'formType' => EntityType::class,
819
                    'targetEntity' => $entityName,
820
                    'joinTable' => [
821
                        'name' => $joinTableName,
822
                        'joinColumns' => [
823
                            [
824
                                'name' => strtolower(Container::underscore($objectName)).'_id',
825
                                'referencedColumnName' => 'id',
826
                            ],
827
                        ],
828
                        'inverseJoinColumns' => [
829
                            [
830
                                'name' => strtolower(
831
                                        Container::underscore($parts[count($parts) - 1])
832
                                    ).'_id',
833
                                'referencedColumnName' => 'id',
834
                                'unique' => true,
835
                            ],
836
                        ],
837
                    ],
838
                    'nullable' => $allNullable,
839
                ];
840
841
                break;
842 View Code Duplication
            case 'boolean':
843
                $fields[$type][] = [
844
                    'fieldName' => lcfirst(Container::camelize($name)),
845
                    'type' => 'boolean',
846
                    'formType' => CheckboxType::class,
847
                    'nullable' => $allNullable,
848
                ];
849
850
                break;
851 View Code Duplication
            case 'integer':
852
                $fields[$type][] = [
853
                    'fieldName' => lcfirst(Container::camelize($name)),
854
                    'type' => 'integer',
855
                    'formType' => IntegerType::class,
856
                    'nullable' => $allNullable,
857
                ];
858
859
                break;
860 View Code Duplication
            case 'decimal':
861
                $fields[$type][] = [
862
                    'fieldName' => lcfirst(Container::camelize($name)),
863
                    'type' => 'decimal',
864
                    'precision' => 10,
865
                    'scale' => 2,
866
                    'formType' => NumberType::class,
867
                    'nullable' => $allNullable,
868
                ];
869
870
                break;
871 View Code Duplication
            case 'datetime':
872
                $fields[$type][] = [
873
                    'fieldName' => lcfirst(Container::camelize($name)),
874
                    'type' => 'datetime',
875
                    'formType' => DateTimeType::class,
876
                    'nullable' => $allNullable,
877
                ];
878
879
                break;
880
        }
881
882
        return $fields;
883
    }
884
885
    /**
886
     * Get an array with the available page templates.
887
     *
888
     * @param BundleInterface $bundle The bundle for which we want to get the template configurations
889
     *
890
     * @return array
891
     */
892
    protected function getAvailableTemplates(BundleInterface $bundle)
893
    {
894
        $configs = [];
895
        $counter = 1;
896
897
        // Get the available sections from disc
898
        if (Kernel::VERSION_ID >= 40000) {
899
            $dir = $this->getContainer()->getParameter('kernel.project_dir').'/config/kunstmaancms/pagetemplates/';
900
        } else {
901
            $dir = $bundle->getPath().'/Resources/config/pagetemplates/';
902
        }
903
904
        if (file_exists($dir) && is_dir($dir)) {
905
            $finder = new Finder();
906
            $finder->files()->in($dir)->depth('== 0');
907
            foreach ($finder as $file) {
908
                $info = $this->getTemplateInfo($dir, $file->getFileName());
909
                if (is_array($info)) {
910
                    $configs[$counter++] = $info;
911
                }
912
            }
913
        }
914
915
        return $configs;
916
    }
917
918
    /**
919
     * Get the information about a pagepart section configuration file.
920
     *
921
     * @param string $dir
922
     * @param string $file
923
     *
924
     * @return array|null
925
     */
926
    protected function getTemplateInfo($dir, $file)
927
    {
928
        $info = null;
929
930
        try {
931
            $data = Yaml::parse(file_get_contents($dir.$file));
932
933 View Code Duplication
            if (array_key_exists('kunstmaan_page_part', $data)) {
934
                //Get rid of the bundle config lines
935
                $data = array_values(array_values(array_values($data)[0])[0])[0];
936
            }
937
938
            // Parse contexts
939
            $contexts = [];
940
            foreach ($data['rows'] as $row) {
941
                foreach ($row['regions'] as $region) {
942
                    $contexts[] = $region['name'];
943
                }
944
            }
945
            $info = [
946
                'name' => $data['name'],
947
                'contexts' => $contexts,
948
                'file' => $file,
949
            ];
950
        } catch (ParseException $e) {
951
        }
952
953
        return $info;
954
    }
955
956
    /**
957
     * Get an array with the available page templates.
958
     *
959
     * @param BundleInterface $bundle The bundle for which we want to get the template configurations
960
     *
961
     * @return array
962
     */
963
    protected function getAvailablePages(BundleInterface $bundle)
964
    {
965
        $pages = [];
966
        $counter = 1;
967
968
        // Get the available pages from disc
969
        $dir = $bundle->getPath().'/Entity/Pages/';
970
        if (file_exists($dir) && is_dir($dir)) {
971
            $finder = new Finder();
972
            $finder->files()->in($dir)->depth('== 0');
973
            foreach ($finder as $file) {
974
                $pages[$counter++] = [
975
                    'name' => substr($file->getFileName(), 0, strlen($file->getFileName()) - 4),
976
                    'path' => $file->getPathName(),
977
                ];
978
            }
979
        }
980
981
        return $pages;
982
    }
983
984
    /**
985
     * Check that it is possible to generate the behat tests.
986
     *
987
     * @param BundleInterface $bundle
988
     *
989
     * @return bool
990
     */
991
    protected function canGenerateBehatTests(BundleInterface $bundle)
992
    {
993
        $behatFile = dirname($this->getContainer()->getParameter('kernel.root_dir').'/').'/behat.yml';
994
        $pagePartContext = $bundle->getPath().'/Features/Context/PagePartContext.php';
995
        $behatTestPage = $bundle->getPath().'/Entity/Pages/BehatTestPage.php';
996
997
        // Make sure behat is configured and the PagePartContext and BehatTestPage exits
998
        return file_exists($behatFile) && file_exists($pagePartContext) && file_exists($behatTestPage);
999
    }
1000
1001
    /**
1002
     * @internal
1003
     */
1004
    protected function isSymfony4()
1005
    {
1006
        return Kernel::VERSION_ID >= 40000;
1007
    }
1008
}
1009