Set template priority for 3rd party plugins - twig

There is a new feature since Shopware 6.4.13.0 where you can set getTemplatePriority() in the bundle class to influnce the template loading order.
We currently have a ordered list of how we want the plugins to be loaded and set the installed_at date. It contains own plugins (custom/ folder) as well as 3rd party plugins.
Is there a way to port this to the new functionality (seems necessary according to https://stackoverflow.com/a/74139837/288568) ?
As I understand it only the plugin it self can set the setTemplatePriority() ? How do we inject / change such a priority for 3rd party plugins?
It might be possible to decorate the bundle class?
Here is the current situation:
'DmfManuTheme',
'CustomImporter',
'OwnMoreCustomFields',
'CustomListingHeader',
'CustomPrintLayout',
'DevertPdfExport',
'TonurSeoFilterLandingpages6', // must be before CustomTheme/CustomProductListing as it extends listing.html.twig
'CustomApplicationManagement',
'CustomProductListing',
'FroshProductCompare',
'CustomRegistrationProcess',
'mmeesRangeSliderPro',
'CustomProductFilter', // must be after mmeesRangeSliderPro
'CustomContactBanner',
'OwnRadioPropertyFilter',
'OwnHierarchicalAttributes',
'CustomLocalMerchant',
'MoorlFoundation',
'MoorlMerchantFinder',
'MoorlMerchantPicker',
'CustomTheme', // must be after the MoorlMerchant plugins
'CustomProductDownloads',
'CustomFreeSampleOrder',
'NetzpBlog6',
'CrswCleverReachOfficial',
'MoorlFormBuilder',
'NetzpSearchAdvanced6',
'CustomProductNotification',
'CogiEtracker',
'ApplifactionGoogleMapsPlugin',
Yes, this is quite some amount of plugins. We try to make one small plugin for each customization instead of big monolithique custom-plugins.
Own + Custom are our namespaces and there we could set the template priority. But I believe the full problem can not be solved that way.
EDIT:
Is it maybe possible to decorate \Shopware\Core\Framework\Bundle - I doubt it.
Or would a compiler pass help?

There is the BundleHierarchyBuilder service you could decorate. Implement the method buildNamespaceHierarchy and get the return value by calling it on the decorated service. That should give you key value pairs with the key being the bundle name and the value the priority received from the bundle. You can then set the priority for each bundle and re-sort. Note that if you use template-extending apps (not bundles), you might have to completely re-implement the method in the decorator as their priority is overwritten by the version after the initial sort.
public function buildNamespaceHierarchy(array $namespaceHierarchy): array
{
$hierarchy = $this->decorated->buildNamespaceHierarchy($namespaceHierarchy);
$extensions = array_diff_key($hierarchy, $namespaceHierarchy);
$extensions['MoorlMerchantFinder'] = 5;
$extensions['TonurSeoFilterLandingpages6'] = 3;
// ...
asort($extensions);
return array_merge(
$extensions,
$namespaceHierarchy
);
}

Related

CodedUI- Best way to create and use UIObject Repository( that requires minimum effort when UI changes)

I started working with CodedUI few months before to automate a desktop Application(WPF).
Just checking out for the best ways to create a framework for my Application.
As, I have seen in other automation tools, I feel the heart of an automation framework using any tool(UI Based) is the way it's object Repository is created i.e. how well the UI objects are defined. A Cleaner and well defined Object Repository always proves to be very helpful when it comes to updating your tests.
I am trying to discover the best way to store my UIObjects so that in case of any UI changes in my Application, I have to put minimum effort to update my automation test.
Also, If an Object changes in application, updating it only at one place should solve the problem.
This can be any kind of change like :
->change in just a property(This I feel would be very easy to update in automation Test. The best and Easiet way I feel is to simply update the .uitest file(the xml file) if possible.)
->change in hierarchy and position
->entirely new object added
For the 2nd and 3rd changes, updating scripts become a difficult job, esp if the UIObject is being referred at may places, in many TestMethods, or Modules.
Also, I have generally seen that in Test Methods, Variable Declarations are done to create a reference to the UIMap objects and those variables are further used in the TestMethod Code.
So, in this case If the UI of my application changes, I will have to update the variable decalaration in each of the Test Methods. I want to reduce this effort to changing the variable decalaration only at one place. OfCourse, I cannot have all the code inside only one Test Method. One way that came to my mind is as:
Can't I have simply one common place for all these Variable decalarations. We can give a unique and understandable name to each UIObject e.g.: The decalratoions will look like:
UITabPage UITabPage = this.UIMap.UISimWindow.UISelectEquipmentTabList.UITabPage;
WpfRow UIRow = this.UIMap.UISimWindow.UISelectEquipmentTabList.UITabPage.UIEquipmentDetailsTable.UIRow;
WpfText UIEquipmentTagText = this.UIMap.UISimWindow.UISelectEquipmentTabList.UITabPage.UIEquipmentDetailsTable.UIRow.UITagCell.UIEquipmentTagText;
WpfCheckBox UIEquipmentCheckBox = this.UIMap.UISimWindow.UISelectEquipmentTabList.UITabPage.UIEquipmentDetailsTable.UIRow.UICheckBoxCell.UICheckBox;
....
....
and use these variables wherever required. Hence, In case of any chnages also, there will be only one place where you will need to update thse objects.
But for this, These varaibles must be made STATIC. What can be problem with making these Object Variables static?
Please provide your suggestion on this topic. May be what I am thinking is not possible or practical. I just want to choose the best way to start with before I go too far with the automation scripts and realize later that my approach wasn't a good one.
Thanks in Advance,
Shruti
Look into using descriptive programming instead of using the UIMaps.
Make a static class with generic functions to assist. Going to give you some examples of how to set it up.
For example:
public WinWindow parentwin(string ParentControlName)
{
var parentwin = new WinWindow();
parentwin.SearchProperties.Add("Control Name", ParentControlName);
return parentwin;
}
public WinWindow childwin(string ChildWinControlName, string ParentControlName)
{
var childwin = new WinWindow(parentwin(ParentControlName));
childwin.SearchProperties.Add("Control Name", ChildWinControlName);
return childwin;
}
public WinButton button(string ButtonName,string ChildWinControlName, string ParentControlName)
{
var childwin = childwin(ChildWinControlName,ParentControlName);
var button = new WinButton(childwin);
button.SearchProperties.Add("Name", ButtonName);
}
public void ClickButton(string ButtonName,string ChildWinControlName, string ParentControlName)
{
var button = button(ButtonName,ChildWinControlName,ParentControlName);
Mouse.Click(button);
}
public void ChangeFocus(WinWindow NewFocus)
{
var NewFocus = new NewFocus();
NewFocus.SetFocus();
}
public void ChangeFocus(WinWindow NewFocusChild, string c)
{
var a = new NewFocus();
a.SetFocus();
}
ChangeFocus(childwin("WelcomeForm", "MainForm");
ClickButton("&OK", "WelcomeForm", "MainForm");

JsConfig<MyClass>.ExcludePropertyNames example, not working for me

Trying to exclude properties from a model from being included during serialization.
I am using the following syntax:
JsConfig<MyTestClass>.ExcludePropertyNames = new[] { "ShortDescription" };
Just after that I have the following:
return (from o in __someProvider.GetAll() select (new
{
o.Name,
o.ShortDescription
o.InsertDate
}).TranslateTo<MyTestClass>()).ToList()
However once result is returned from the method, it still contains "ShortDescription" field in the Json. Am I doing something wrong?
JsConfig<T>.ExcludePropertyNames appears to be checked only once for each type, in a static constructor for TypeConfig<T>. Thus, if you are configuring ExcludePropertyNames in your service class, just before returning your response, it might be too late -- the TypeConfig properties may already be set up and cached for MyTestClass. I was able to reproduce this.
A more reliable alternative is to move all of your JsConfig<T> configuration to your AppHost setup code.
If you really do need to do this in your service class, e.g. if you are only conditionally excluding property names, then an alternative approach would be to ensure that JsConfig.IncludeNullValues is false (I believe it is by default) and in your service code set ShortDescription to null when appropriate.

Swap out repositories with a flag

I have an IRepository< T > interface with many T's and several implementations (on-demand DB, web service, etc.). I use AutoFac to register IRepository's for many T's depending on the kind of repository I want for each T.
I also have a .NET-caching-based implementation that looks for T's in cache and then calls a 'real' IRepository.Find to resolve a cache miss. It is constructed something like this:
new CachingRepository(realRepository, cacheImplementation);
I would like to use a configuration flag to decide if AutoFac serves up caching-based IRepository's or the 'real things.' It seems like 'realRepository' comes from asking AutoFac to resolve IRepository < T > but then what do clients get when they ask to resolve the same interface? I want them to get the CachingRepository if the flag is set.
I can't get my head around how to implement this flag-based resolution. Any ideas?
Simplest Option: Conditional Registration Delegate
There are a number of ways to do this. Using your cache setting in a registration delegate is probably the simplest (and illustrates the power of delegate registrations):
var builder = new ContainerBuilder();
bool cache = GetCacheConfigSetting(); //Up to you where this setting is.
builder.Register(c => cache ? (IRepository<string>)new CachingRepository<string>(new RealRepos<string>(), new CacheImpl()) : new RealRepos<string>());
The code above will only read the cache config once. You could also include the GetCacheConfigSetting() in the registration delegate. This would result in the setting being checked on every Resolve (assuming InstancePerDependency).
Other Options: Autofac Decorators and Modules
There are some more advanced features of Autofac that you may also find useful. The cache class in your question is an example of the Decorator Pattern. Autofac has explicit support for decorators. It also has a nice model for structuring your registrations and managing configuration information, called Modules.

Code Contracts and Auto Generated Files

When I enabled code contracts on my WPF control project I ran into a problem with an auto generated file which was created at compile time (XamlNamespace.GeneratedInternalTypeHelper). Note, the generated file is called GeneratedInternalTypeHelper.g.cs and is not the same as the GeneratedInternalTypeHelper.g.i.cs which there are several obsolete blog posts about.
I'm not exactly sure what its purpose is, but I am assuming it is important for some internal reflection to resolve XAML. The problem is that it does not have code contracts, nor is the code contract system smart enough to recognize it as an auto generated file. This leads to a bunch of errors from the static checker.
I tried searching for a solution to this problem, but it seems like nobody is developing WPF controls and using code contracts. I did come across an interesting attribute, ContractVerificationAttribute, which takes a boolean value to set whether the assembly or class is to be verified. This allows you to decorate a class as not verified. Sadly the GeneratedInternalTypeHelper is regenerated with every compile, so it is not possible to exclude just this one class. The inverse scenario is possible though, decorate the assembly as not verified and then opt in for every class.
To mitigate the obvious hack I wanted to create a test that would at least verify that the exposed classes have code contract verification with a test like the following to ensure that own classes were at least being verified:
[Fact]
public void AllAssemblyTypesAreDecoratedWithContractVerificationTrue()
{
var assembly = typeof(someType).Assembly;
var exposedTypes = assembly.GetTypes().Where(t=>!string.IsNullOrWhiteSpace(t.Namespace) && t.Namespace.StartsWith("MyNamespace") && !t.Name.StartsWith("<>"));
var areAnyNotContractVerified = exposedTypes.Any(t =>
{
var verificationAttribute = t.GetCustomAttributes(typeof(ContractVerificationAttribute), true).OfType<ContractVerificationAttribute>();
return verificationAttribute.Any() && verificationAttribute.First().Value;
});
Assert.False(areAnyNotContractVerified);
}
As you can see it takes all classes in the controls assembly and finds the one from the company namespace which are not also auto generated anonymous types (<>WeirdClassName).
(I also need to exclude Resources and settings, but I hope you get the idea).
I'm not loving the solution since there are ways of avoiding contract verification, but currently it's the best I can come up with. If anyone has a better solution, please let me know.
So you can treat this class exactly like you would treat any other "3rd party" class or library. I'm sure certain assumptions would hold with the interaction with this generated class so at the interaction points, decorate your own code with Contract.Assume(result != null) or similar.
var result = new GennedClass().GetSomeValue();
Contract.Assume(result != null);
What this does is translate into an assertion that is checked at run time, but it allows the static analyzer to reason about the rest of the code that you do control.

Is it secure to use a controller, module or action name to set include paths?

I want to set up include paths (and other paths, like view script paths) based on the module being accessed. Is this safe? If not, how could I safely set up include paths dynamically? I'm doing something like the code below (this is from a controller plugin.)
public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request) {
$modName = $request->getModuleName();
$modulePath = APP_PATH.'/modules/'.$modName.'/classes';
set_include_path(get_include_path().PATH_SEPARATOR.$modulePath);
}
I'm not sure whether it is safe or not, but it doesn't sound like the best practice. What if someone entered a module name like ../admin/? You should sanitize the module name before using it.
It's fine as long as you sanitize your variables before using them, but it won't be very performant. Fiddling with include paths at runtime causes a serious impact performance.
You're trying to load models/helpers per module? You should look at Zend_Application:
Zend_Application provides a bootstrapping facility for applications which provides reusable resources, common- and module-based bootstrap classes and dependency checking. It also takes care of setting up the PHP environment and introduces autoloading by default.
Emphasis by me

Resources