Forum
Bug in perch_blog_custom when skipping template and returning HTML ...
(This may affect more than just perch_blog_custom, as I think other perch functions probably use the same underlying code) (EDIT: markdown is nerfing the indentation whitespace in my codeblocks, so the patch below may not work -- let me know if there's another way to send it to you)
Here is what I am doing:
$html = perch_blog_custom(array(
'count' => 5,
'template' => 'pavlus_posts.html',
'sort' => 'postDateTime',
'sort-order' => 'DESC',
'skip-template' => true,
'return-html' => true,
));
And then what I get inside of the $html variable is this -- it's leaving the unprocessed <perch:form, :input, :error, :label>, etc. tags in the html output.
<div class="blog-entry">
<h2><a href="/my-blog/post.php?s=2016-08-26-my-post-title" rel="bookmark" class="entry-title">My Post Title</a></h2>
<p class="entry-published date">26 August 2016</p>
<div class="description e-content">
<p>Lorem ipsum dolor sit amet ... etc.</p>
</div>
<p class='comment-count'>Comments: 0</p>
<perch:form template="/addons/apps/perch_blog/templates/blog/pavlus_posts.html" id="comment" method="post" app="perch_blog" class="comment-form form-horizontal" action="/my-blog/post.php?s=2016-08-26-my-post-title">
<fieldset>
<legend>Leave a comment</legend>
<div>
<perch:input type="text" id="commentName" required="true" label="Name" antispam="name" placeholder="Your Name" />
<perch:error for="commentName" type="required">Required</perch:error>
</div>
<div>
<perch:input type="email" id="commentEmail" required="true" label="Email" antispam="email" placeholder="Email Address" />
<perch:error for="commentEmail" type="required">Required</perch:error>
<perch:error for="commentEmail" type="format">Check format of address</perch:error>
</div>
<div>
<perch:label for="commentHTML">Comments</perch:label>
<br />
<perch:input type="textarea" id="commentHTML" required="true" label="Message" antispam="body" />
<perch:error for="commentHTML" type="required">Required</perch:error>
</div>
<div>
<perch:input type="hidden" id="postID" value="71" />
<perch:input type="submit" id="submitComment" value="Submit" />
</div>
</fieldset>
<perch:success>
<p>Thank you. Your comment has been submitted and will appear on the site once approved.</p>
</perch:success>
</perch:form>
<hr />
</div>
...
<div class="paging">
Page 1 of 14
<a href="/my-blog/?page=2">Next</a>
</div>
(FWIW, here are the contents of pavlus_posts.html):
<div class="blog-entry">
<h2><a href="<perch:blog id="postURL" />" rel="bookmark" class="entry-title"><perch:blog id="postTitle" /></a></h2>
<perch:if exists="image"><img src="<perch:blog id="image" type="image" width="50" height="50" crop="true" />" alt="<perch:blog id="postTitle" />" /></perch:if>
<p class="entry-published date"><perch:blog id="postDateTime" format="%d %B %Y" /></p>
<div class="description e-content">
<perch:blog id="postDescHTML" type="textarea" label="Post" order="2" editor="markitup" markdown="true" size="xxl autowidth" required="true" />
</div>
<p class='comment-count'>Comments: <perch:blog id="postCommentCount"/></p>
<perch:if id="postAllowComments" value="1">
<perch:form id="comment" method="post" app="perch_blog" class="comment-form form-horizontal" action="<perch:blog id="postURL" />">
<fieldset>
<legend>Leave a comment</legend>
<div>
<perch:input type="text" id="commentName" required="true" label="Name" antispam="name" placeholder="Your Name" />
<perch:error for="commentName" type="required">Required</perch:error>
</div>
<div>
<perch:input type="email" id="commentEmail" required="true" label="Email" antispam="email" placeholder="Email Address" />
<perch:error for="commentEmail" type="required">Required</perch:error>
<perch:error for="commentEmail" type="format">Check format of address</perch:error>
</div>
<div>
<perch:label for="commentHTML">Comments</perch:label>
<br />
<perch:input type="textarea" id="commentHTML" required="true" label="Message" antispam="body" />
<perch:error for="commentHTML" type="required">Required</perch:error>
</div>
<div>
<perch:input type="hidden" id="postID" value="<perch:blog id="postID" />" />
<perch:input type="submit" id="submitComment" value="Submit" />
</div>
</fieldset>
<perch:success>
<p>Thank you. Your comment has been submitted and will appear on the site once approved.</p>
</perch:success>
</perch:form>
</perch:if>
<hr />
</div>
<perch:after>
<perch:if exists="paging">
<div class="paging">
Page <perch:blog id="current_page" /> of <perch:blog id="number_of_pages" />
<perch:blog id="page_links" encode="false" />
<perch:if exists="not_first_page">
<a href="<perch:blog id="prev_url" encode="false" />">Previous</a>
</perch:if>
<perch:if exists="not_last_page">
<a href="<perch:blog id="next_url" encode="false" />">Next</a>
</perch:if>
</div>
</perch:if>
</perch:after>
If I turn off the 'skip-template' option, it processes those correctly, and turns them into what they need to be ... but then I can't save the content into a variable like I want to.
I tried to work around the problem by using output buffering, like so:
ob_start();
perch_blog_custom(array(
'count' => 5,
'template' => 'pavlus_posts.html',
'sort' => 'postDateTime',
'sort-order' => 'DESC',
//'skip-template' => true,
//'return-html' => true,
));
$html = ob_end_flush();
But for some reason I couldn't get it to work; I think perch is doing something that doesn't allow me to output buffer it (frustrating! Is it? If so, why??).
So I looked into the source code, and I found that in PerchFactory.class.php, in get_filtered_listing_from_index(), and get_filtered_listing(), if the 'skip-template' option is set, it returns from the function before doing its post-processing on the HTML ... this seems like a bug to me. Shouldn't it be doing that either way?
I was using an older version of Perch (2.7.10), but found the exact same code in the latest version (3.0.4) and the latest 2.8 version, 2.8.34.
Anyway, so I went ahead and fixed the logic in that function, so that it will call its post-processing if necessary whether or not skip-template is set. I can't seem to attach files here, otherwise I would attach the git repository I made from it so you could see the commits with notes, but here is contents of a patch I created (in git patch format):
diff --git a/perch/core/lib/PerchFactory.class.php b/perch/core/lib/PerchFactory.class.php
index cbc0099..8a37cab 100644
--- a/perch/core/lib/PerchFactory.class.php
+++ b/perch/core/lib/PerchFactory.class.php
@@ -670,98 +670,101 @@ class PerchFactory
if (PerchUtil::count($items)) {
$html = $Template->render_group($items, true);
}else{
$Template->use_noresults();
$html = $Template->render(array());
}
}
- if (isset($opts['skip-template']) && $opts['skip-template']==true) {
+ // this post-processing should happen whether we skip-template or not
+ if (strpos($html, '<perch:')!==false) {
+ $Template = new PerchTemplate();
+ $html = $Template->apply_runtime_post_processing($html);
+ }
+
- if (isset($opts['api']) && $opts['api']==true) {
- $api = true;
+ if (!isset($opts['skip-template']) || $opts['skip-template']==false) {
+ return $html;
+ }
+
+ // things below only applicable when skip-template is set
+ if (isset($opts['api']) && $opts['api']==true) {
+ $api = true;
+ } else {
+ $api = false;
+ }
+
+ if ($single_mode) {
+ if ($api) {
+ return $Item->to_array_for_api();
} else {
- $api = false;
+ return $Item->to_array();
}
+ }
- if ($single_mode) {
+ $processed_vars = array();
+ if (PerchUtil::count($items)) {
+ foreach($items as $Item) {
if ($api) {
- return $Item->to_array_for_api();
+ $Item->prefix_vars = false;
+ $processed_vars[] = $Item->to_array_for_api();
} else {
- return $Item->to_array();
- }
- }
-
- $processed_vars = array();
- if (PerchUtil::count($items)) {
- foreach($items as $Item) {
- if ($api) {
- $Item->prefix_vars = false;
- $processed_vars[] = $Item->to_array_for_api();
- } else {
- $processed_vars[] = $Item->to_array();
- }
+ $processed_vars[] = $Item->to_array();
}
}
+ }
-
- if (PerchUtil::count($processed_vars)) {
- if ($api) {
- $field_type_map = $Template->get_field_type_map($this->namespace);
- }
+ if (PerchUtil::count($processed_vars)) {
- $category_field_ids = $Template->find_all_tag_ids('categories');
+ if ($api) {
+ $field_type_map = $Template->get_field_type_map($this->namespace);
+ }
- foreach($processed_vars as &$item) {
- if (PerchUtil::count($item)) {
- foreach($item as $key => &$field) {
+ $category_field_ids = $Template->find_all_tag_ids('categories');
- if ($api) {
+ foreach($processed_vars as &$item) {
+ if (PerchUtil::count($item)) {
+ foreach($item as $key => &$field) {
- if (array_key_exists($key, $field_type_map)) {
- $field = $field_type_map[$key]->get_api_value($field);
- continue;
- }
- } else {
+ if ($api) {
- if (in_array($key, $category_field_ids)) {
- $field = $this->_process_category_field($field);
- }
+ if (array_key_exists($key, $field_type_map)) {
+ $field = $field_type_map[$key]->get_api_value($field);
+ continue;
}
+ } else {
- if (is_array($field) && isset($field['processed'])) {
- $field = $field['processed'];
- }
- if (is_array($field) && isset($field['_default'])) {
- $field = $field['_default'];
+ if (in_array($key, $category_field_ids)) {
+ $field = $this->_process_category_field($field);
}
}
+
+ if (is_array($field) && isset($field['processed'])) {
+ $field = $field['processed'];
+ }
+ if (is_array($field) && isset($field['_default'])) {
+ $field = $field['_default'];
+ }
}
}
}
-
- if (isset($opts['return-html'])&& $opts['return-html']==true) {
- $processed_vars['html'] = $html;
- }
-
- return $processed_vars;
}
- if (strpos($html, '<perch:')!==false) {
- $Template = new PerchTemplate();
- $html = $Template->apply_runtime_post_processing($html);
+ if (isset($opts['return-html'])&& $opts['return-html']==true) {
+ $processed_vars['html'] = $html;
}
- return $html;
+ return $processed_vars;
+
}
public function get_filtered_listing_from_index($opts, $where_callback, $pre_template_callback=null)
{
$Perch = Perch::fetch();
$index_table = PERCH_DB_PREFIX.$this->index_table;
$where = array();
@@ -1254,109 +1257,112 @@ class PerchFactory
}
}else{
$Template->use_noresults();
$html = $Template->render(array());
}
}
- if (isset($opts['skip-template']) && $opts['skip-template']==true) {
+ // make sure this happens before return whether we skip template or not
+ if (is_array($html)) {
+ // split-items
+ if (PerchUtil::count($html)) {
+ $Template = new PerchTemplate();
+ foreach($html as &$html_item) {
+ if (strpos($html_item, '<perch:')!==false) {
+ $html_item = $Template->apply_runtime_post_processing($html_item);
+ }
+ }
+ }
+ }else{
+ if (strpos($html, '<perch:')!==false) {
+ $Template = new PerchTemplate();
+ $html = $Template->apply_runtime_post_processing($html);
+ }
+ }
+
- if (isset($opts['api']) && $opts['api']==true) {
- $api = true;
+ // if not skipping template, we can return html now
+ if (! isset($opts['skip-template']) || $opts['skip-template']==false) {
+ return $html;
+ }
+
+ if (isset($opts['api']) && $opts['api']==true) {
+ $api = true;
+ } else {
+ $api = false;
+ }
+
+ if ($single_mode) {
+ if ($api) {
+ return $Item->to_array_for_api();
} else {
- $api = false;
+ return $Item->to_array();
}
+ }
- if ($single_mode) {
- if ($api) {
- return $Item->to_array_for_api();
+ $processed_vars = array();
+ if (PerchUtil::count($items)) {
+ foreach($items as $Item) {
+ if (isset($opts['api']) && $opts['api']) {
+ $Item->prefix_vars = false;
+ $processed_vars[] = $Item->to_array_for_api();
} else {
- return $Item->to_array();
+ $processed_vars[] = $Item->to_array();
}
- }
- $processed_vars = array();
- if (PerchUtil::count($items)) {
- foreach($items as $Item) {
- if (isset($opts['api']) && $opts['api']) {
- $Item->prefix_vars = false;
- $processed_vars[] = $Item->to_array_for_api();
- } else {
- $processed_vars[] = $Item->to_array();
- }
-
- }
}
+ }
- if (PerchUtil::count($processed_vars)) {
+ if (PerchUtil::count($processed_vars)) {
- if ($api) {
- $field_type_map = $Template->get_field_type_map($this->namespace);
- }
+ if ($api) {
+ $field_type_map = $Template->get_field_type_map($this->namespace);
+ }
- $category_field_ids = $Template->find_all_tag_ids('categories');
- //PerchUtil::debug($category_field_ids, 'notice');
-
- foreach($processed_vars as &$item) {
- if (PerchUtil::count($item)) {
- foreach($item as $key => &$field) {
-
- if ($api) {
- if (array_key_exists($key, $field_type_map)) {
- $field = $field_type_map[$key]->get_api_value($field);
- continue;
- }
- } else {
- if (in_array($key, $category_field_ids)) {
- $field = $this->_process_category_field($field);
- }
- }
+ $category_field_ids = $Template->find_all_tag_ids('categories');
+ //PerchUtil::debug($category_field_ids, 'notice');
- if (is_array($field) && isset($field['processed'])) {
- $field = $field['processed'];
+ foreach($processed_vars as &$item) {
+ if (PerchUtil::count($item)) {
+ foreach($item as $key => &$field) {
+
+ if ($api) {
+ if (array_key_exists($key, $field_type_map)) {
+ $field = $field_type_map[$key]->get_api_value($field);
+ continue;
}
- if (is_array($field) && isset($field['_default'])) {
- $field = $field['_default'];
+ } else {
+ if (in_array($key, $category_field_ids)) {
+ $field = $this->_process_category_field($field);
}
}
+
+ if (is_array($field) && isset($field['processed'])) {
+ $field = $field['processed'];
+ }
+ if (is_array($field) && isset($field['_default'])) {
+ $field = $field['_default'];
+ }
}
}
}
-
- if (isset($opts['return-html'])&& $opts['return-html']==true) {
- $processed_vars['html'] = $html;
- }
-
- return $processed_vars;
}
- if (is_array($html)) {
- // split-items
- if (PerchUtil::count($html)) {
- $Template = new PerchTemplate();
- foreach($html as &$html_item) {
- if (strpos($html_item, '<perch:')!==false) {
- $html_item = $Template->apply_runtime_post_processing($html_item);
- }
- }
- }
- }else{
- if (strpos($html, '<perch:')!==false) {
- $Template = new PerchTemplate();
- $html = $Template->apply_runtime_post_processing($html);
- }
+ if (isset($opts['return-html'])&& $opts['return-html']==true) {
+ $processed_vars['html'] = $html;
}
- return $html;
+ return $processed_vars;
+
}
private function _get_filter_sub_sql($field, $items, $negative_match=false, $match, $fuzzy, $where_clause)
{
if (count($items)) {
$index_table = PERCH_DB_PREFIX.$this->index_table;
$cat_sql = 'SELECT DISTINCT idx.itemID FROM '.$index_table.' idx JOIN '.$this->table.' main ON idx.itemID=main.'.$this->pk.' AND '.$where_clause. ' AND ';
It is, yes. Here's why and how to turn it off: https://grabaperch.com/blog/archive/progressive-output-flushing
Thank you for that, Drew ... that is helpful to know about output buffering.
I still think the above is a bug. Any feedback on that?
I'm able to work around it, however, if I pass the third parameter, 'return', as true, instead of relying on the 'return-html' flag. Seems like behavior should be identical either way, but it's not.
Yes, I think that's right.