Magento issues when saving Categories - php

I have some issues saving categories in Magento 2.3.5, when I click save after changing the SEO information (Meta Title, Meta description and Meta Keywords) gives me this error.
Argument 1 passed to Magento\Catalog\Model\Category\FileInfo::removeStorePath() must be of the type string, array given, called in /home/adminpsol2016/public_html/vendor/magento/module-catalog/Model/Category/FileInfo.php on line 167
here you can see a screenshot of the problem.

This gave me quite a headache but finally managed to get to the bottom of it; my case is as follow:
Repro:
add a custom category attribute with backend_model := Magento\Catalog\Model\Category\Attribute\Backend\Image
Have the category form save operation fail for whatever reason (e.g. have a plugin on the category model save function which throws an Exception)
Reason:
If you look at https://github.com/magento/magento2/blob/2.4-develop/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php#L240 you'll see that this has the effect of storing the entire POST data of the current form request to session (also the LocalizedException block does the same).
Later on, this data is restored in https://github.com/magento/magento2/blob/2.4-develop/app/code/Magento/Catalog/Controller/Adminhtml/Category/Edit.php#L95 and immediately after the form information for the image attribute is stripped/cleared.
This of course does not handle any custom attribute of Image type we might have defined for our category entity.
Solution:
I added an after* plugin (in adminhtml area only) on \Magento\Framework\Session\SessionManager::__call, where I explicitly check that the invoked method is getCategoryData: if this is the case, I fetch all the custom category image attributes, and strip them from the returned array like Category/Edit does.
This way any further exception message is correctly displayed in the backoffice (granted it extends LocalizedException)

Just to expand on the answer from Francesco Salvi which really helped me with the same problem, this is how we implemented that solution:
etc/adminhtml/di.xml
<?xml version="1.0" ?>
<config>
<type name="Magento\Framework\Session\SessionManager">
<plugin name="pluginNameGoesHere" type="Vendor\Namespace\Plugin\StripCustomImage" />
</type>
</config>
plugin/StripCustomImage.php
<?php
namespace Vendor\Namespace\Plugin;
class StripCustomImage
{
public function after__call($method, $response, ...$args)
{
if ($args[0] === 'getCategoryData') {
if (isset($response['widget_image']['delete'])) {
$response['widget_image'] = null;
} else {
unset($response['widget_image']);
}
}
return $response;
}
}
Where 'widget_image' is the attribute name for the custom category image we created in another module that was causing us the pain.

Related

Magento: adding link in head to every frontend page

I have a module that needs a canonical link injected into <head> on literally every page on frontend. Is there a way to do it? Currently, given my module doesn't need its own page on frontend, nor any controllers whatsoever, I have only set the helper in my config.xml. Now, I do have an xml in layout, but the problem is that I need to change canonical link attributes based on user input(in admin), so XML doesn't fit. Yes, I could indeed fopen said fronted layout xml file, then replace what I need, then write new content back to it, but I wanted to check first whether there's other way for achieving that.
You can hook in on the core_block_abstract_prepare_layout_before event and use the Head block's addLinkRel method to add the link tag.
In your config.xml you need to define an observer like so:
<events>
<core_block_abstract_prepare_layout_before>
<observers>
<your_module>
<class>Your_Module_Model_Observer</class>
<method>addCanonicalLink</method>
</your_module>
</observers>
</core_block_abstract_prepare_layout_before>
</events>
Create an observer class in the Model directory
<?php
class Your_Module_Model_Observer {
public function addCanonicalLink(Varien_Event_Observer $observer) {
$block = $observer->getData('block');
if ($block->getNameInLayout() === 'head') {
// this will add <link rel="canonical" href="http://your-url.com">
$block->addLinkRel('canonical', 'http://your-url.com');
// If you need more attributes on the link tag use addItem instead
// This will add <link rel="canonical" href="http://your-url" attr="Your Attribute">
// $block->addItem('link_rel', 'http://your-url', 'rel="canonical" attr="Your Attribute"')
}
}
}
Update:
Since the core head.phtml template files run echo $this->getChildHtml() (render all children) it is possible to insert tags by adding a core/text block as a child and add a text string to it (just like you already tried with xml). If addItem doesn't fit your needs, this is more flexible as you can insert any string this way. Replace thie $block->addLinkRel line with
$canonical = $block->getLayout()->createBlock('core/text')
->setText('<link rel="canonical" href="http://your-url.com">')
$block->append($canonical);

Ajax in magento (load product view block)

What I want to achieve:
Clicking on a product link/image (at least in certain areas) to open a pop-up with the full product information (basically all the contents of the product view page).
What I did/tried so far:
created all the stuff outside the ajax php code (the module, links, templates, rewrites)
created the ajax controller (which can be accessed with a link similar to: http://test.com/index.php/ajaxproductview/ajax/index/id/2 ).
to follow various tutorials ( like this or this ) - that helped me get this far. But I don't want to load my custom block, I want the default product view block(s).
tried to add some code in the indexAction(). It gets there, but the code fails. I don't get any errors/notices/reports, just what it seems like an infinite loop that kills my processor.
$body = $this
->getLayout()
->createBlock('product.info') // taken from catalog.xml
->toHtml();
$this->getResponse()->setBody($body);
All the other pages work fine, and it's a fresh magento with only magneto and my module installed and activated.
My AJAX function simply gets this HTML response, puts it into a div, and opens a pop-up.
My question(s) is(are) - how can I set the product id, so the block knows what product to load, and how can I load this block correctly. I also tried something similar to this:
Thank you.
PS: I also tried this:
$layout = $this->getLayout();
$update = $layout->getUpdate();
$update->load('catalog_product_view');
$layout->generateXml();
$layout->generateBlocks();
$output = $layout->getOutput(); // $output is an empty string
The Product controller uses a helper to set the active product. You should be able to do the same in your controller!
Try this before you do your layouting:
$productId = (int) $this->getRequest()->getParam('id');
Mage::helper('catalog/product')->initProduct($productId, $this);
Another thing to be aware of:
If you add a block like the product.info block. It needs additional child blocks if it calls them in its template file.
It would be easiest to use a custom layout xml file. You can then add a specific layout for your action handle (your action handle consists of your routers node in your module's etc/config.xml file under <frontend><routers>, e.g. <Yourmodule> node, make sure to lowercase it! And then with underscores add the controller name and action name, in your case index_index) like this:
<yourmodule_index_index>
<remove name="right"/>
<remove name="left"/>
<block type="catalog/product_view" name="root" output="toHtml" template="catalog/product/view.phtml">
<!-- Add all the child blocks you need -->
</block>
</yourmodule_index_index>
This makes the view.phtml the root block which renders itself using its toHtml method.
Therefore, in your controller action, all you need is my two lines above and then:
$this->loadLayout();
$this->renderLayout();

Magento translation of dynamic/php created text fails

I have a function which adds labels with numbers to form fields in a registration form. For each additional input field it adds a label like Address-2, Address-3 etc. I want to use a CSV translation file to change these labels from "Address-2" to "Number", "Address-3" to "District" etc. but it does not work. I have the correct path to the CSV as I have other text in the file which is translated correctly.
I am using the following code:
<?php for ($_i=2, $_n=$this->helper('customer/address')->getStreetLines(); $_i<=$_n; $_i++): ?>
<label for="<?php echo $this->getPrefix();?><?php echo $this->__('_street%s', $_i) ?>" <?php echo $this->__('Address %s', $_i) ?>
</label>
<?php endfor;?>
But Magento does not translate these labels, I assume due to the %s variable, which is part of the translation.
I tried different combinations in the CSV file like "Address 2", "Address "2"" but it did not work. Any ideas or suggestions on how to get this translated (either by CSV or changing the PHP code itself)?
Generally, you either store translations for entity data in the database and retrieving it by store scope. This is one of the uses of EAV storage.
Another approach would be to store these translations in custom themes and have the theme change per store.
In your case, the deciding factors for me would be (1) whether these forms which you are storing in the DB are really arbitrarily configurable or (2) if this is to be a distributed module - either of these would indicate EAV storage. Otherwise, go the theme translation route.
Update based on OP comment
"I need the variable translated" means that you are (conventionally) limited to storing the translation in the database against the entity, using the store scope. You do this any number of ways, but given that this is an extension to another extension, messing with DB schema seems out of the question. You could also manipulate inline translation, but this seems hackish (curious to hear otherwise).
This is a case where the core_block_abstract_to_html_after event may be used. The event accepts the block instance AND the rendered html. In your event observer you could perform the translation via string replace, but because this event is fired for all blocks you would want to configure it as a singleton AND test for the block type.
<?php
class Ns_Mn_Model_FormTranslate
{
public function translateLabelValues(Varien_Event_Observer $o)
{
if ($o->getBlock() instanceof The_Specific_Block_Class) {
$html = $o->getHtml();
$html = //your translation logic here
$o->setHtml($html); //this will be used
}
}
}
Main caveat here is that block_html cache will not incorporate this transformed output. Alternatively, rewrite the original class using config-based class rewrite and add transformation logic into the _html() method.

Drupal View (Page) vs Taxonomy

I have the following problem:
I use taxonomys (tx) as tags. They can be added when the node is created. So I don't know how many tx I have or what ID they have.
The path of the tx is like the following:
/foo/element1
/foo/element2
/foo/element3
...
The secound element is the tx.
Now I want to use a view (page) to handle the tx-path:
/foo/%
The problem is, when I open a path like the one on top I see the theme of the node-taxonomy.tpl.php but not the style I set in the view.
Whenever I open a path in the form (/foo/not-a-tx) I can see the output of the view.
Could someone give me a hint how to get out the view output but not the tx-output?
Thanks
Sebastian
I solved the problem with this way:
I use a view block (not a page)
I added a new output area in my ,info file
I use this way to show only the vocab
I show the block in the new area online bei foo/*
It works Okay for me.
Thx to every one.
Do you want to get rid of the taxonomy pages completely?
If so, you can use a hook_menu_alter() and unset the taxonomy page.
EX.
hook_menu_alter(&$items) {
unset($items['taxonomy/term/%taxonomy_term']);
}
You'd have to look at the $items array to pinpoint the name of the registered menu path, but I think this is it.
This will remove the taxonomy page for all vocabularies however.
Actually you need to make a view to override the internal drupal path of the taxonomy term page: taxonomy/term/% (where % is the taxonomy id) and not the aliased path, which in your case is foo/%
[Optional but saves work: There is already an example view that is bundled with Drupal that implements the taxonomy view. Go to Views > List and you will see the the view is greyed out and it is called
Default Node view: taxonomy_term (default)
All you need to do is enable it and modify it to your needs]
Don't worry about the aliases. You can define your URL pattern at /admin/build/path/pathauto (make sure pathauto module is enabled. You can download it at http://drupal.org/project/pathauto ). In your case the pattern would be foo/[cat] where [cat] is a token for category. Make sure you enter this pattern under Taxonomy Term paths in the pathauto automated alias settings.

How to use PHP in the header of a drupal view?

I have a drupal view, which displays a content type filtered by a url argmuent (e.g. category). Now i'd like to add a link in top of my view, which allows my users to add a new node. In the add form the argument field should be prepopulated with the value from the filter.
I think to archive this, i have to install the prepopulate module and add some php-code to the header section of my view, which generates the link.
Any suggestions how to manage this?
If you are wanting to add php code to get the arguments passed to views in the views header section, do the following:
Make sure that the PHP filter is turned on; this is a module that can be enabled
In the header section use the following code:
$view = views_get_current_view();
// [0] is first arg, [1] is second etc.
$argumentOutput = $view-> args[0];
Remember to have the input format set to PHP code. This will get you the argument passed to views.
It would be a lot easier to override the template for the view and insert the code/markup there. Check out the Theming infomation inside the view to find out what to call your template.

Categories