XSLTForms using position() to select repeat element from another instance - xpath

Background:
Building a form (using XSLTForms) that is submitted, then the user may resubmit at a later date. We want the user to see the values of their previous submission next to the form input, so we're loading 2 instances 'data-set' and 'old-data-set'. This is working in most instances, but not where we have repeats. for whatever reason the position() method always returns '1' when calling data from the 'old-data-set' instance. For example:
<xf:repeat nodeset="instance('data-set')/references/reference">
<xf:group ref=".">
<xf:label>Reference <xf:output value="position()"/></xf:label>
<xf:input ref="/org_name">...</xf:input>
<xf:output ref="instance('old-data-set')/references/reference[position()]/org_name"/>
</xf:group>
</xf:repeat>
The position() method works in the label, but always returns '1' when attempting to get the value from the second instance. For example the above produces:
<label>Reference 1</label>
<input>Org name 1</input>
<output>Old org name 1</output>
<label>Reference 2</label>
<input>Org name 2</input>
<output>Old org name 1</output>
<label>Reference 3</label>
<input>Org name 3</input>
<output>Old org name 1</output>
How can I call the position() of the repeat so I can use it to get the correct value from the 'old-data-set' instance?

The reason this is happening is that inside the predicate (inside the [], the context is different and position() refers to the position of the old-data-set reference node rather than the current reference node in your iteration.
I'm not familiar with XSLTForms, but how's this?
<xf:repeat nodeset="instance('data-set')/references/reference">
<xf:group ref=".">
<xf:label>Reference <xf:output value="position()"/></xf:label>
<xf:input ref="/org_name">...</xf:input>
<xf:output
ref="instance('old-data-set')/references/reference[count(current()/preceding-sibling::reference) + 1]/org_name"/>
</xf:group>
</xf:repeat>

Related

Is it possible in XPATH to find an element by attribute value, not by name?

For example I have an XML element:
<input id="optSmsCode" type="tel" name="otp" placeholder="SMS-code">
Suppose I know that somewhere there must be an attribute with otp value, but I don’t know in what attribute it can be, respectively, is it possible to have an XPath expression of type like this:
.//input[(contains(*, "otp")) or (contains(*, "ode"))]
Try it like this and see if it works:
one = '//input/#*[(contains(.,"otp") or contains(.,"ode"))]/..'
print(driver.find_elements_by_xpath(one))
Edit:
The contains() function has a required cardinality of first argument of either one or zero. In plain(ish) English, it means you can check only one element at a time to see if it contains the target string.
So, the expression above goes through each attribute of input separately (/#*), checks if the attribute value of that specific attribute contains within it the target string and - if target is found - goes up to the parent of that attribute (/..) which, in the case of an attribute, is the node itself (input).
This XPath expression selects all <input> elements that have some attribute, whose string value contains "otp" or "ode". Notice that there is no need to "go up to the parent ..."
//input[#*[contains(., 'otp') or contains(., 'ode')]]
If we know that "otp" or "ode" must be the whole value of the attribute (not just a substring of the value), then this expression is stricter and more efficient to evaluate:
//input[#*[. ='otp' or . = 'ode']]
In this latter case ("otp" or "ode" are the whole value of the attribute), if we have to compare against many values then an XPath expression of the above form will quickly become too long. There is a way to simplify such long expression and do just a single comparison:
//input[#*[contains('|s1|s2|s3|s4|s5|', concat('|', ., '|'))]]
The above expression selects all input elements in the document, that have at least one attribute whose value is one of the strings "s1", "s2", "s3", "s4" or "s5".

three dependent drop down list opencart

I want to make 3 dependents drop down list, each drop down dependent to the previous drop down, so when I select an item from first drop down , all data fetch from database and add to second drop down as item.
I know how to do this in a normal php page using ajax, but as opencart uses MVC I don't know how can I get the selected value
Basically, you need two things:
(1) Handling list changes
Add an event handler to each list that gets its selected value when it changes (the part that you already know), detailed tutorial here in case someone needed it
Just a suggestion (for code optimization), instead of associating a separate JS function to each list and repeating the code, you can write the function once, pass it the ID of the changing list along with the ID of the depending list and use it anywhere.
Your HTML should look like
<select id="list1" onchange="populateList('list1', 'list2')">
...
</select>
<select id="list2" onchange="populateList('list2', 'list3')">
...
</select>
<select id="list3">
...
</select>
and your JS
function populateList(listID, depListID)
{
// get the value of the changed list thorugh fetching the elment with ID "listID"
var listValue = ...
// get the values to be set in the depending list through AJAX
var depListValues = ...
// populate the depending list (element with ID "depListID")
}
(2) Populating the depending list
Send the value through AJAX to the appropriate PHP function and get the values back to update the depending list (the part you are asking for), AJAX detailed tutorial here
open cart uses the front controller design patter for routing, the URL always looks like: bla bla bla.bla/index.php?route=x/y/z&other parameters, x = folder name that contains a set of class files, y = file name that contains a specific class, z = the function to be called in that class (if omitted, index() will be called)
So the answer for your question is:
(Step 1) Use the following URL in your AJAX request:
index.php?route=common/home/populateList
(Step 2) Open the file <OC_ROOT>/catalog/controller/common/home.php , you will find class ControllerCommonHome, add a new function with the name populateList and add your logic there
(Step 3) To use the database object, I answered that previously here
Note: if you are at the admin side, there is a security token that MUST be present in all links along with the route, use that URL:
index.php?route=common/home/populateList&token=<?php echo $this->session->data['token'] ?> and manipulate the file at the admin folder not the catalog
P.S: Whenever the user changes the selected value in list # i, you should update options in list # i + 1 and reset all the following lists list # i + 2, list # i + 3 ..., so in your case you should always reset the third list when the first list value is changed
P.P.S: A very good guide for OC 1.5.x => here (It can also be used as a reference for OC 2.x with some modifications)

How to get full classname with xpathquery?

I'm parsing through a HTML document and I need a class name of a div. I know a part of the class name (that never changes) but I need the full class name.
Here's the code I use:
$doc = new DOMDocument;
$doc->loadHTMLFile('http://some_website.com');
$xpath = new DOMXPath($doc);
$classname_of_the_div=$xpath->query('//div[#class="part_of_the_class_name_that_never_changes"]');
When I var_dump() the $classname_of_the_div and $classname_of_the_div->item(0) the result is:
object(DOMNodeList)#3 (1) { ["length"]=> int(0) }
NULL
I know that $classname_of_the_div=$xpath->evaluate('string(//div[#class="part_of_the_class_name_that_never_changes"])'); gives me the content of the div but how do I get the full class name?
P.S.: The part of the classname is separated from the rest of the class name by white spaces, so it's not really a part of the class. The div has just several classes.
I mean the div has several class names like - I want to select it by "class2" for example and receive the
full class string including "class1 class2 class3"
Then, an XPath expression like
//div[#class="part_of_the_class_name_that_never_changes"]
will never yield a result, save for the situation that a particular div element only has one class, that is, the one "that never changes". That's because the XPath expression above means:
Select div elements that have a class attribute whose string value
exactly corresponds to "part_of_the_class_name_that_never_changes".
But imagine the following situation:
<div class="part_of_the_class_name_that_never_changes other_class1 other_class2"/>
Then, you would need to change the expression to:
//div[contains(#class,'part_of_the_class_name_that_never_changes')]/#class
The expression means:
Look for div elements that have a class attribute whose string
value contains the string
"part_of_the_class_name_that_never_changes" and return the attribute
value.

Xpath how to get element by index AND attribute

Given this xml:
<mets:techMD ID="techMD014">
<mets:mdWrap MDTYPE="PREMIS:OBJECT">
<mets:xmlData>
<premis:object
xsi:type="premis:file"
xsi:schemaLocation="info:lc/xmlns/premis-v2
http://www.loc.gov/standards/premis/v2/premis-v2-0.xsd">
<premis:objectIdentifier>
<premis:objectIdentifierType
>filepath</premis:objectIdentifierType>
<premis:objectIdentifierValue
>bib1234_yyyymmdd_99_x_performance.xml</premis:objectIdentifierValue>
</premis:objectIdentifier>
</premis:object>
</mets:xmlData>
</mets:mdWrap>
</mets:techMD>
<mets:techMD ID="techMD015">
<mets:mdWrap MDTYPE="PREMIS:OBJECT">
<mets:xmlData>
<premis:object
xsi:type="premis:representation"
xsi:schemaLocation="info:lc/xmlns/premis-v2
http://www.loc.gov/standards/premis/v2/premis-v2-0.xsd">
<premis:objectIdentifier>
<premis:objectIdentifierType
>local</premis:objectIdentifierType>
<premis:objectIdentifierValue
>bib1234_yyyymmdd_99_x</premis:objectIdentifierValue>
</premis:objectIdentifier>
</premis:object>
</mets:xmlData>
</mets:mdWrap>
</mets:techMD>
I would like to make a xpath query that takes both index and attribute into account.
I.e can I combine these two into ONE query? (Its the stuff around the "object" element Im interested in):
//techMD/mdWrap[
#MDTYPE=\'PREMIS:OBJECT\'
]/xmlData//object[1]/objectIdentifier/objectIdentifierValue
//techMD/mdWrap[
#MDTYPE=\'PREMIS:OBJECT\'
]/xmlData//object[
#xsi:type=\'premis:file\'
]/objectIdentifier/objectIdentifierValue
Thanks!
Just replace according part to:
object[#xsi:type='premis:file'][1]
if you want first object of those who have a given xsi:type value or
object[1][#xsi:type='premis:file']
if you want the first object, providing it has a given xsi:type value.

Xpath: find an element value from a match of id attribute to id anchor

I would like to find the value of an element matched on id attribute for which I only have the ref - the bit with #, the anchor.
I am looking for the value of partyId:
< party id="partyA" >
< partyId >THEID< /partyId >
but to get there I only have the href from the following
< MyData >
< MyReference href="#partyA" />
Strip the # sign does not look good to me.
Any hints?
Because you haven't provided complete XML documents, I have to use // -- a practice I strongly recommend to avoid.
Suppose that
$vDataRef
is defined as
//MyData/MyReference/#href
and its string value is "#partyA", then one possible XPath expression that selects the wanted node is:
//party[#id=substring($vDataRef,2)]
In case the XML document has a DTD in which the id attribute of party is defined to be of type ID, then it is more convenient and efficient to use the standard XPath function id():
id(substring($vDataRef,2))
Assuming you have your ID as a variable already (lets say $myId), then try using:
//party[contains($myId, #id)]
The contains() function will look to see on each matching node whether or not the partyId attibute is in the value that you pass in.
Alternatively (as that could be considered 'ropey'), you can try:
//party[#id=substring($myId, 2, 1 div 0)]
the substring() function should be a little more precise.

Resources