How to "refresh" DOMDocument instances of LibXML2? - refresh

Using PHP to illustrate: there are a BUG in the normalizeDocument() method, or a lack of a "refresh" method, because DOM consistence is lost after changes (even only attribute changes)... So, any algorithm "with DOM changes" that you implement with LIBXML2 somethimes works and sometimes not, is unpredictable!! (?)
The "refresh" by $doc->LoadXML($doc->saveXML()); is a workaround and lost performance in a flow of work with DOM... A sub-question: all moment I need to refresh DOM?
$XML = '
<html>
<h1>Hello</h1>
<ol>
<li>test (no id)</li>
<li xml:id="i2">test i2</li>
</ol>
</html>
';
$doc = new DOMDocument;
$doc->LoadXML($XML);
doSomeChange($doc); // here DOM is modified
print $doc->saveXML(); // show new DOM state
$doc->normalizeDocument(); // NOT REFRESHING!?!
var_dump($doc->getElementById('i2')); //NULL!??! is a BUG!
//CAN_NOT_doMORESomeChange($doc);
$doc->LoadXML($doc->saveXML()); // only way to refresh?
print $doc->getElementById('i2')->tagName; //OK, is there
// illustrating attribute modification:
function doSomeChange(&$dom) {
$max = 0;
$xp = new DOMXpath($dom);
foreach(iterator_to_array($xp->query('/html/* | //li')) as $e) {
$max++;
$e->setAttribute('xml:id',"i$max");
}
print "\ncmpDOM='".($xp->document === $dom)."'\n"; // after #ThomasWeinert
}
So, input is the $XML and output is
<html>
<h1 xml:id="i1">Hello</h1>
<ol xml:id="i2">
<li xml:id="i3">test (no id)</li>
<li xml:id="i4">test i2</li>
</ol>
</html>
NULL
ol
the NULL is the bug (see code comments).
PS: if I change input line <li xml:id="i2">test i2</li> to <li>test i2</li> the algorithm works as expected (!), so, is unpredictable.
Related questions: In DomDocument, reuse of DOMXpath, it is stable? PHP DomDocument, reuse of XSLTProcessor, it is stable/secure?

Changes are applied to the DOM the moment you're doing them. In your example this creates a status where two elements have the same xml:id and this seems to screw up the index. Remove the xml:id attributes before setting them and it works:
$XML = '
<html>
<h1>Hello</h1>
<ol>
<li>test (no id)</li>
<li xml:id="i2">test i2</li>
</ol>
</html>
';
$doc = new DOMDocument;
$doc->loadXML($XML);
var_dump($doc->getElementById('i2'), $doc->getElementById('i2')->tagName);
/*
object(DOMElement)#2 (0) { }
string(2) "li"
*/
doSomeChange($doc); // here DOM is modified
var_dump($doc->getElementById('i2'), $doc->getElementById('i2')->tagName);
/*
object(DOMElement)#6 (0) { }
string(2) "ol"
*/
print $doc->saveXML(); // show new DOM state
/*
<?xml version="1.0"?>
<html>
<h1 xml:id="i1">Hello</h1>
<ol xml:id="i2">
<li xml:id="i3">test (no id)</li>
<li xml:id="i4">test i2</li>
</ol>
</html>
*/
// illustrating xml:id attribute modification:
function doSomeChange($dom) {
$xp = new DOMXpath($dom);
foreach($xp->evaluate('//*') as $e) {
$e->removeAttribute('xml:id');
}
$max = 0;
foreach($xp->evaluate('/html/*|//li') as $e) {
$max++;
$e->setAttribute('xml:id',"i$max");
}
}
Your specific dom modification is, what breaks the getElementById() calls.
To the "stability" question: The connection between DOMXpath and DOMDocument is not completly "stable". If you're using a load*() method in the DOMDocument, the connection is lost. You can validate that the DOMXpath uses the correct DOMDocument comparing its document property:
var_dump($xpath->document === $doc);
This does not happen in your case, because you always create a new instance of DOMXpath in the function. But it means you should avoid reloading the document because this will break xpath instances created for the document.

Related

Move "Submitted By" from node.tpl to page.tpl

I'm looking to move "Submitted By" info from node.tpl to page.tpl however when I add the following from node.tpl I get errors. Im assuming I dont have access to those variables, but would like to know how I can set up a pre-proccess to get it to display as it does in the node.tpl
<?php if ($display_submitted): ?>
<div class="submitted">
<?php print $submitted; ?>
</div>
<?php endif; ?>
You can either use a preprocess function in the template.php of your theme, as explained here:
https://drupal.stackexchange.com/questions/40222/how-can-i-print-node-authors-last-login-date-on-page-tpl-php
In your case it would look like this (tested on Drupal 7):
function yourtheme_preprocess_page(&$variables) {
$variables['author'] = "";
if (isset($variables['node']) && ($account = user_load($variables['node']->uid))) {
$variables['author'] = $account->name;
}
}
And then in your page.tpl.php use this:
Submitted by: <?php print $author; ?>
If you don’t want to touch any of your theme’s files, but you need to output the author’s name in another region as the node content, you could create a view (block display) containing the node author, and assign it to the region.
While normally done in node.tpl.php, if the page is a node view page, the $node variable is also available in page.tpl.php
You can then use something like:
if (isset($node)) {
// Check if display submitted variable is set for this node type
if (variable_get('node_submitted_'. $node->type, 0)) {
// Do stuff
}
}
An alternative approach would be adding the required logic instead to an implementation of
hook_preprocess_page
Bonus update: You can see the $node variable added to page.tpl.php in core template_preprocess_page
if ($node = menu_get_object()) {
$variables['node'] = $node;
}
In page.tpl.php:
<div class="submitted">
<?php echo format_date($node->created, 'custom','d.m.Y'); ?><br />
<?php echo 'by ' . $node->name; ?>
</div>

How to add a PHP if statement within an echo?

I would like to place an if statement within an echo and I am not quite sure how to do it. Here is the echo:
if(!$hideProduct) {
echo '
<div class="gearBorder">
<img title="Sold Out" alt="Sold Out" class="soldOut" src="soldout.png" />':"").'
<div class="gearInfo">
<h4>' . $productName . '</h4>
<p class="gearDesc">'. $productDescription .'</p>
<p class="cost">$' . $productPrice . '</div>
</div>
</div>
';}
On line 3, I would like to wrap the image in an if statement:
if($productStatus = '0') {
}
What would be the best way to wrap the image in that statement? Thanks!
You can actually end control flow blocks like if statements outside of the same PHP block they were opened in. For example, this should work:
<?php if (!$hideProduct) { ?>
<div class="gearBorder">
<?php if ($productStatus == '0') { ?>
<img title="Sold Out" ... />
<?php } ?>
...HTML...
</div>
<?php } ?>
If you don't like the curly braces, you can also replace them with a colon (:) and endif, respectively. See this link for more information.
Use an array to hold the CSS classes (with background-image) for each $productStatus
Fast and efficient. There is a performance hot when you toggle from HTML mode to PHP mode. This method eliminates the if elseif performance hit in the Intel micro code.
<style type="text/css">
.soldout{width:40px;height:40px;background-image: url('soldout.png');}
.backorder{width:40px;height:40px;background-image: url('backorder.png');}
.instock{width:40px;height:40px;background-image: url('instock.png');}
</style>
$icon = array(' class="soldout" ',' class="backorder" ',' class="instock" ');.
echo '<div class="gearBorder"><div ' . $icon[$productStatus] . '></div><div class="gearInfo"> ... ';
I would also use a 4-bit color GIF icon and convert it to Base64 MIME
Make sure page is served with gZip and there will be little to no penalty for the Base64.
If you want to stick with image files, make sure images are served with a large cache max-age value.
background-image: url('data:image/gif;base64,R0lGODlhKAAoAK...');

Trouble accessing attribute after using BeautifulSoup's findAll

I'm trying to scrape sites like this one on the BBC website to grab the relevant parts of the programme listing, and I've just started using BeautifulSoup to do this.
The parts of interest start with sections like:
<li about="/programmes/p013zzsl#segment" class="segment track" id="segmentevent-p013zzsm" typeof="po:MusicSegment">
<li about="/programmes/p014003v#segment" class="segment speech alt" id="segmentevent_p014003w" typeof="po:SpeechSegment">
What I've done so far is opened the HTML as soup and then used soup.findAll(typeof=['po:MusicSegment', 'po:SpeechSegment']) to give a ResultSet of the parts I'm interested in the order in which they appear.
What I then want to do is check whether a section refers to po:MusicSegment or po:SpeechSegment in HTML that looks like:
<li about="/programmes/p01400m9#segment" class="segment track" id="segmentevent-p01400mb" typeof="po:MusicSegment"> <span class="artist-image"> <span class="depiction" rel="foaf:depiction"><img alt="" height="63" src="http://static.bbci.co.uk/programmes/2.54.3/img/thumbnail/artists_default.jpg" width="112"/></span> </span> <script type="text/javascript"> window.programme_data.tracklist.push({ segment_event_pid : "p01400mb", segment_pid : "p01400m9", playlist : "http://www.bbc.co.uk/programmes/p01400m9.emp" }); </script> <h3> <span rel="mo:performer"> <span class="artist no-image" property="foaf:name" typeof="mo:MusicArtist">Mala</span> </span> <span class="title" property="dc:title">Calle F</span> </h3></li>
I want to access the typeof attribute associated with <li>, but if this chunk of HTML (as a BS4 tag) is called section and I enter section.li, it returns None.
Note that if I do section.img instead, I get something back:
<img alt="" height="63" src="http://static.bbci.co.uk/programmes/2.54.3/img/thumbnail/artists_default.jpg" width="112"/>
and I could then do, e.g. section.img['height'] to get back u'63'
What I want is something analogous for the section.li part, so section.li['typeof'] to give me po:MusicSegment or po:SpeechSegment
Of course, I could simply convert each result to text and then do a simple string search, but searching by attribute seems more elegant.
I'd iterate over the list returned by findAll:
soup = BeautifulSoup('<li about="/programmes/p013zzsl#segment" class="segment track" id="segmentevent-p013zzsm" typeof="po:MusicSegment"><li about="/programmes/p014003v#segment" class="segment speech alt" id="segmentevent_p014003w" typeof="po:SpeechSegment">')
for elem in soup.findAll(typeof=['po:MusicSegment', 'po:SpeechSegment']):
print elem['typeof']
returns
po:MusicSegment
po:SpeechSegment
and then conditionally perform your other tasks:
if elem['typeof'] == 'po:MusicSegment'
do.something()
elif elem['typeof'] == 'po:SpeechSegment':
do.something_else()

What is the proper way to add a custom dashboard "box" in the Magento backend without editing default templates?

I am working on creating what I hope one day will be a publicly available Magento extension (this part I mention because it's important to me that I do the "right thing" here). One of the things I would like to do is add a box in the default Magento dashboard, basically a new "box" exactly like "Top 5 Search Terms" except with my own content. I would like my new custom box to be the last box that is displayed (ideally).
The issue that I'm running into is that the template that is responsible for rendering the dashboard calls out specific blocks to be rendered, and these blocks are nested inside of html. In other words, where often there is an area where child blocks will be rendered into a nice reasonable HTML element, in this situation it appears that specific blocks are rendered. Here is the contents of /app/design/adminhtml/default/default/template/dashboard/index.phtml
<div class="dashboard-container">
<?php echo $this->getChildHtml('store_switcher') ?>
<table cellspacing="25" width="100%">
<tr>
<td><?php echo $this->getChildHtml('sales') ?>
<div class="entry-edit">
<div class="entry-edit-head"><h4><?php echo $this->__('Last 5 Orders') ?></h4></div>
<fieldset class="np"><?php echo $this->getChildHtml('lastOrders'); ?></fieldset>
</div>
<div class="entry-edit">
<div class="entry-edit-head"><h4><?php echo $this->__('Last 5 Search Terms') ?></h4></div>
<fieldset class="np"><?php echo $this->getChildHtml('lastSearches'); ?></fieldset>
</div>
<div class="entry-edit">
<div class="entry-edit-head"><h4><?php echo $this->__('Top 5 Search Terms') ?></h4></div>
<fieldset class="np"><?php echo $this->getChildHtml('topSearches'); ?></fieldset>
</div>
</td>
<td>
<div class="entry-edit" style="border:1px solid #ccc;">
<?php echo $this->getChildHtml('diagrams') ?>
<?php if (is_array($this->getChild('diagrams')->getTabsIds())) : ?>
<div id="diagram_tab_content"></div>
<?php endif; ?>
<div style="margin:20px;">
<?php echo $this->getChildHtml('totals') ?>
</div>
<div style="margin:20px;">
<?php echo $this->getChildHtml('grids') ?>
<div id="grid_tab_content"></div>
</div>
</div>
</td>
</tr>
</table>
</div>
If I was doing this in my own store, I believe I could achieve this relatively easily by editing the base Magento dashboard template index.phtml above, add what I need to have my block render, something like:
<div class="entry-edit">
<div class="entry-edit-head">
<h4><?php echo $this->__('Top 5 Search Terms') ?></h4></div>
<fieldset class="np"><?php echo $this->getChildHtml('myDashboardBox'); ?></fieldset>
</div>
</div>
But, this isn't my own store, so this doesn't really seem like an option.
Now, after some thought my options seem to be as follows (note that most of these seem "bad" and not something I'd be super proud to have seen in the public):
0) Something obvious that I'm not seeing that you will tell me is the perfect/right solution
1) I might (maybe?) be able to add my custom block inside of one of these other blocks in this area ("topSearches", "sales", etc) and have my block rendered. This does not seem very "clean"
2) I might be able to have the block rendered somewhere else on the dashboard page, and then move it with javascript to the correct place. This would be fairly easy I'm guessing, but feels very "hacky" for obvious reasons.
Does anybody have any feedback on the way to do this, or IF there is a way? Keep in mind that again I would like to release this module publicly so my goal is to do a good job and do as little "hacking" as possible.
Thank you very much for reading!
There is no really clean option, as you said, the template is coded in a non-extendable way, so there will always be some degree of hackiness. This is my personal preferred way of doing it by using event observers. This way it at least doesn't conflict with other modules.
First, add an observer for the core_block_abstract_prepare_layout_after and core_block_abstract_to_html_after event.
<adminhtml>
<events>
<core_block_abstract_prepare_layout_after>
<observers>
<your_module>
<class>your_module/observer</class>
<method>coreBlockAbstractPrepareLayoutAfter</method>
</your_module>
</observers>
</core_block_abstract_prepare_layout_after>
<core_block_abstract_to_html_after>
<observers>
<your_module>
<class>your_module/observer</class>
<method>coreBlockAbstractToHtmlAfter</method>
</your_module>
</observers>
</core_block_abstract_to_html_after>
</events>
</adminhtml>
These two events are dispatched for every block that is instantiated and rendered in Mage_Core_Block_Abstract. In my experience it's not such an issue using them in the adminhtml interface, but on the frontend observers for these events add too much overhead.
Back to the task at hand, you need to create the observer class.
class Your_Module_Model_Observer
{
public function coreBlockAbstractPrepareLayoutAfter(Varien_Event_Observer $observer)
{
if (Mage::app()->getFrontController()->getAction()->getFullActionName() === 'adminhtml_dashboard_index')
{
$block = $observer->getBlock();
if ($block->getNameInLayout() === 'dashboard')
{
$block->getChild('topSearches')->setUseAsDashboardHook(true);
}
}
}
public function coreBlockAbstractToHtmlAfter(Varien_Event_Observer $observer)
{
if (Mage::app()->getFrontController()->getAction()->getFullActionName() === 'adminhtml_dashboard_index')
{
if ($observer->getBlock()->getUseAsDashboardHook())
{
$html = $observer->getTransport()->getHtml();
$myBlock = $observer->getBlock()->getLayout()
->createBlock('you_module/block')
->setTheValuesAndTemplateYouNeed('HA!');
$html .= $myBlock->toHtml();
$observer->getTransport()->setHtml($html);
}
}
}
}
Your template will need to accomodate for the fact that you are inserting a sibling <div> from inside the sibling, but otherwise you should be fine.
</fieldset></div>
<div class="entry-edit">
<div class="entry-edit-head"><h4>Your Module</h4></div>
<fieldset class="np">Your Content
Leave it at that, because the parent template will be closing the <fieldset> and the <div> for you (ugly as heck, I know).

XSL master/ detail

I have a xsl code that I would like to show a nested row below each row that the user chooses.
Let's say I have a row that shows 4 columns with the main order details, I want the user to be able to click a plus or 3 dots "..." to see more details about this order.
I have all the information loaded already to the xml on the page so there is no need to go to the db again for the details.
Is this possible?
Example will be appreciated.
Thanks.
A crude example to show how its done in javascript! :) In IE click on "allow blocked contents"
<html>
<head>
<script language="javascript" type="text/javascript">
var f=0;
function tnd()
{
if(f==1)
{
var str2="The images, quotes and everything else are intended to be maintained strictly confidential. Rightclick of the mouse has been disabled, as well as alt+printscreen and copy options do not work well in major browsers. design:aravind"
document.getElementById("t_n_d").innerHTML=str2;
f=0;
return 1;
}
if(f==0)
{
var str1="to read features, terms and conditions about this design."
document.getElementById("t_n_d").innerHTML=str1;
f=1;
return 1;
}
}
</script>
</head>
<body>
<span id='footertext'
style="font-size: 12px;"><span onmousedown='tnd();' style='color: red; text-decoration: underline; cursor: pointer;'>click here</span> : <span id='t_n_d'>to read features, terms and
conditions about this design.</span></span></td>
</tr>
</body>
</html>
Copy the html code in "str2" to load tables .. pictures or etc ..
(ps: replace double-quotes with single-quotes in str2)
also please note that javascript is a client-side script .. it is harms performance on over usage .. this is just to give you an idea.