wrapping child node content in xquery - xquery

I have what I hope is a really simple question, but I'm a novice at xquery and I can't make this work:
I have the following bit of xml:
<collation>1<num>12</num> 2<num>12</num> ||
I<num>8</num>-V<num>8</num>, 1 flyleaf</collation>
That I need to transform so that becomes the content of a new node, like so:
<note displayLabel="Collation: Notes">1(12) 2(12) || I(8)-V(8), 1 flyleaf<note>
I am using the following xquery code to attempt to do this:
<note displayLabel="Collation:Notes">{for $t in doc("collation.xml")//collation,
$h in distinct-values($t)
return
????
</note>
The problem is that I can either display all of the content (so without the parentheses) using data($t), or I can display just the things I want to be in parentheses (the information in the tags) using data($t/num) but I can't figure out how to display both with the items in tags wrapped in parentheses. I'm sure it's a really simple answer but I can't find it.

This is a good job for recursion:
declare function local:render(
$n as node()
) as node()?
{
typeswitch($n)
case element(num) return text{concat('(', $n, ')')}
case element(collation) return
<note displayLabel="Collation: Notes">{
for $n in $n/node()
return local:render($n)
}</note>
case element() return element { node-name($n) } {
for $n in $n/node()
return local:render($n)
}
default return $n
};
local:render(
<collation>1<num>12</num> 2<num>12</num> || I<num>8</num>-V<num>8</num>, 1 flyleaf</collation>)
=>
<note displayLabel="Collation: Notes">1(12) 2(12) || I(8)-V(8), 1 flyleaf</note>

Related

Prevent WordPress from escaping shortcode attributes

at the moment I'm developing a plugin, which hooks up to the content editor. My callback receives the post content after editing and calls do_shortcode(), but there is a problem and i don't know how to fix it.
add_filter('wp_insert_post_data', 'prepareContentSaving', 99, 2);
add_filter('wp_update_post_data', 'prepareContentSaving', 99, 2);
For instance if my post looks like (which obviously looks like valid shortcode syntax):
[foo bar="two words"]
my callback receives:
[foo bar=\"two words\"]
Looks right, right? But now whenever the shortcode is parsed via do_shortcode() the arguments are parsed like
[tag argument1=value1 argument2]
instead of
[tag argument="Foo bar"]
which then looks something like this in PHP:
array(
[0]=> string "bar=\"two"
[1]=> string "words\""
)
So how can I prevent the quotes inside the shortcode from being escaped? Is there something wrong with the post data hook? Changing the priority from 99 to 0 doesn't change something either. Am I using the right filter?
You can try to modify your code like this:
$post = array_map('stripslashes_deep', $_POST);
More info link: http://codex.wordpress.org/Function_Reference/stripslashes_deep
WordPress actually doesn't feature any option for preventing shortcodes to be escaped. The only way is to undo it is to convert all '\"' back to '"' (same for single quotes) inside the function 'prepareContentSaving':
add_filter('wp_insert_post_data', 'prepareContentSaving', 99, 2);
add_filter('wp_update_post_data', 'prepareContentSaving', 99, 2);
function prepareContentSaving($data, $post) {
$content = $post['post_content'];
$content = correctShortcodeSlashes($content);
... any further processing ...
$data['post_content'] = $content;
return $data;
}
After saving a post wordpress not only escapes quotes but also escapes backslashes. So '"' becomes '\"' and '\"' (if the editor wants to escape a quote) becomes '\\"'.
The first given PCRE converts all single escaped quotes inside shortcode brackets back to normal quotes, the second one converts all the double escaped ones inside brackets. This way the content stays the same which reduces the chances of code injection.
PHP Manual on preg_replace
function correct_shortcode_slashes($text) {
$attribute_escaped_slashes_pattern = '/(\[)((.|\s)*?)([^\\\\])\\\\("|\')(.*?)(\])/';
$attribute_escaped_slashes_replacement = '$1$2$4"$6$7';
$attribute_double_slashes_pattern = '/(\[)((.|\s)*?)\\\\+("|\')(.*?)(\])/';
$attribute_double_slashes_replacement = '$1$2"$5$6';
$result = $text;
$counter = 0;
while(true) {
$result = preg_replace($attribute_escaped_slashes_pattern, $attribute_escaped_slashes_replacement, $result, -1, $counter);
if($counter === 0) {
break;
}
}
while(true) {
$result = preg_replace($attribute_double_slashes_pattern, $attribute_double_slashes_replacement, $result, -1, $counter);
if($counter === 0) {
break;
}
}
return $result;
}
Please feel free to enhance this answer.

XQuery with if condition in for loop

I have written xquery to return results in normal way.
let $results := //data:data
return
<result>
{
for $i in $results
return
<documentInformation>
<id>{data($i/DATA:ID)}</id>
<status>{data($i/#status)}</status>
<title>{data($i/data:title)}</title>
<displayName>{data($i/DATA:DISPLAYNAME)}</displayName>
</documentInformation>
}
</result>
Now, I have to filter out the results in for loop with some condition like
(pseudo logic)
if id = 'abc' and status ="closed"
then skip the row
else add row.
I have tried several ways. but could not run the query..
Try this:
<result>
{
for $i in //data:data
where fn:not($i/DATA:ID = 'abc' and $i/#status = "closed")
return
<documentInformation>
<id>{data($i/DATA:ID)}</id>
<status>{data($i/#status)}</status>
<title>{data($i/data:title)}</title>
<displayName>{data($i/DATA:DISPLAYNAME)}</displayName>
</documentInformation>
}
</result>
Note that the XPath //data:data may have a lot of work to do, but that's a separate matter.
You Can also use if condition instead of where
<result>
{
for $i in //data:data
return
if($i/DATA:ID != 'abc' and $i/#status != "closed")
then
(
<documentInformation>
<id>{data($i/DATA:ID)}</id>
<status>{data($i/#status)}</status>
<title>{data($i/data:title)}</title>
<displayName>{data($i/DATA:DISPLAYNAME)}</displayName>
</documentInformation>
)
else ()
}
</result>

How to get value of sibling node by name?

I am new to OracleServiceBus and XQuery.
My requirement is as follows.
'FieldName' element can have values from 'udf1' till 'udf10'
Input XML
<UserDefinedFields>
<UserDefinedField>
<FieldName>udf5</FieldName>
<ValueAsString>UDF5Value</ValueAsString>
</UserDefinedField>
<UserDefinedField>
<FieldName>udf8</FieldName>
<ValueAsString>UDF8Value</ValueAsString>
</UserDefinedField>
<UserDefinedField>
<FieldName>udf3</FieldName>
<ValueAsString>UDF3Value</ValueAsString>
</UserDefinedField>
</UserDefinedFields>
Expected XML
<udf1></udf1>
<udf2></udf2>
<udf3>UDF3Value</udf3>
<udf4></udf4>
<udf5>UDF5Value</udf5>
<udf6></udf6>
<udf7></udf7>
<udf8>UDF8Value</udf8>
<udf9></udf9>
<udf10></udf10>
Any answer will be a great help.
User computed element constuctors for generating the elements:
for $i in 1 to 10
let $udf := concat('udf', $i)
return element { $udf } { data(//UserDefinedField[FieldName = $udf]/ValueAsString) }
I think Oracle supports them, with MS SQL this would not be possible and you'd have to enumerate all of them:
(
element udf1 { data(//UserDefinedField[FieldName = 'udf1']/ValueAsString),
element udf2 { data(//UserDefinedField[FieldName = 'udf1']/ValueAsString),
(: ... :)
element udf10 { data(//UserDefinedField[FieldName = 'udf10']/ValueAsString)
)
Thanks #Jens Erat.
Following code worked for me.
for $UserDefinedField in $UserDefinedFields/UserDefinedField
for $i in 1 to 20
let $udf := concat('udf', $i)
return if ($UserDefinedField/FieldName = $udf ) then
element {$udf}{data($UserDefinedField/ValueAsString)}
else ()

How to convert text inside a node into child node using xquery update?

I have a xml document like
<root>
<first>
First Level
<second>
second level
<third>
Third Level
</third>
</second>
<second2>
another second level
</second2>
</first>
</root>
How to convert this document with all nodes, that means if a node contains text and child node convert text into a child node (let's say childtext) using xquery-update
<root>
<first>
<childtext>First Level</childtext>
<second>
<childtext>second level</childtext>
<third>
Third Level
</third>
</second>
<second2>
another second level
</second2>
</first>
</root>
And here is what I tried:
let $a :=
<root>
<first>
First Level
<second>
second level
<third>
Third Level
</third>
</second>
<second2>
another second level
</second2>
</first>
</root>
return
copy $i := $a
modify (
for $x in $i/descendant-or-self::*
return (
if($x/text() and exists($x/*)) then (
insert node <childtext>
{$x/text()}
</childtext> as first into $x
(: here should be some code to delete the text only:)
) else ()
)
)
return $i
I could not delete the text which has sibling node.
As you want to replace an element, you should simply use the replace construct, instead of inserting the new element and deleting the old one. Seems much simpler to me:
copy $i := $a
modify (
for $x in $i/descendant-or-self::*[exists(*)]/text()
return replace node $x with <childtext>{$x}</childtext>
)
return $i
The modified solution is from #dirkk here:
copy $i := $a
modify (
for $x in $i/descendant-or-self::*
return (
if($x/text() and exists($x/*)) then (
if(count($x/text())=1) then (
replace node $x/text() with <child> {$x/text()}</child>
) else (
for $j at $pos in 1 to count($x/text())
return
replace node $x/text()[$pos] with <child>{$x/text()[$pos]}</child>
)
) else ()
)
)
return $i
Thank you Once again.

Drupal module_invoke() and i18n

I am tasked with i18n-ing our current CMS setup in Drupal.
The problem that I am facing is with use of module_invoke() to place blocks within nodes.
I have managed to string translate blocks, and that is working when a block is placed in a region (block content is successfully translated) using the UI.
However, when a block is injected into a node like such:
$block = module_invoke('block', 'block', 'view', 22); print $block['content'];
It is not getting translated, or even worse, not showing at all.
I have also tried this variation using t(). e.g.:
$block = module_invoke('block', 'block', 'view', 22); print t($block['content']);
to no avail.
Generally speaking I've having a bit of trouble with blocks for i18n. Does anyone have a recommended approach for dealing with blocks in drupal with regards to translating them? I would prefer not to create different blocks for each language.
So .. After digging around in the bowels of Drupal - and much hair pulling .. I've come up with an almost decent solution.
Basically, with this function, I can extract a translated version of a block:
function render_i18n_block($block_id, $region = "hidden"){
if ($list = block_list($region)) {
foreach ($list as $key => $block) {
// $key == <i>module</i>_<i>delta</i>
$key_str = "block_".$block_id;
if ($key_str == $key){
return theme('block', $block);
}
}
}
}
Then, in my node, I simple call:
<?php echo render_i18n_block(<block_id>,<region>); ?>
There can be some issues where your blocks might not be displaying in a region (and therefore you can't pass a region into block_list). For this case, I simply created a region called "hidden" which is not rendered anywhere in my template, but can be used to call block_list.
Finally (and this is the part that I still need to find a good solution for), I discovered that block_list() in: includes/blocks/block.inc has a bit of an issue.
It appears that $theme_key is not reliably set unless block_list() is being called from the theme() function (in includes/themes.inc) .. this causes the SQL to return an empty results set. The SQL looks like this:
$result = db_query(db_rewrite_sql("SELECT DISTINCT b.* FROM {blocks} b LEFT JOIN {blocks_roles} r ON b.module = r.module AND b.delta = r.delta WHERE b.theme = '%s' AND b.status = 1 AND (r.rid IN (". db_placeholders($rids) .") OR r.rid IS NULL) ORDER BY b.region, b.weight, b.module", 'b', 'bid'), array_merge(array($theme_key), $rids));
As you can see, if theme_key is not set, then it will just return an empty result.
For now I am bypassing this by simply adding:
if (!isset($theme_key)){$theme_key="<my_theme_name>";}
in modules/blocks/block.inc::block_list() around line 429 .. I still need to work out a better way to do this.
10 for anyone with suggestions on how I could ensure that $theme_key is set before calling block_list :)
I had exactly the same problem as you, since I was using
$block = module_invoke('block', 'block_view', 'block_id');
print render($block['content']);
to inject the block into my nodes. However, looking up module_invoke in the Drupal reference, I found a comment titled "to render blocks in Drupal 7 better to use Block API", with this code:
function block_render($module, $block_id) {
$block = block_load($module, $block_id);
$block_content = _block_render_blocks(array($block));
$build = _block_get_renderable_array($block_content);
$block_rendered = drupal_render($build);
return $block_rendered;
}
I just un-functioned it to use directly, like so:
$block = block_load('block', 'block_id');
$block_content = _block_render_blocks(array($block));
$build = _block_get_renderable_array($block_content);
print render($build);
And for me it works like a charm. Be aware however that this method prints the block title as well, so maybe you'll want to set it to 'none' in the original language.
Create a function like this
<?php
function stg_allcontent2($allC, $level
= "1") {
global $language; $lang = $language->language;
foreach ($allC as $acKey => $ac) {
if($ac['link']['options']['langcode']
== $lang){ if ($level == "1")
$toR .= "";
if (is_array($ac['below']))
$class="expanded"; else
$class="leaf";
$toR .= "<li class=\"".$class."\">" . l($ac['link']['link_title'], $ac['link']['link_path']) . "</li>";
if ($level != "1") $toR .= ""; if (is_array($ac['below'])) $toR .= "<ul class=\"menu\">".stg_allcontent2($ac['below'], "2")."</ul>"; if ($level == "1") $toR .= ""; }
}
return $toR; } ?>
call like this
<?php echo '<ul class="menu">'; echo stg_allcontent2(menu_tree_all_data($menu_name
= 'menu-header', $item = NULL)); echo '</ul>'; ?>
This may help you: http://drupal-translation.com/content/translating-block-contents#
UPDATE: the t() function allows you to pass in the language code to use.

Resources