Message-ID: <1117832650.3540.1485853538226.JavaMail.confluence@ip-10-127-227-164> Subject: Exported From Confluence MIME-Version: 1.0 Content-Type: multipart/related; boundary="----=_Part_3539_301935156.1485853538225" ------=_Part_3539_301935156.1485853538225 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Content-Location: file:///C:/exported.html How to expose SiteAccess aware configuration for your bundle</ti= tle> <!--[if gte mso 9]> <xml> <o:OfficeDocumentSettings> <o:TargetScreenSize>1024x640</o:TargetScreenSize> <o:PixelsPerInch>72</o:PixelsPerInch> <o:AllowPNG/> </o:OfficeDocumentSettings> <w:WordDocument> <w:View>Print</w:View> <w:Zoom>90</w:Zoom> <w:DoNotOptimizeForBrowser/> </w:WordDocument> </xml> <![endif]--> <style> <!-- @page Section1 { size: 8.5in 11.0in; margin: 1.0in; mso-header-margin: .5in; mso-footer-margin: .5in; mso-paper-source: 0; } td { page-break-inside: avoid; } tr { page-break-after: avoid; } div.Section1 { page: Section1; } /* Confluence print stylesheet. Common to all themes for print medi= a */ /* Full of !important until we improve batching for print CSS */ @media print { #main { padding-bottom: 1em !important; /* The default padding of 6em is to= o much for printouts */ } body { font-family: Arial, Helvetica, FreeSans, sans-serif; font-size: 10pt; line-height: 1.2; } body, #full-height-container, #main, #page, #content, .has-personal-sid= ebar #content { background: #fff !important; color: #000 !important; border: 0 !important; width: 100% !important; height: auto !important; min-height: auto !important; margin: 0 !important; padding: 0 !important; display: block !important; } a, a:link, a:visited, a:focus, a:hover, a:active { color: #000; } #content h1, #content h2, #content h3, #content h4, #content h5, #content h6 { font-family: Arial, Helvetica, FreeSans, sans-serif; page-break-after: avoid; } pre { font-family: Monaco, "Courier New", monospace; } #header, .aui-header-inner, #navigation, #sidebar, .sidebar, #personal-info-sidebar, .ia-fixed-sidebar, .page-actions, .navmenu, .ajs-menu-bar, .noprint, .inline-control-link, .inline-control-link a, a.show-labels-editor, .global-comment-actions, .comment-actions, .quick-comment-container, #addcomment { display: none !important; } .comment .date::before { content: none !important; /* remove middot for print view */ } h1.pagetitle img { height: auto; width: auto; } .print-only { display: block; } #footer { position: relative !important; /* CONF-17506 Place the footer at en= d of the content */ margin: 0; padding: 0; background: none; clear: both; } #poweredby { border-top: none; background: none; } #poweredby li.print-only { display: list-item; font-style: italic; } #poweredby li.noprint { display: none; } /* no width controls in print */ .wiki-content .table-wrap, .wiki-content p, .panel .codeContent, .panel .codeContent pre, .image-wrap { overflow: visible !important; } /* TODO - should this work? */ #children-section, #comments-section .comment, #comments-section .comment .comment-body, #comments-section .comment .comment-content, #comments-section .comment p { page-break-inside: avoid; } #page-children a { text-decoration: none; } /** hide twixies the specificity here is a hack because print styles are getting loaded before the base styles. */ #comments-section.pageSection .section-header, #comments-section.pageSection .section-title, #children-section.pageSection .section-header, #children-section.pageSection .section-title, .children-show-hide { padding-left: 0; margin-left: 0; } .children-show-hide.icon { display: none; } /* personal sidebar */ .has-personal-sidebar #content { margin-right: 0px; } .has-personal-sidebar #content .pageSection { margin-right: 0px; } } --> </style> </head> <body> <h1>How to expose SiteAccess aware configuration for your bundle</h1> <div class=3D"Section1"> <div class=3D"confluence-information-macro confluence-information-m= acro-note"> <p class=3D"title">Version compatibility</p> <span class=3D"aui-icon aui-icon-small aui-iconfont-warning confluence-info= rmation-macro-icon"></span> <div class=3D"confluence-information-macro-body"> <p>This recipe is compatible with <strong>eZ Publish 5.4 / 2014.07</strong>= </p> </div> </div> <p> </p> <p><style type=3D"text/css">/*<![CDATA[*/ div.rbtoc1485853538063 {padding: 0px;} div.rbtoc1485853538063 ul {list-style: disc;margin-left: 0px;} div.rbtoc1485853538063 li {margin-left: 0px;padding-left: 0px;} /*]]>*/</style></p> <div class=3D"toc-macro rbtoc1485853538063">=20 <ul class=3D"toc-indentation">=20 <li><a href=3D"#HowtoexposeSiteAccessawareconfigurationforyourbundle-Descri= ption">Description</a></li>=20 <li><a href=3D"#HowtoexposeSiteAccessawareconfigurationforyourbundle-Semant= icconfigurationparsing">Semantic configuration parsing</a></li>=20 <li><a href=3D"#HowtoexposeSiteAccessawareconfigurationforyourbundle-Mappin= gtointernalsettings">Mapping to internal settings</a>=20 <ul class=3D"toc-indentation">=20 <li><a href=3D"#HowtoexposeSiteAccessawareconfigurationforyourbundle-Mergin= ghashvaluesbetweenscopes">Merging hash values between scopes</a>=20 <ul class=3D"toc-indentation">=20 <li><a href=3D"#HowtoexposeSiteAccessawareconfigurationforyourbundle-Mergef= romsecondlevel">Merge from second level</a></li>=20 <li><a href=3D"#HowtoexposeSiteAccessawareconfigurationforyourbundle-Limita= tions">Limitations</a></li>=20 </ul> </li>=20 </ul> </li>=20 <li><a href=3D"#HowtoexposeSiteAccessawareconfigurationforyourbundle-Dedica= tedmapperobject">Dedicated mapper object</a>=20 <ul class=3D"toc-indentation">=20 <li><a href=3D"#HowtoexposeSiteAccessawareconfigurationforyourbundle-Mergin= ghashvaluesbetweenscopes.1">Merging hash values between scopes</a></li>=20 </ul> </li>=20 </ul>=20 </div> <p></p> <p><span class=3D"status-macro aui-lozenge aui-lozenge-current">EZP >=3D= 5.4 / 2014.07</span></p> <h2 id=3D"HowtoexposeSiteAccessawareconfigurationforyourbundle-Description"= >Description</h2> <p>Symfony Config component makes it possible to define <em>semantic c= onfiguration</em>, exposed to the end-developer. This configuration is vali= dated by rules you define, e.g. validating type (string, array, integer, bo= olean...). Usually, once validated and processed this semantic configuratio= n is then mapped to internal <em>key/value</em> parameters stored= in the <code>ServiceContainer</code>.</p> <p>eZ Publish uses this for its core configuration, but adds another config= uration level, the <strong>SiteAccess</strong>. For each defined SiteA= ccess, we need to be able to use the same configuration tree in order to de= fine SiteAccess specific config. These settings then need to be mapped to S= iteAccess aware internal parameters, that one can retrieve via the <co= de>ConfigResolver</code>. For this, internal keys need to follow the format=  <code><namespace>.<scope>.<parameter_name></code>,&= nbsp;<code>namespace</code> being specific to your app/bundle, <c= ode>scope</code> being the SiteAccess, SiteAccess group, <code>de= fault</code> or <code>global</code>, <code>parameter_name</c= ode> being the actual setting <em>identifier</em>.</p> <div class=3D"confluence-information-macro confluence-information-macro-inf= ormation"> <span class=3D"aui-icon aui-icon-small aui-iconfont-info confluence-informa= tion-macro-icon"></span> <div class=3D"confluence-information-macro-body"> <p><span style=3D"color: rgb(0,0,0);">For more information on ConfigResolve= r, namespaces and scopes, see </span><a href=3D"/display/EZP/Configura= tion">eZ Publish configuration basics</a><span style=3D"color: rgb(119,119,= 119);">.</span></p> </div> </div> <p><span>Goal of this feature is to make it easy to implement a </span= ><em>SiteAccess aware</em><span> semantic configuration and its mappin= g to internal config for any eZ bundle developer.</span></p> <p> </p> <h2 id=3D"HowtoexposeSiteAccessawareconfigurationforyourbundle-Semanticconf= igurationparsing">Semantic configuration parsing</h2> <p><span><span>An abstract </span><code>Configuration</code><span>&nbs= p;class has been added, simplifying the way to add a SiteAccess settings tr= ee like the following:</span></span></p> <p> </p> <div class=3D"code panel pdl" style=3D"border-width: 1px;"> <div class=3D"codeHeader panelHeader pdl" style=3D"border-bottom-width: 1px= ;"> <b>ezpublish.yml or config.yml</b> </div> <div class=3D"codeContent panelContent pdl">=20 <pre class=3D"brush: bash; gutter: false; theme: Eclipse" style=3D"font-siz= e:12px;">acme_demo: system: my_siteaccess: hello: "world" foo_setting: an_integer: 456 enabled: true my_siteaccess_group: hello: "universe" foo_setting: foo: "bar" some: "thing" an_integer: 123 enabled: false</pre>=20 </div> </div> <p> </p> <p><span>Class FQN is </span><code>eZ\Bundle\EzPublishCoreBundle\Depen= dencyInjection\Configuration\SiteAccessAware\Configuration</code><span>.<br= ></span><span style=3D"line-height: 1.4285715;">All you have to do is to ex= tend it and use </span><code style=3D"line-height: 1.4285715;">$this-&= gt;generateScopeBaseNode()</code><span style=3D"line-height: 1.4285715;">:<= /span></p> <div class=3D"code panel pdl" style=3D"border-width: 1px;"> <div class=3D"codeContent panelContent pdl">=20 <pre class=3D"brush: php; gutter: false; theme: Eclipse" style=3D"font-size= :12px;">namespace Acme\DemoBundle\DependencyInjection; use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAcc= essAware\Configuration as SiteAccessConfiguration; use Symfony\Component\Config\Definition\Builder\NodeBuilder; use Symfony\Component\Config\Definition\Builder\TreeBuilder; class Configuration extends SiteAccessConfiguration { public function getConfigTreeBuilder() { $treeBuilder =3D new TreeBuilder(); $rootNode =3D $treeBuilder->root( 'acme_demo' ); // $systemNode will then be the root of siteaccess aware settings. $systemNode =3D $this->generateScopeBaseNode( $rootNode ); $systemNode ->scalarNode( 'hello' )->isRequired()->end() ->arrayNode( 'foo_setting' ) ->children() ->scalarNode( "foo" )->end() ->scalarNode( "some" )->end() ->integerNode( "an_integer" )->end() ->booleanNode( "enabled" )->end() ->end() ->end(); return $treeBuilder; } }</pre>=20 </div> </div> <div class=3D"confluence-information-macro confluence-information-macro-inf= ormation"> <span class=3D"aui-icon aui-icon-small aui-iconfont-info confluence-informa= tion-macro-icon"></span> <div class=3D"confluence-information-macro-body"> <p><span style=3D"color: rgb(0,0,0);">Default name for the <em>SiteAcc= ess root node</em> is <code>system</code>, but you can customize = it. For this, just pass the name you want to use as a 2nd argument of = <code>$this->generateScopeBaseNode()</code>.</span></p> </div> </div> <h2 id=3D"HowtoexposeSiteAccessawareconfigurationforyourbundle-Mappingtoint= ernalsettings">Mapping to internal settings</h2> <p>Semantic configuration must always be <em>mapped</em> to inter= nal <em>key/value</em> settings within the <code>ServiceCont= ainer</code>. This is usually done in the DIC extension.</p> <p>For SiteAccess aware settings, new <code>ConfigurationProcessor</co= de> and <code>Contextualizer</code> classes have been introd= uced to ease the process.</p> <div class=3D"code panel pdl" style=3D"border-width: 1px;"> <div class=3D"codeContent panelContent pdl">=20 <pre class=3D"brush: php; gutter: false; theme: Eclipse" style=3D"font-size= :12px;">namespace Acme\DemoBundle\DependencyInjection; use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAcc= essAware\ConfigurationProcessor; use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAcc= essAware\ContextualizerInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Config\FileLocator; use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\DependencyInjection\Loader; /** * This is the class that loads and manages your bundle configuration * * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles= /extension.html} */ class AcmeDemoExtension extends Extension { public function load( array $configs, ContainerBuilder $container ) { $configuration =3D $this->getConfiguration( $configs, $container= ); $config =3D $this->processConfiguration( $configuration, $config= s ); $loader =3D new Loader\YamlFileLoader( $container, new FileLocator(= __DIR__.'/../Resources/config' ) ); $loader->load( 'default_settings.yml' ); // "acme_demo" will be the namespace as used in ConfigResolver form= at. $processor =3D new ConfigurationProcessor( $container, 'acme_demo' = ); $processor->mapConfig( $config, // Any kind of callable can be used here. // It will be called for each declared scope/SiteAccess. function ( $scopeSettings, $currentScope, ContextualizerInterfa= ce $contextualizer ) { // Will map "hello" setting to "acme_demo.<$currentScope= >.hello" container parameter // It will then be possible to retrieve this parameter thro= ugh ConfigResolver in the application code: // $helloSetting =3D $configResolver->getParameter( 'hel= lo', 'acme_demo' ); $contextualizer->setContextualParameter( 'hello', $curre= ntScope, $scopeSettings['hello'] ); } ); // Now map "foo_setting" and ensure keys defined for "my_siteaccess= " overrides the one for "my_siteaccess_group" // It is done outside the closure as it is needed only once. $processor->mapConfigArray( 'foo_setting', $config ); } }</pre>=20 </div> </div> <div class=3D"confluence-information-macro confluence-information-macro-tip= "> <p class=3D"title">Tip</p> <span class=3D"aui-icon aui-icon-small aui-iconfont-approve confluence-info= rmation-macro-icon"></span> <div class=3D"confluence-information-macro-body"> <p>You can map simple settings by calling <code>$processor->mapSetting()= </code>, without having to call <code>$processor->mapConfig()</code> wit= h a callable.</p> <div class=3D"code panel pdl" style=3D"border-width: 1px;"> <div class=3D"codeContent panelContent pdl">=20 <pre class=3D"brush: php; gutter: false; theme: Eclipse" style=3D"font-size= :12px;">$processor =3D new ConfigurationProcessor( $container, 'acme_demo' = ); $processor->mapSetting( 'hello', $config );</pre>=20 </div> </div> </div> </div> <div class=3D"confluence-information-macro confluence-information-macro-war= ning"> <span class=3D"aui-icon aui-icon-small aui-iconfont-error confluence-inform= ation-macro-icon"></span> <div class=3D"confluence-information-macro-body"> <p><strong>Important:</strong> Always ensure you have defined and loaded de= fault settings.</p> </div> </div> <div class=3D"code panel pdl" style=3D"border-width: 1px;"> <div class=3D"codeHeader panelHeader pdl" style=3D"border-bottom-width: 1px= ;"> <b>@AcmeDemoBundle/Resources/config/default_settings.yml</b> </div> <div class=3D"codeContent panelContent pdl">=20 <pre class=3D"brush: bash; gutter: false; theme: Eclipse" style=3D"font-siz= e:12px;">parameters: acme_demo.default.hello: world acme_demo.default.foo_setting: foo: ~ some: ~ planets: [Earth] an_integer: 0 enabled: false j_adore: les_sushis</pre>=20 </div> </div> <h3 id=3D"HowtoexposeSiteAccessawareconfigurationforyourbundle-Merginghashv= aluesbetweenscopes">Merging hash values between scopes</h3> <p>When you define a hash as semantic config, you sometimes don't want the = SiteAccess settings to replace the default or group values, but <em>en= rich</em> them by appending new entries. This is made possible by usin= g <code>$processor->mapConfigArray()</code>, which needs to be call= ed outside the closure (before or after), in order to be called only once.<= /p> <p><span>Consider the following default config:</span></p> <div class=3D"code panel pdl" style=3D"border-width: 1px;"> <div class=3D"codeHeader panelHeader pdl" style=3D"border-bottom-width: 1px= ;"> <b>default_settings.yml</b> </div> <div class=3D"codeContent panelContent pdl">=20 <pre class=3D"brush: bash; gutter: false; theme: Eclipse" style=3D"font-siz= e:12px;">parameters: acme_demo.default.foo_setting: foo: ~ some: ~ planets: [Earth] an_integer: 0 enabled: false j_adore: les_sushis</pre>=20 </div> </div> <p><span><span><br></span></span></p> <p><span><span>And then this semantic config:</span></span></p> <p> </p> <div class=3D"code panel pdl" style=3D"border-width: 1px;"> <div class=3D"codeHeader panelHeader pdl" style=3D"border-bottom-width: 1px= ;"> <b>ezpublish.yml or config.yml</b> </div> <div class=3D"codeContent panelContent pdl">=20 <pre class=3D"brush: bash; gutter: false; theme: Eclipse" style=3D"font-siz= e:12px;">acme_demo: system: sa_group: foo_setting: foo: bar some: thing an_integer: 123 # Assuming "sa1" is part of "sa_group" sa1: foo_setting: an_integer: 456 enabled: true j_adore: le_saucisson</pre>=20 </div> </div> <p><span><br></span></p> <p><span>What we want here, is that keys defined for </span><code>foo_= setting</code><span> are merged between default/group/SiteAccess:</spa= n></p> <div class=3D"code panel pdl" style=3D"border-width: 1px;"> <div class=3D"codeHeader panelHeader pdl" style=3D"border-bottom-width: 1px= ;"> <b>Expected result</b> </div> <div class=3D"codeContent panelContent pdl">=20 <pre class=3D"brush: bash; gutter: false; theme: Eclipse" style=3D"font-siz= e:12px;">parameters: acme_demo.sa1.foo_setting: foo: bar some: thing planets: [Earth] an_integer: 456 enabled: true j_adore: le_saucisson</pre>=20 </div> </div> <h4 id=3D"HowtoexposeSiteAccessawareconfigurationforyourbundle-Mergefromsec= ondlevel">Merge from <em>second level</em></h4> <p>In the example above, entries were merged in respect to the scope order = of precedence. However, if we define the <code>planets</code> key= for<code>sa1</code>, it will completely override the default value since t= he merge process is done at only 1 level.</p> <p>You can add another level by passing <code>ContextualizerInterface:= :MERGE_FROM_SECOND_LEVEL</code> as an option (3rd argument) to<code>$c= ontextualizer->mapConfigArray()</code>.</p> <div class=3D"code panel pdl" style=3D"border-width: 1px;"> <div class=3D"codeHeader panelHeader pdl" style=3D"border-bottom-width: 1px= ;"> <b>default_settings.yml</b> </div> <div class=3D"codeContent panelContent pdl">=20 <pre class=3D"brush: bash; gutter: false; theme: Eclipse" style=3D"font-siz= e:12px;">parameters: acme_demo.default.foo_setting: foo: ~ some: ~ planets: [Earth] an_integer: 0 enabled: false j_adore: [les_sushis]</pre>=20 </div> </div> <div class=3D"code panel pdl" style=3D"border-width: 1px;"> <div class=3D"codeHeader panelHeader pdl" style=3D"border-bottom-width: 1px= ;"> <b>Semantic config (ezpublish.yml / config.yml)</b> </div> <div class=3D"codeContent panelContent pdl">=20 <pre class=3D"brush: bash; gutter: false; theme: Eclipse" style=3D"font-siz= e:12px;">acme_demo: system: sa_group: foo_setting: foo: bar some: thing planets: [Mars, Venus] an_integer: 123 # Assuming "sa1" is part of "sa_group" sa1: foo_setting: an_integer: 456 enabled: true j_adore: [le_saucisson, la_truite_a_la_vapeur]</pre>=20 </div> </div> <p>Result using <code>ContextualizerInterface::MERGE_FROM_SECOND_LEVEL= </code> option:</p> <div class=3D"code panel pdl" style=3D"border-width: 1px;"> <div class=3D"codeContent panelContent pdl">=20 <pre class=3D"brush: bash; gutter: false; theme: Eclipse" style=3D"font-siz= e:12px;">parameters: acme_demo.sa1.foo_setting: foo: bar some: thing planets: [Earth, Mars, Venus] an_integer: 456 enabled: true j_adore: [les_suhis, le_saucisson, la_truite_a_la_vapeur]</pre>=20 </div> </div> <div class=3D"confluence-information-macro confluence-information-macro-inf= ormation"> <span class=3D"aui-icon aui-icon-small aui-iconfont-info confluence-informa= tion-macro-icon"></span> <div class=3D"confluence-information-macro-body"> <p>There is also another option, <code>ContextualizerInterface::UNIQUE</cod= e>, to be used when you want to ensure your array setting has unique values= . It will only work on normal arrays though, not hashes.</p> </div> </div> <h4 id=3D"HowtoexposeSiteAccessawareconfigurationforyourbundle-Limitations"= >Limitations</h4> <p>A few limitation exist with this scope hash merge:</p> <ul class=3D"task-list"> <li>Semantic setting name and internal name will be the same (like <co= de>foo_setting</code> in the examples above).</li> <li>Applicable to 1st level semantic parameter only (i.e. settings right un= der the SiteAccess name).</li> <li>Merge is not recursive. Only 2nd level merge is possible by using = <code>ContextualizerInterface::MERGE_FROM_SECOND_LEVEL</code> option.<= /li> </ul> <h2 id=3D"HowtoexposeSiteAccessawareconfigurationforyourbundle-Dedicatedmap= perobject">Dedicated mapper object</h2> <p><span>Instead of passing a callable to <code>$processor->mapConf= ig()</code>, an instance of <code>eZ\Bundle\EzPublishCoreBundle\Depend= encyInjection\Configuration\SiteAccessAware\ConfigurationMapperInterface</c= ode> can be passed.</span></p> <p><span>This can be useful if you have a lot of configuration to map and d= on't want to pollute your DIC extension class (better for maintenance).</sp= an></p> <h3 id=3D"HowtoexposeSiteAccessawareconfigurationforyourbundle-Merginghashv= aluesbetweenscopes.1">Merging hash values between scopes</h3> <p><span><span>As specified above, </span><code>$contextualizer->ma= pConfigArray()</code><span> is not to be used within the </span><= em>scope loop</em><span>, like for simple values. When using a closure/call= able, you usually call it before or after </span><code>$processor->= mapConfig()</code><span>. For mapper objects, a dedicated interface can be = used: </span><code>HookableConfigurationMapperInterface</code><span>, = which defines 2 methods: </span><code>preMap()</code><span> and&n= bsp;</span><code>postMap()</code><span>.</span></span></p> <p><span><br></span></p> <p> </p> </div> </body> </html> ------=_Part_3539_301935156.1485853538225--