├── Classes ├── Command │ └── GeneratorCommandController.php └── Generator │ ├── AbstractNodeGeneratorImplementation.php │ ├── Content │ ├── ImageGeneratorImplementation.php │ ├── TextGeneratorImplementation.php │ └── TextWithImageGeneratorImplementation.php │ ├── Document │ └── PageGeneratorImplementation.php │ ├── NodeGeneratorImplementationInterface.php │ ├── NodesGenerator.php │ └── PresetDefinition.php ├── Configuration └── Settings.yaml ├── README.md ├── Resources └── Private │ └── Images │ ├── Sample1.jpg │ ├── Sample2.jpg │ └── Sample3.jpg └── composer.json /Classes/Command/GeneratorCommandController.php: -------------------------------------------------------------------------------- 1 | presets[$preset]))) { 65 | $this->outputLine('Error: Invalid preset'); 66 | $this->quit(1); 67 | } 68 | $preset = $this->presets[$preset]; 69 | /** @var Site $currentSite */ 70 | $currentSite = $this->siteRepository->findOneByNodeName($siteNode); 71 | if ($currentSite === null) { 72 | $this->outputLine('Error: No site for exporting found'); 73 | $this->quit(1); 74 | } 75 | /** @var ContentContext $contentContext */ 76 | $contentContext = $this->createContext($currentSite, 'live'); 77 | 78 | $workspace = 'live'; 79 | if ($this->workspaceRepository->findByName($workspace)->count() === 0) { 80 | $this->outputLine('Workspace "%s" does not exist', [$workspace]); 81 | $this->quit(1); 82 | } 83 | 84 | /** @var Node $siteNode */ 85 | $siteNode = $contentContext->getCurrentSiteNode(); 86 | 87 | if ($path === null) { 88 | $rootNode = $siteNode; 89 | } else { 90 | if (str_starts_with('/',$path)) { 91 | // absolute path 92 | $rootNode = $contentContext->getNode($path); 93 | } else { 94 | // relative path 95 | $rootNode = $siteNode->getNode($path); 96 | } 97 | } 98 | 99 | if ($rootNode === null) { 100 | $this->outputLine('Error: Could not determine the root node'); 101 | $this->quit(1); 102 | } 103 | $preset = new PresetDefinition($rootNode, $preset); 104 | $generator = new NodesGenerator($preset); 105 | 106 | $generator->generate(); 107 | 108 | $this->outputLine('Success: Node generation complete'); 109 | } 110 | 111 | /** 112 | * @param Site $currentSite 113 | * @param string $workspace 114 | * @return Context 115 | */ 116 | protected function createContext(Site $currentSite, $workspace = 'live') 117 | { 118 | return $this->contextFactory->create([ 119 | 'workspaceName' => $workspace, 120 | 'currentSite' => $currentSite, 121 | 'invisibleContentShown' => true, 122 | 'inaccessibleContentShown' => true 123 | ]); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /Classes/Generator/AbstractNodeGeneratorImplementation.php: -------------------------------------------------------------------------------- 1 | resourceManager->importResource(sprintf('resource://Flowpack.NodeGenerator/Private/Images/Sample%d.jpg', rand(1, 3)))); 43 | $this->imageRepository->add($image); 44 | 45 | $newImageVariant = new ImageVariant($image); 46 | $resizeImageAdjustment = new ResizeImageAdjustment(); 47 | $resizeImageAdjustment->setWidth(1024); 48 | $resizeImageAdjustment->setHeight(1500); 49 | 50 | $newImageVariant->addAdjustment($resizeImageAdjustment); 51 | 52 | $image->addVariant($newImageVariant); 53 | return $newImageVariant; 54 | } 55 | 56 | /** 57 | * @param NodeInterface $parentNode 58 | * @param NodeType $nodeType 59 | * @return NodeInterface The freshly created node 60 | */ 61 | abstract public function create(NodeInterface $parentNode, NodeType $nodeType); 62 | } 63 | -------------------------------------------------------------------------------- /Classes/Generator/Content/ImageGeneratorImplementation.php: -------------------------------------------------------------------------------- 1 | setProperty('image', $this->getRandomImageVariant()); 31 | 32 | if (rand(0, 10) === 0) { 33 | $node->setProperty('hasCaption', true); 34 | $node->setProperty('caption', Lorem::sentence(rand(5, 12))); 35 | $node->setProperty('title', Lorem::sentence(rand(5, 12))); 36 | $node->setProperty('alternativeText', Lorem::sentence(rand(5, 12))); 37 | } 38 | 39 | return $node; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Classes/Generator/Content/TextGeneratorImplementation.php: -------------------------------------------------------------------------------- 1 | createNode(uniqid('node'), $nodeType); 29 | $contentNode->setProperty('text', sprintf('

%s

', Lorem::paragraph(rand(1, 10)))); 30 | 31 | return $contentNode; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Classes/Generator/Content/TextWithImageGeneratorImplementation.php: -------------------------------------------------------------------------------- 1 | setProperty('image', $this->getRandomImageVariant()); 31 | 32 | if (rand(0, 10) === 0) { 33 | $node->setProperty('hasCaption', true); 34 | $node->setProperty('caption', Lorem::sentence(rand(5, 12))); 35 | $node->setProperty('title', Lorem::sentence(rand(5, 12))); 36 | $node->setProperty('alternativeText', Lorem::sentence(rand(5, 12))); 37 | } 38 | 39 | return $node; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Classes/Generator/Document/PageGeneratorImplementation.php: -------------------------------------------------------------------------------- 1 | company; 31 | $name = Utility::renderValidNodeName($title); 32 | 33 | $childrenNode = $parentNode->createNode($name, $nodeType); 34 | $childrenNode->setProperty('title', $title); 35 | 36 | return $childrenNode; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Classes/Generator/NodeGeneratorImplementationInterface.php: -------------------------------------------------------------------------------- 1 | preset = $preset; 52 | } 53 | 54 | /** 55 | * @throws Exception 56 | * @throws NodeTypeNotFoundException 57 | */ 58 | public function generate() 59 | { 60 | $siteNode = $this->preset->getSiteNode(); 61 | $this->createBatchDocumentNode($siteNode); 62 | } 63 | 64 | /** 65 | * @param NodeType $nodeType 66 | * @return NodeGeneratorImplementationInterface 67 | * @throws Exception 68 | */ 69 | protected function getNodeGeneratorImplementationClassByNodeType(NodeType $nodeType) 70 | { 71 | if (!isset($this->generators[(string)$nodeType]['class'])) { 72 | throw new Exception(sprintf('Unknown generator for the current Node Type (%s)', (string)$nodeType, 1391771111)); 73 | } 74 | return $this->objectManager->get($this->generators[(string)$nodeType]['class']); 75 | } 76 | 77 | /** 78 | * @param NodeInterface $baseNode 79 | * @param int $level 80 | * @throws Exception 81 | * @throws NodeTypeNotFoundException 82 | */ 83 | protected function createBatchDocumentNode(NodeInterface $baseNode, $level = 0) 84 | { 85 | for ($i = 0; $i < $this->preset->getNodeByLevel(); $i++) { 86 | try { 87 | $nodeType = $this->nodeTypeManager->getNodeType($this->preset->getDocumentNodeType()); 88 | $generator = $this->getNodeGeneratorImplementationClassByNodeType($nodeType); 89 | $childrenNode = $generator->create($baseNode, $nodeType); 90 | $this->createBatchContentNodes($childrenNode); 91 | if ($level < $this->preset->getDepth()) { 92 | $level++; 93 | $this->createBatchDocumentNode($childrenNode, $level); 94 | } 95 | } catch (NodeExistsException $e) { 96 | } 97 | } 98 | } 99 | 100 | /** 101 | * @param NodeInterface $documentNode 102 | * @throws Exception 103 | * @throws NodeTypeNotFoundException 104 | */ 105 | protected function createBatchContentNodes(NodeInterface $documentNode) 106 | { 107 | $mainContentCollection = $documentNode->getNode('main'); 108 | for ($j = 0; $j < $this->preset->getContentNodeByDocument(); $j++) { 109 | try { 110 | $nodeType = $this->nodeTypeManager->getNodeType($this->preset->getContentNodeType()); 111 | $generator = $this->getNodeGeneratorImplementationClassByNodeType($nodeType); 112 | $generator->create($mainContentCollection, $nodeType); 113 | } catch (NodeExistsException $e) { 114 | } 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /Classes/Generator/PresetDefinition.php: -------------------------------------------------------------------------------- 1 | siteNode = $siteNode; 58 | $this->nodeByLevel = (int)$configuration['nodeByLevel']; 59 | $this->contentNodeByDocument = (int)$configuration['contentNodeByDocument']; 60 | $this->depth = (int)$configuration['depth']; 61 | $this->contentNodeType = (array)$configuration['contentNodeType']; 62 | $this->documentNodeType = (array)$configuration['documentNodeType']; 63 | $this->randomness = (int)$configuration['randomness']; 64 | } 65 | 66 | /** 67 | * @return int 68 | */ 69 | protected function getRandomness(): int 70 | { 71 | return (int)$this->randomness; 72 | } 73 | 74 | /** 75 | * @return string 76 | */ 77 | public function getContentNodeType(): string 78 | { 79 | return (string)$this->contentNodeType[array_rand($this->contentNodeType)]; 80 | } 81 | 82 | /** 83 | * @return int 84 | */ 85 | public function getDepth(): int 86 | { 87 | if ($this->getRandomness()) { 88 | $variant = $this->depth * $this->getRandomness() / 100; 89 | $minimum = ceil($this->depth - $variant); 90 | $maximum = ceil($this->depth + $variant); 91 | $depth = rand($minimum, $maximum); 92 | } else { 93 | $depth = $this->depth; 94 | } 95 | return $depth; 96 | } 97 | 98 | /** 99 | * @return string 100 | */ 101 | public function getDocumentNodeType(): string 102 | { 103 | return (string)$this->documentNodeType[array_rand($this->documentNodeType)]; 104 | } 105 | 106 | /** 107 | * @return int 108 | */ 109 | public function getNodeByLevel(): int 110 | { 111 | if ($this->getRandomness()) { 112 | $variant = $this->nodeByLevel * $this->getRandomness() / 100; 113 | $minimum = ceil($this->nodeByLevel - $variant); 114 | $maximum = ceil($this->nodeByLevel + $variant); 115 | $nodeByLevel = rand($minimum, $maximum); 116 | } else { 117 | $nodeByLevel = $this->nodeByLevel; 118 | } 119 | return $nodeByLevel; 120 | } 121 | 122 | /** 123 | * @return int 124 | */ 125 | public function getContentNodeByDocument(): int 126 | { 127 | if ($this->getRandomness()) { 128 | $variant = $this->contentNodeByDocument * $this->getRandomness() / 100; 129 | $minimum = ceil($this->contentNodeByDocument - $variant); 130 | $maximum = ceil($this->contentNodeByDocument + $variant); 131 | $contentNodeByDocument = rand($minimum, $maximum); 132 | } else { 133 | $contentNodeByDocument = $this->contentNodeByDocument; 134 | } 135 | return $contentNodeByDocument; 136 | } 137 | 138 | /** 139 | * @return NodeInterface 140 | */ 141 | public function getSiteNode(): NodeInterface 142 | { 143 | return $this->siteNode; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /Configuration/Settings.yaml: -------------------------------------------------------------------------------- 1 | Flowpack: 2 | NodeGenerator: 3 | generator: 4 | 'Neos.NodeTypes:Page': 5 | class: 'Flowpack\NodeGenerator\Generator\Document\PageGeneratorImplementation' 6 | 'Neos.NodeTypes:Text': 7 | class: 'Flowpack\NodeGenerator\Generator\Content\TextGeneratorImplementation' 8 | 'Neos.NodeTypes:Image': 9 | class: 'Flowpack\NodeGenerator\Generator\Content\ImageGeneratorImplementation' 10 | 'Neos.NodeTypes:TextWithImage': 11 | class: 'Flowpack\NodeGenerator\Generator\Content\TextWithImageGeneratorImplementation' 12 | 13 | preset: 14 | # Basic website, with a multiple level page tree 15 | small-website: 16 | depth: 3 17 | nodeByLevel: 10 18 | contentNodeByDocument: 5 19 | documentNodeType: [ 'Neos.NodeTypes:Page' ] 20 | contentNodeType: [ 'Neos.NodeTypes:Text', 'Neos.NodeTypes:TextWithImage' ] 21 | # Randomness of the number of nodes generated from 0 to 100 22 | randomness: 25 23 | medium-website: 24 | depth: 8 25 | nodeByLevel: 15 26 | contentNodeByDocument: 10 27 | documentNodeType: [ 'Neos.NodeTypes:Page' ] 28 | contentNodeType: [ 'Neos.NodeTypes:Text', 'Neos.NodeTypes:TextWithImage' ] 29 | # Randomness of the number of nodes generated from 0 to 100 30 | randomness: 45 31 | big-website: 32 | depth: 12 33 | nodeByLevel: 25 34 | contentNodeByDocument: 15 35 | documentNodeType: [ 'Neos.NodeTypes:Page' ] 36 | contentNodeType: [ 'Neos.NodeTypes:Text', 'Neos.NodeTypes:TextWithImage' ] 37 | # Randomness of the number of nodes generated from 0 to 100 38 | randomness: 15 39 | 40 | # Blog style website, with all blog post on the first level 41 | small-blog: 42 | depth: 0 43 | nodeByLevel: 10 44 | contentNodeByDocument: 20 45 | documentNodeType: [ 'Neos.NodeTypes:Page' ] 46 | contentNodeType: [ 'Neos.NodeTypes:Text', 'Neos.NodeTypes:Image', 'Neos.NodeTypes:TextWithImage' ] 47 | # Randomness of the number of nodes generated from 0 to 100 48 | randomness: 0 49 | medium-blog: 50 | depth: 0 51 | nodeByLevel: 50 52 | contentNodeByDocument: 25 53 | documentNodeType: [ 'Neos.NodeTypes:Page' ] 54 | contentNodeType: [ 'Neos.NodeTypes:Text', 'Neos.NodeTypes:Image', 'Neos.NodeTypes:TextWithImage' ] 55 | # Randomness of the number of nodes generated from 0 to 100 56 | randomness: 12 57 | big-blog: 58 | depth: 0 59 | nodeByLevel: 200 60 | contentNodeByDocument: 30 61 | documentNodeType: [ 'Neos.NodeTypes:Page' ] 62 | contentNodeType: [ 'Neos.NodeTypes:Text', 'Neos.NodeTypes:Image', 'Neos.NodeTypes:TextWithImage' ] 63 | # Randomness of the number of nodes generated from 0 to 100 64 | randomness: 12 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NodeGenerator - Random nodes generator for Neos CMS 2 | 3 | ## Configuration of the Node generator classes 4 | 5 | In your Settings.yaml you can register a node generator class. Each node type used in your setup, must 6 | have an attached generator class. 7 | 8 | ```yaml 9 | Flowpack: 10 | NodeGenerator: 11 | generator: 12 | 'Neos.NodeTypes:Page': 13 | class: 'Flowpack\NodeGenerator\Generator\Document\PageGeneratorImplementation' 14 | 'Neos.NodeTypes:Text': 15 | class: 'Flowpack\NodeGenerator\Generator\Content\TextGeneratorImplementation' 16 | 'Neos.NodeTypes:Image': 17 | class: 'Flowpack\NodeGenerator\Generator\Content\ImageGeneratorImplementation' 18 | 'Neos.NodeTypes:TextWithImage': 19 | class: 'Flowpack\NodeGenerator\Generator\Content\TextWithImageGeneratorImplementation' 20 | ``` 21 | 22 | ### Minimal Generator Class 23 | 24 | The NodesGenerators who call your node generator class, will catch NodeExistsException so you 25 | don't need to take care about that. The generator will skip silently nodes that currently exist 26 | in the content repository. 27 | 28 | ```php 29 | class PageGeneratorImplementation extends AbstractNodeGeneratorImplementation { 30 | 31 | /** 32 | * @param NodeInterface $parentNode 33 | * @param NodeType $nodeType 34 | * @return NodeInterface 35 | */ 36 | public function create(NodeInterface $parentNode, NodeType $nodeType) { 37 | $title = Company::name(); 38 | $name = Utility::renderValidNodeName($title); 39 | 40 | $childrenNode = $parentNode->createNode($name, $nodeType); 41 | $childrenNode->setProperty('title', $title); 42 | 43 | return $childrenNode; 44 | } 45 | } 46 | ``` 47 | 48 | ## Configuration of presets 49 | 50 | If multiple Content and Document node types are configured, the generator will select a 51 | random node type for each new node. Take care to declare a generator class for each node 52 | type. 53 | 54 | The Extension is shipped with some examples of presets, a basic preset looks like: 55 | 56 | ```yaml 57 | Flowpack: 58 | NodeGenerator: 59 | preset: 60 | # Basic website, with a multiple level page tree 61 | small-website: 62 | depth: 3 63 | nodeByLevel: 10 64 | contentNodeByDocument: 5 65 | documentNodeType: [ 'Neos.NodeTypes:Page' ] 66 | contentNodeType: [ 'Neos.NodeTypes:Text', 'Neos.NodeTypes:Images' ] 67 | # Randomness of the number of nodes generated from 0 to 100 68 | randomness: 25 69 | ``` 70 | 71 | ## Run your preset 72 | 73 | ``` 74 | ./flow generator:nodes --site-node blog --preset small-blog 75 | ``` 76 | 77 | ## Configure the root node 78 | 79 | By default all the generated pages will be created on the root-level 80 | of the site. This behavior can be changed by providing a specific path 81 | to an existing node: 82 | 83 | ``` 84 | flow generator:nodes --site-node homepage --preset small-blog --path blog 85 | ``` 86 | 87 | -------------------------------------------------------------------------------- /Resources/Private/Images/Sample1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flowpack/Flowpack.NodeGenerator/2dca66c51497883cd51ab730264a9cdae0883323/Resources/Private/Images/Sample1.jpg -------------------------------------------------------------------------------- /Resources/Private/Images/Sample2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flowpack/Flowpack.NodeGenerator/2dca66c51497883cd51ab730264a9cdae0883323/Resources/Private/Images/Sample2.jpg -------------------------------------------------------------------------------- /Resources/Private/Images/Sample3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flowpack/Flowpack.NodeGenerator/2dca66c51497883cd51ab730264a9cdae0883323/Resources/Private/Images/Sample3.jpg -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flowpack/nodegenerator", 3 | "type": "neos-package", 4 | "license": "MIT", 5 | "description": "Random nodes generator for Neos CMS", 6 | "require": { 7 | "neos/neos": "~3.3 || ~4.0 || ~5.0", 8 | "fzaninotto/faker": "~1.8" 9 | }, 10 | "autoload": { 11 | "psr-4": { 12 | "Flowpack\\NodeGenerator\\": "Classes" 13 | } 14 | }, 15 | "extra": { 16 | "applied-flow-migrations": [ 17 | "TYPO3.FLOW3-201201261636", 18 | "TYPO3.Fluid-201205031303", 19 | "TYPO3.FLOW3-201205292145", 20 | "TYPO3.FLOW3-201206271128", 21 | "TYPO3.FLOW3-201209201112", 22 | "TYPO3.Flow-201209251426", 23 | "TYPO3.Flow-201211151101", 24 | "TYPO3.Flow-201212051340", 25 | "TYPO3.TypoScript-130516234520", 26 | "TYPO3.TypoScript-130516235550", 27 | "TYPO3.TYPO3CR-130523180140", 28 | "TYPO3.Neos.NodeTypes-201309111655", 29 | "TYPO3.Flow-201310031523", 30 | "TYPO3.Flow-201405111147", 31 | "TYPO3.Neos-201407061038", 32 | "TYPO3.Neos-201409071922", 33 | "TYPO3.TYPO3CR-140911160326", 34 | "TYPO3.Neos-201410010000", 35 | "TYPO3.TYPO3CR-141101082142", 36 | "TYPO3.Neos-20141113115300", 37 | "TYPO3.Fluid-20141113120800", 38 | "TYPO3.Flow-20141113121400", 39 | "TYPO3.Fluid-20141121091700", 40 | "TYPO3.Neos-20141218134700", 41 | "TYPO3.Fluid-20150214130800", 42 | "TYPO3.Neos-20150303231600", 43 | "TYPO3.TYPO3CR-20150510103823", 44 | "TYPO3.Flow-20151113161300", 45 | "TYPO3.Form-20160601101500", 46 | "TYPO3.Flow-20161115140400", 47 | "TYPO3.Flow-20161115140430", 48 | "Neos.Flow-20161124204700", 49 | "Neos.Flow-20161124204701", 50 | "Neos.Twitter.Bootstrap-20161124204912", 51 | "Neos.Form-20161124205254", 52 | "Neos.Flow-20161124224015", 53 | "Neos.Party-20161124225257", 54 | "Neos.Eel-20161124230101", 55 | "Neos.Kickstart-20161124230102", 56 | "Neos.Setup-20161124230842", 57 | "Neos.Imagine-20161124231742", 58 | "Neos.Media-20161124233100", 59 | "Neos.NodeTypes-20161125002300", 60 | "Neos.SiteKickstarter-20161125002311", 61 | "Neos.Neos-20161125002322", 62 | "Neos.ContentRepository-20161125012000", 63 | "Neos.Fusion-20161125013710", 64 | "Neos.Setup-20161125014759", 65 | "Neos.SiteKickstarter-20161125095901", 66 | "Neos.Fusion-20161125104701", 67 | "Neos.NodeTypes-20161125104800", 68 | "Neos.Neos-20161125104802", 69 | "Neos.Kickstarter-20161125110814", 70 | "Neos.Neos-20161125122412", 71 | "Neos.Flow-20161125124112", 72 | "TYPO3.FluidAdaptor-20161130112935", 73 | "Neos.Fusion-20161201202543", 74 | "Neos.Neos-20161201222211", 75 | "Neos.Fusion-20161202215034", 76 | "Neos.Fusion-20161219092345", 77 | "Neos.ContentRepository-20161219093512", 78 | "Neos.Media-20161219094126", 79 | "Neos.Neos-20161219094403", 80 | "Neos.Neos-20161219122512", 81 | "Neos.Fusion-20161219130100", 82 | "Neos.Neos-20161220163741", 83 | "Neos.Neos-20170115114620", 84 | "Neos.Fusion-20170120013047", 85 | "Neos.Flow-20170125103800", 86 | "Neos.Seo-20170127154600", 87 | "Neos.Flow-20170127183102", 88 | "Neos.Neos-20180907103800" 89 | ] 90 | } 91 | } 92 | --------------------------------------------------------------------------------