How do I insert an element into XML using Linq? - linq

My XML:
<content>
<item id="1">A</item>
<item id="2">B</item>
<item id="4">D</item>
</content>
I have loaded this using XML similar to:
XDocument xDoc = new XDocument(data.Value);
var items = from i in xDoc.Element("content").Elements("item")
select i;
I want to insert another element, to end up with something like:
<content>
<item id="1">A</item>
<item id="2">B</item>
<item id="3">C</item>
<item id="4">D</item>
</content>
How do I do this using Linq2Xml?

Try this:
xDoc.Element("content")
.Elements("item")
.Where(item => item.Attribute("id").Value == "2").FirstOrDefault()
.AddAfterSelf(new XElement("item", "C", new XAttribute("id", "3")));
Or, if you like XPath like I do:
xDoc.XPathSelectElement("content/item[#id = '2']")
.AddAfterSelf(new XElement("item", "C", new XAttribute("id", "3")));

Related

Get order data with customer details in custom checkout page magento2

I have create custom checkout Step between shipping and payment step . Now i want to show all the order details and customer fillup details.
Below is the files that i have created for the Custom checkout step
Below is the file (checkout_index_index.xml) i have created for new step in the checkout :
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<body>
<referenceBlock name="checkout.root">
<arguments>
<argument name="jsLayout" xsi:type="array">
<item name="components" xsi:type="array">
<item name="checkout" xsi:type="array">
<item name="children" xsi:type="array">
<item name="steps" xsi:type="array">
<item name="children" xsi:type="array">
<!-- The new step you add -->
<item name="my-new-step" xsi:type="array">
<item name="component" xsi:type="string">Mycustom_Checkoutnew/js/view/my-step-view</item>
<!--To display step content before shipping step "sortOrder" value should be < 1-->
<!--To display step content between shipping step and payment step 1 < "sortOrder" < 2 -->
<!--To display step content after payment step "sortOrder" > 2 -->
<item name="sortOrder" xsi:type="string">1</item>
<item name="children" xsi:type="array">
<!--add here child component declaration for your step-->
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</argument>
</arguments>
</referenceBlock>
</body>
</page>
In the second file (my-step-view.js) there will be js code as follow :
define([
'ko',
'uiComponent',
'underscore',
'Magento_Checkout/js/model/step-navigator'
], function (ko, Component, _, stepNavigator) {
'use strict';
/**
* mystep - is the name of the component's .html template,
* <Vendor>_<Module> - is the name of your module directory.
*/
return Component.extend({
defaults: {
template: 'Mycustom_Checkoutnew/mystep'
},
// add here your logic to display step,
isVisible: ko.observable(true),
/**
* #returns {*}
*/
initialize: function () {
this._super();
// register your step
stepNavigator.registerStep(
// step code will be used as step content id in the component template
'shippingaddress',
// step alias
null,
// step title value
'Shipping',
// observable property with logic when display step or hide step
this.isVisible,
_.bind(this.navigate, this),
/**
* sort order value
* 'sort order value' < 10: step displays before shipping step;
* 10 < 'sort order value' < 20 : step displays between shipping and payment step
* 'sort order value' > 20 : step displays after payment step
*/
15
);
return this;
},
/**
* The navigate() method is responsible for navigation between checkout steps
* during checkout. You can add custom logic, for example some conditions
* for switching to your custom step
* When the user navigates to the custom step via url anchor or back button we_must show step manually here
*/
navigate: function () {
this.isVisible(true);
},
/**
* #returns void
*/
navigateToNextStep: function () {
stepNavigator.next();
}
});
});
Below is the html file (mystep.html) where i want to display all the data of orders and the customer
<li id="shippingaddress" data-bind="fadeVisible: isVisible">
<div class="step-title" data-bind="i18n: 'Shipping'" data-role="title"></div>
<div id="checkout-step-title"
class="step-content"
data-role="content">
<form data-bind="submit: navigateToNextStep" novalidate="novalidate">
<div class="actions-toolbar">
<div class="primary">
<button data-role="opc-continue" type="submit" class="button action continue primary">
<span><!-- ko i18n: 'Next'--><!-- /ko --></span>
</button>
</div>
</div>
</form>
</div>
</li>
In the new custom page i want show following things
1)Email 2)Contact 3 ) Address that sellected in previous step
4)Shipping method
If anyone can help me in this then please let me know
In frontend i want to show data as per image attached. go through that image

Magento 2 problem with script (map - INPOST ) in checkout - Shipping method

I am trying to add my new shipping method with a map (INPOST) but I have a problem. My map won't load. Then I try to use a case with no map, with points but still not working in the checkout. I tried to do this on the product page and it worked.
My custom shipping method working good, only problem with that map
Documentation: https://docs.inpost24.com/pages/viewpage.action?pageId=7798862
Magento version: 2.3
Bellow my codes:
app/code/Kitsune/Inpost/view/frontend/layout/checkout_index_index.xml
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<head>
<script src="https://geowidget.easypack24.net/js/sdk-for-javascript.js" src_type="url" async="async"/>
<css src="https://geowidget.easypack24.net/css/easypack.css" src_type="url"/>
</head>
<body>
<referenceBlock name="checkout.root">
<arguments>
<argument name="jsLayout" xsi:type="array">
<item name="components" xsi:type="array">
<item name="checkout" xsi:type="array">
<item name="children" xsi:type="array">
<item name="steps" xsi:type="array">
<item name="children" xsi:type="array">
<item name="shipping-step" xsi:type="array">
<item name="children" xsi:type="array">
<item name="shippingAddress" xsi:type="array">
<item name="config" xsi:type="array">
<item name="shippingMethodItemTemplate" xsi:type="string">Kitsune_Inpost/custom-method-item-template</item>
<item name="shippingMethodListTemplate" xsi:type="string">Kitsune_Inpost/custom-method-list-template</item> </item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</argument>
</arguments>
</referenceBlock>
</body>
</page>
app/code/Kitsune/Inpost/view/frontend/web/js/inpost.js
jQuery( document ).ready(function() {
easyPack.init({
instance: 'pl',
mapType: 'osm',
searchType: 'osm',
points: {
types: ['parcel_locker'],
},
map: {
useGeolocation: true,
initialTypes: ['parcel_locker']
}
})
window.onload = function() {
easyPack.dropdownWidget('easypack-widget', function(point) {
console.log(point)
});
}
});
app/code/Kitsune/Inpost/view/frontend/web/template/custom-method-item-template.html
<!-- Initialize collapsible binding -->
<tbody collapsible="as: '$collapsible_' + method.method_code">
<tr class="row">
<td class="col col-method">
<input type="radio"
class="radio"
click="element.selectShippingMethod"
ifnot="method.error_message"
ko-checked="element.isSelected"
ko-value="method.carrier_code + '_' + method.method_code"
attr="'aria-labelledby': 'label_method_' + method.method_code + '_' + method.carrier_code + ' ' + 'label_carrier_' + method.method_code + '_' + method.carrier_code,
'checked': element.rates().length == 1 || element.isSelected" />
</td>
<td class="col col-price">
<each args="element.getRegion('price')" render="" />
</td>
<td class="col col-method"
attr="'id': 'label_method_' + method.method_code + '_' + method.carrier_code"
text="method.method_title" />
<td class="col col-carrier"
attr="'id': 'label_carrier_' + method.method_code + '_' + method.carrier_code"
text="method.carrier_title" />
<!-- Column with collapsible trigger -->
<td class="col">
<!-- ko if: method.carrier_code == 'kitsune_inpost' -->
<div data-bind="mageInit: {'inpost': {}}" id="easypack-widget"></div>
<h1>eloo</h1>
<!-- /ko -->
</td>
</tr>
<!-- Row for shipping method description -->
<tr class="row" visible="$context['$collapsible_' + method.method_code].opened">
<td class="col" colspan="5" i18n="'Some description.'"/>
</tr>
<tr class="row row-error"
if="method.error_message">
<td class="col col-error" colspan="5">
<div role="alert" class="message error">
<div text="method.error_message"></div>
</div>
<span class="no-display">
<input type="radio"
attr="'value' : method.method_code, 'id': 's_method_' + method.method_code" />
</span>
</td>
</tr>
</tbody>
app/code/Kitsune/Inpost/view/frontend/web/template/custom-method-list-template.html
<div id="checkout-shipping-method-load">
<table class="table-checkout-shipping-method" markdown="1"> <thead>
<tr class="row" markdown="1">
<th class="col col-method" translate="'Select Method'" />
<th class="col col-price" translate="'Price'" />
<th class="col col-method" translate="'Method Title'" />
<th class="col col-carrier" translate="'Carrier Title'" />
<!-- Column for triggers -->
<th class="col" />
</tr>
</thead> <!-- tbody was moved inside item template --> <!-- ko foreach: { data: rates(), as: 'method'} --> <!--ko template: { name: element.shippingMethodItemTemplate} --><!-- /ko --> <!-- /ko --> </table>
</div>
app/code/Kitsune/Inpost/view/frontend/requirejs-config.js
var config = {
map: {
'*': {
inpost: 'Kitsune_Inpost/js/inpost'
}
}
}
I wanted to add just comment instead of posting an answer (but I can't comment).
Wrap your js in define and check comments I added here:
define(['domReady!'], function() {
// check here via debugger if code is executed
// then you can investigate why rest isn't working
window.easyPack.init({
instance: 'pl',
mapType: 'osm',
searchType: 'osm',
points: {
types: ['parcel_locker'],
},
map: {
useGeolocation: true,
initialTypes: ['parcel_locker']
}
});
// this also won't work as you can miss onload event
window.onload = function() {
easyPack.dropdownWidget('easypack-widget', function(point)
{
console.log(point)
});
};
});
you need be sure that easyPack will be available. Do you load it synchronously? I see some async version in documentation so you need to think about that and implement it properly.

"Field is required" error shows up on every required field once I make a selection on a select

I'm currently making a custom UI-component for the adminhtml form which extends the default UI-select element. But the issue is that when I make a selection on this new UI-select all fields have their values cleared and if they are required the error appears.
I've been debugging for a while and have reached the conclusion that this issue appears down the line when the code gets to this.value(data) and also throws an exception.
Uncaught TypeError: Cannot read property 'click' of undefined
The element which is clicked in this case is the same element from the normal UI-select.
This is the component's XML through which it's added to the form.
And yes the options shouldn't be there but I couldn't figure out where to put it otherwise to make it work.
<field name="programs" class="Mirasvit\Affiliate\Component\Filters\Type\MultiplePrograms">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="label" xsi:type="string" translate="true">Programs</item>
<item name="componentType" xsi:type="string">field</item>
<item name="formElement" xsi:type="string">select</item>
<item name="elementTmpl" xsi:type="string">Mirasvit_Affiliate/form/elements/multiple-programs</item>
<item name="component" xsi:type="string">Mirasvit_Affiliate/js/component/multiple-programs</item>
<item name="filterOptions" xsi:type="boolean">true</item>
<item name="multiple" xsi:type="boolean">false</item>
<item name="options" xsi:type="object">Mirasvit\Affiliate\Ui\Account\Source\Program</item>
<item name="tableOptions" xsi:type="object">Mirasvit\Affiliate\Ui\Program\Source\ProgramsWithTiers</item>
<item name="tableValues" xsi:type="object">Mirasvit\Affiliate\Ui\Program\Source\AccountPrograms</item>
<item name="tableFields" xsi:type="array">
<item name="name" xsi:type="array">
<item name="label" xsi:type="string">Name</item>
<item name="type" xsi:type="string">text</item>
</item>
<item name="tier" xsi:type="array">
<item name="label" xsi:type="string">Tier</item>
<item name="type" xsi:type="string">select</item>
</item>
<item name="fixed" xsi:type="array">
<item name="label" xsi:type="string">Fixed tier</item>
<item name="type" xsi:type="string">checkbox</item>
</item>
</item>
</item>
</argument>
</field>
This is the component's js file
define([
'underscore',
'jquery',
'Magento_Ui/js/form/element/ui-select',
'uiRegistry',
], function (_, $, Abstract, registry) {
'use strict';
return Abstract.extend({
defaults: {
optionsCache: [],
options: [],
listVisible: false,
tableOptions: [],
tableValues: [],
tableFields: {},
currentSelected: [],
},
toggleOptionSelected: function (data) {
if (this.lastSelectable && data.hasOwnProperty(this.separator)) {
return this;
}
this.options(this.options.without(data));
/**
* THIS IS WHERE THE COMPONENT BREAKS
*/
this.value(data);
this.listVisible(false);
return this;
},
cleanHoveredElement: function () {
if (this.hoveredElement) {
$(this.hoveredElement)
.children(this.actionTargetSelector)
.removeClass(this.hoverClass);
this.hoveredElement = null;
}
return this;
},
});
});
And this is the component's php file
class MultiplePrograms extends Select
{
public function prepare() {
$config = $this->getData('config');
/** #var ArrayHelper $helper */
$helper = ObjectManager::getInstance()->get(ArrayHelper::class);
if (isset($config['tableOptions']) && $config['tableOptions'] instanceof ArrayInterface) {
$config['tableOptions'] = $config['tableOptions']->toOptionArray();
} else {
throw new \Exception('Missing tableOptions tag for multiple-options field');
}
if (isset($config['tableFields'])) {
$config['tableFields'] = $helper->nameToColumn($config['tableFields']);
} else {
throw new \Exception('Missing tableValues tag for multiple-options field');
}
if (isset($config['tableValues']) && $config['tableValues'] instanceof ArrayInterface) {
$config['tableValues'] = $config['tableValues']->toOptionArray();
}
if (isset($config['options']) && $config['options'] instanceof ArrayInterface) {
$config['options'] = $config['options']->toOptionArray();
}
$this->setData('config', (array)$config);
parent::prepare();
}
}
I want to be able to store information in the value of this component so that it can be passed to the controller later
I found out why this was happening.
The reason it was throwing that error is because this.value() contained all the form data and thus by setting this.value({anything}) to anything, would clear all of the fields stored and trigger the errors and the observers.

How to select an element using Nokogiri

Given the following XML, I want to get the value "0123456" for Name="Cat":
xml.xpath '//Custom[Name="Cat"]'
Gives me the first custom, which is correct, but I only want the "Value" not the entire Custom node.
<body>
<Custom>
<count>1</count>
<Name>Cat</Name>
<Value>0123456</Value>
</Custom>
<Custom>
<count>2</count>
<Name>Dog</Name>
<Value>9876543</Value>
</Custom>
<body>
I only want the "Value" not the entire Custom node.
So just go on writing the path:
//Custom[Name="Cat"]/Value
I prefer to use CSS selectors over XPath, for readability, as usually CSS contains less visual noise:
require 'nokogiri'
doc = Nokogiri::HTML(<<EOT)
<body>
<Custom>
<count>1</count>
<Name>Cat</Name>
<Value>0123456</Value>
</Custom>
<Custom>
<count>2</count>
<Name>Dog</Name>
<Value>9876543</Value>
</Custom>
<body>
EOT
foo = doc.search('name:contains("Cat")').map{ |node|
node.next_element.text
}
foo # => ["0123456"]
This works because Nokogiri contains some of the jQuery CSS extensions, resulting in some useful additions.
To get the value element text you need to set the xpath as below:
doc = Nokogiri::HTML(<<EOT)
<body>
<Custom>
<count>1</count>
<Name>Cat</Name>
<Value>0123456</Value>
</Custom>
<Custom>
<count>2</count>
<Name>Dog</Name>
<Value>9876543</Value>
</Custom>
<body>
EOT
val=doc.xpath("//Custom[Name='Cat']/Value").text()
val => "0123456"

Parse XML to Ruby objects and create attribute methods dynamically?

I need to parse an XML file to Ruby objects.
Is there a tool to read attributes from XML like this
report.system_slots.items to return an array of item properties,
or report.system_slots.current_usage to return 'Available'?
Is it possible to do this with Nokogiri?
<page Title="System Slots" H1="Property" H2="Value" __type__="2">
<item Property="System Slot 1">
<item Property="Name" Value="PCI1"/>
<item Property="Type" Value="PCI"/>
<item Property="Data Bus Width" Value="32 bits"/>
<item Property="Current Usage" Value="Available"/>
<item Property="Characteristics">
<item Property="Vcc voltage supported" Value="3.3 V, 5.0 V"/>
<item Property="Shared" Value="No"/>
<item Property="PME Signal" Value="Yes"/>
<item Property="Support Hot Plug" Value="No"/>
<item Property="PCI slot supports SMBus signal" Value="Yes"/>
</item>
</item>
Look at Ox. It reads XML and returns a reasonable Ruby object facsimile of the XML.
require 'ox'
hash = {'foo' => { 'bar' => 'hello world'}}
puts Ox.dump(hash)
pp Ox.parse_obj(Ox.dump(hash))
Dumping that into IRB gives me:
require 'ox'
> hash = {'foo' => { 'bar' => 'hello world'}}
{
"foo" => {
"bar" => "hello world"
}
}
> puts Ox.dump(hash)
<h>
<s>foo</s>
<h>
<s>bar</s>
<s>hello world</s>
</h>
</h>
nil
> pp Ox.parse_obj(Ox.dump(hash))
{"foo"=>{"bar"=>"hello world"}}
{
"foo" => {
"bar" => "hello world"
}
}
That said, your XML sample is broken and won't work with OX. It WILL work with Nokogiri, though there are errors reported, which would hint that you wouldn't be able to parse the DOM correctly.
My question is, why do you want to convert the XML to an object? It is SO much easier to handle XML using a parser like Nokogiri. Using a fixed version of your XML:
require 'nokogiri'
xml = '
<xml>
<page Title="System Slots" H1="Property" H2="Value" __type__="2">
<item Property="System Slot 1"/>
<item Property="Name" Value="PCI1"/>
<item Property="Type" Value="PCI"/>
<item Property="Data Bus Width" Value="32 bits"/>
<item Property="Current Usage" Value="Available"/>
<item Property="Characteristics">
<item Property="Vcc voltage supported" Value="3.3 V, 5.0 V"/>
<item Property="Shared" Value="No"/>
<item Property="PME Signal" Value="Yes"/>
<item Property="Support Hot Plug" Value="No"/>
<item Property="PCI slot supports SMBus signal" Value="Yes"/>
</item>
</page>
</xml>'
doc = Nokogiri::XML(xml)
page = doc.at('page')
page['Title'] # => "System Slots"
page.at('item[#Property="Current Usage"]')['Value'] # => "Available"
item_properties = page.at('item[#Property="Characteristics"]')
item_properties.at('item[#Property="PCI slot supports SMBus signal"]')['Value'] # => "Yes"
Parsing a big XML document into memory can return a labyrinth of arrays and hashes that still have to be peeled apart to access the values you want. Using Nokogiri, you have CSS and XPath accessors which are easy to learn and read; I used CSS above but could easily have used XPath to accomplish the same things.

Resources