Page 1 of 1

Using serendipity_fetchEntries and serendipity_printEntries

Posted: Wed Nov 23, 2005 11:41 pm
by judebert
I've searched, really I have. I've tried multiple solutions. I just don't understand, and I'm feeling kinda idiotic right now.

I'm making "newsboxes". I'm envisioning it to be a div, styled like a dategroup, with entries styled like, well, entries. This newsbox will contain the entries for a particular category (for instance, "news"). The entries in the newsbox will not be contained in the overview list; they will only be in the newsbox. Eventually, newsboxes could contain other newsboxes; then I'll be able to have a newsbox on top of my frontpage, on one side containing the latest information on one important topic, and on the other side containing the latest information on a different important topic, and underneath it would go all the site updates.

Phew.

I'm programming a stackable event plugin to accomplish this. As I mentioned before, I'm trying to take baby steps. Garvin showed my the proper SQL to exclude entries from the mainpage listing. I've found that serendipity_fetchEntries can be used with the sql_filter to retrieve those entries again. Now I need to format them to HTML, complete with CSS tags -- in short, I need to run them through Smarty.

I just can't get it to work. I've tried multiple approaches, as specified in th code below:

Code: Select all

<?php # $Id: serendipity_event_newsbox.php 001 2005-11-17 09:01:45E judebert $

// Built from serendipity_event_entryproperties, with help from serendipity_event_includeentries

// Probe for a language include with constants. Still include defines later on, if some constants were missing
$probelang = dirname(__FILE__) . '/' . $serendipity['charset'] . 'lang_' . $serendipity['lang'] . '.inc.php';
if (file_exists($probelang)) {
    include $probelang;
}

@define('PLUGIN_EVENT_NEWSBOX_TITLE', 'News categories');
@define('PLUGIN_EVENT_NEWSBOX_DESC', 'Group a category\'s entries in a frontpage box instead of the usual article listing');
@define('PLUGIN_EVENT_NEWSBOX_NEWSCATS', 'Which categories will be moved to this newsbox?');
@define('PLUGIN_EVENT_NEWSBOX_NEWSCATS_DESC', 'Here you can select the categories whose entries will be removed from the frontpage listing. Those entries will instead be added to this newsbox.  You can pick any number of categories (even none, but then your newsbox will be empty).');
//@define('PLUGIN_EVENT_NEWSBOX_CUSTOMFIELDS_DESC3', 'The list of available custom fields can be changed in the <a href="%s" target="_blank" title="' . PLUGIN_EVENT_NEWSBOX_TITLE . '">plugin configuration</a>.');

class serendipity_event_newsbox extends serendipity_event
{
    var $services;
    var $title = PLUGIN_EVENT_NEWSBOX_TITLE;

    function introspect(&$propbag)
    {
        global $serendipity;

        $propbag->add('name',          PLUGIN_EVENT_NEWSBOX_TITLE);
        $propbag->add('description',   PLUGIN_EVENT_NEWSBOX_DESC);
        $propbag->add('stackable',     false);
        $propbag->add('author',        'Jude Anthony');
        $propbag->add('version',       '0.1');
        $propbag->add('requirements',  array(
            'serendipity' => '0.8',
            'smarty'      => '2.6.7',
            'php'         => '4.1.0'
        ));

        $propbag->add('event_hooks',    array(
            'frontend_fetchentries'        => true,
            'frontend_fetchentries_sqldbg' => true,
            'frontend_header'              => true,
            'entries_header'               => true,
            'entries_footer'               => true,
            'frontend_footer'              => true
        ));
        $propbag->add('groups', array('BACKEND_EDITOR'));
        $propbag->add('configuration', array('news_cats', 'sqldbg'));
    }

    function introspect_config_item($name, &$propbag)
    {
        switch($name) {
            case 'news_cats':
                $propbag->add('type',    'content');
                $propbag->add('default', $this->makeCategorySelector());
                // Name and description aren't used for type 'content'.
                return true;
                break;
            case 'sqldbg':
                $propbag->add('type', 'boolean');
                $propbag->add('name', 'Debug final SQL?');
                $propbag->add('description', 'Prints the SQL on the page just before it is executed.  Only prints if categories exclusion code will be executed.  If you have multiple newsboxes, you obviously don\'t want to enable this multiple times.');
                $propbag->add('default', 'true');
                break;
        }
        return true;
    }

    // Ah, a neat trick.  See, when someone hits the "save" button for 
    // configuration, it returns us to the configuration screen.  In that case,
    // s9y is going to call introspection again.  This gives us a chance to
    // check variables and change settings, even though the "configuration
    // saved" message has already been printed.  A bit deceptive, but 
    // effective.
    //
    // Strangely, while serendipity_event_includeentries can use the same
    // string for the POST variable and the configuration variable name, I
    // get an error about htmlspecialchars() at admin/plugins.inc.php line
    // 356: an array is passed to parameter 1.  It's easily fixed by making
    // the two strings slightly different.
    function &makeCategorySelector() 
    {
        global $serendipity;

        $html = '<strong>'. PLUGIN_EVENT_NEWSBOX_NEWSCATS .'</strong><br />';
        $html .= '<span style="color: rgb(94, 122, 148); font-size: 8pt;">';
        $html .= PLUGIN_EVENT_NEWSBOX_NEWSCATS_DESC . '</span><br />';
        $html .= '<strong>' . CATEGORIES . '</strong><br />';
        
        // $selected_cats is used to set up the selections; 
        // 'news_cats' holds the value when it's set.
        if (is_array($serendipity['POST']['plugin']['newscats'])) 
        {
            // Someone has already selected categories
            $selected_cats = array();
            foreach ($serendipity['POST']['plugin']['newscats'] AS $idx => $id) 
            {
                $selected_cats[$id] = true;
            }
            $catlist = implode(',', array_keys($selected_cats));
            $this->set_config('news_cats', $catlist);
        } else {
            // Form is just being displayed; get previously selected categories
            // Must use default value! 'false' is very fast.
            $catlist = $this->get_config('news_cats', false);
            if (!$catlist)
            {
                $selected_cats = array();
            }
            else
            {
                $cat_ids = explode(',', $catlist);
                foreach ($cat_ids AS $id)
                {
                    $selected_cats[$id] = true;
                }
            }
        }

        $html .= '<select name="serendipity[plugin][newscats][]" multiple="true" size="5">';
        if (is_array($cats = serendipity_fetchCategories())) {
            $cats = serendipity_walkRecursive($cats, 'categoryid', 'parentid', VIEWMODE_THREADED);
            foreach ( $cats as $cat ) {
                $catid = $cat['categoryid'];
                $html .= '<option value="'. $catid .'"'. (isset($selected_cats[$catid]) ? ' selected="selected"' : '') .'>'. str_repeat(' ', $cat['depth']) . $cat['category_name'] .'</option>' . "\n";
            }
        }

        $html .= '</select><hr>';
        
        return $html;
    }

    function generate_content(&$title) {
        $title = $this->title;
    }


    function event_hook($event, &$bag, &$eventData, $addlData = null) {
        global $serendipity;

        $hooks = &$bag->get('event_hooks');

        // I'm not really certain why this is here (since we only get called 
        // for the events we hooked), but Garvin uses it, so it must be 
        // important.
        if (isset($hooks[$event])) {
            switch($event) {
                case 'frontend_fetchentries':
                    // No joins required!
                    // $joins = array();
                    $conds = array();

                    // If we're fetching entries for the frontpage...
                    // (and not newsbox entries, either!)
                    //
                    // I tried Garvin's subpage thing here, but it just never
                    // worked for me.  I've always got a subpage set.
                    $source = $addlData['source'];
                    if ((!isset($serendipity['newsbox']) || $serendipity['newsbox'] != 'no_exclude') 
                        && ($source == 'entries') 
                        && (empty($args) || trim($args) == $serendipity['indexFile']) 
                        && !isset($serendipity['GET']['adminModule']) 
                        && !isset($serendipity['GET']['category'])
                       )
                    {
                        $news_cats = $this->get_config('news_cats');
                        if (isset($news_cats) && !empty($news_cats))
                        {
                          // Exclude entries in the newbox
                          $conds[] =
                            ' (e.id NOT IN (SELECT entryid from ' 
                            . $serendipity['dbPrefix'] . 'entrycat'
                            . ' WHERE categoryid IN (' . $news_cats . ')))';
                        }
                    }

                    if (count($conds) > 0) {
                        $cond = implode(' AND ', $conds);
                        if (empty($eventData['and'])) {
                            $eventData['and'] = " WHERE $cond ";
                        } else {
                            $eventData['and'] .= " AND $cond ";
                        }
                    }

                    return true;
                    break;

                // For debugging only
                case 'frontend_fetchentries_sqldbg':
                    if ($this->get_config('sqldbg'))
                    {
                            print('Actual SQL query: <br><pre>');
                            print_r($eventData);
                            print('</pre>');
                    }
                    break;

                // For now, I only add at the entries' top.  Later I'll make
                // it configurable.
                case 'entries_header':
                  if (!isset($serendipity['newsbox']))
                  {
                    $news_cats = $this->get_config('news_cats');
                    $sql = "\n" . ' e.id IN ' . "\n"  
                        . '(SELECT entryid FROM ' 
                        . $serendipity['dbPrefix'] . 'entrycat'
                        . "\n" . ' WHERE categoryid IN (' 
                        . $news_cats . ')' . "\n" . ')';

                    // Can't use fetchEntries, since I'm already excluding 
                    // entries from there, unless I add extra flags to my
                    // exclusion logic.
                    $serendipity['newsbox'] = 'no_exclude';
                    $entries = serendipity_fetchEntries(null, true, 2, false, false, 'timestamp DESC', $sql);

                    // Now I've gotta figure out how to print the entries.

                    // Just including this caused a 500 Internal Server Error.
                    // I figure I was recursively calling it; when I use 
                    // serendipity_printEntries, I bet it triggers 
                    // entries_header.  I wrapped the whole thing in a big if
                    // statement, and now it prints only the newsbox. Wrapping
                    // it to preserve and reset the $serendipity variable 
                    // doesn't help.
                    serendipity_printEntries($entries); 

                    // This didn't help; it doesn't seem to print anything but
                    // the standard entry list.
                    //serendipity_smarty_fetch('ENTRIES', 'entries.tpl', true);

                    // No noticeable difference.
                    //serendipity_plugin_api::hook_event('frontend_display', $entries);
                    
                    // Works, displays entire array, no formatting (of course)
                    //print_r($entries);

                    // This works, but it doesn't format 
                    /*
                    foreach ($entries as $entry)
                    {
                      echo $entry['title'];
                      echo $entry['body'];
                    }
                    */

                    // I tried a different block, but still no luck... until 
                    // I echoed it!  Then everything was printed twice. I 
                    // wrapped it to save the old $serendipity and tried
                    // again.  Still double entries.  Double entries when I
                    // used the 'ENTRIES' block.
                    //echo serendipity_smarty_fetch('NEWSBOX', 'entries.tpl', true);

                    // Looking in entries.tpl, it seems that we're already
                    // printing the entry list, and the $entries variable 
                    // needs to be modified.  That resulted in two blank 
                    // prints; obviously the Smarty entries variable needs to
                    // be massaged as in serendipity_printEntries() first, and
                    // I need to replace it afterwards.  That's a lot of work.
                    //$serendipity['smarty']->assign('entries', $entries);
                    //echo serendipity_smarty_fetch('NEWSBOX', 'entries.tpl', true);

                    // This lower-level version echoes an empty newsbox, then
                    // the content.  That's closer!  At least I'll only need
                    // to massage the data, not wrap it, too.  I tried looping
                    // over the entries individually, but there's no entry.tpl,
                    // and entries.tpl is expecting an array.
                    //$smarty = $serendipity['smarty'];
                    //$smarty->assign('entries', $entries);
                    //echo $smarty->fetch('entries.tpl');

                    // Sigh.  Perhaps I should simply add my entries to the
                    // $serendipity['entries'] at the entry_display hook.
                    
                    unset($serendipity['newsbox']);
                  }
                    break;
                case 'entries_footer':
                case 'frontend_header':
                case 'frontend_footer':
                    break;
                default:
                    return false;
                    break;
            }
        } else {
            return false;
        }
    }
}

/* vim: set sts=4 ts=4 expandtab : */
?>
Sorry for the long post. Any takers? This is Serendipity 0.9. How do you Smarty-format the entries retrieved from serendipity_fetchEntries without duplicating the entire serendipity_printEntries function?

Re: Using serendipity_fetchEntries and serendipity_printEntr

Posted: Thu Nov 24, 2005 12:13 am
by garvinhicking
:)

Heck, you get +10 Karma points from me for trying so hard. I like that.

Now, you've almost exactly figured it out. Your core problem is that the printEntries() function uses the same "namespace" for assigning $entries, and that it recursively calls itself when being called time and again.

I'm afraid at this time (it's 12pm here in germany) I can only give you a few hints. They might not all interact, but since you're great at trying things out, they might be of help.

A: You might want to make your plugin assign a global variable like "$serendipity['is_running_fetchentries']". Only execute your event hook when this variable is not set, so that you won't run into recursion.
This might not solve problems with other plugins doing their plugin calls multiple times.
The best solution in that case might be to patch up our printEntries() function for a new parameter, which does not execute certain plugin hooks like entries_header. I'm gladly willing to include that patch in the official s9y.

B: Your idea of cloning the $serendipity['smarty'] object sounds like a good idea. You write:

Code: Select all

                    // This lower-level version echoes an empty newsbox, then
                    // the content.  That's closer!  At least I'll only need
                    // to massage the data, not wrap it, too.  I tried looping
                    // over the entries individually, but there's no entry.tpl,
                    // and entries.tpl is expecting an array.
                    //$smarty = $serendipity['smarty'];
                    //$smarty->assign('entries', $entries);
                    //echo $smarty->fetch('entries.tpl'); 
What is the problem here? Just wrap $entries in a "dategroup" approach, but then again you need to duplicate certain functionality of printEntries() function.

C. You should be able to do something nasty like this:

Code: Select all

// Clone our object, so that we can rape the copy ;)
if ($serendipity['in_smarty']) return;
$serendipity['in_smarty'] = true;
$oldsmarty = $serendipity['smarty'];
// Process our input data
serendipity_printEntries($entries);
unset($serendipity['in_smarty']);
// Echo the newsbox part, that (should) only contain our entries
echo serendipity_smarty_fetch('NEWSBOX', 'entries.tpl', true);
// Restore the object for further object flow.
$serendipity['smarty'] = $oldsmarty;
With this solution you might run into the duplicat entries_header call problem as well, though. Patching up serendipity_fetchEntries() like mentioned above sounds very useful to me, now.

Hope to be of any help. I love the way you keep poking at it, you're being a valuable addition to us developers in the future, I think. :-)

Best regards,
Garvin

Posted: Thu Nov 24, 2005 4:41 am
by judebert
Holy carp, it worked! That little piece of work had just about defeated me. I really appreciate the fast response, too, especially considering the hour. Your commitment to this software amazes me.

I basically implemented part C, since part A's recursion variable was already defined, and I don't understand enough of the printEntries code to duplicate it reliably for part B. (Besides, who wants to duplicate code?)

I was already using $serendipity['newsbox'] to determine whether I was in the callback or not for fetching, so the wrapping just seemed natural; it took four lines of code.

I did run into the problem with other plugins: the newsbox includes an HTML nugget added specifically for this testing, which is duplicated for the regular entries. Additionally, the newsbox includes an entry footer, also duplicated after the regular entries. I don't think these can be easily eliminated, since they're called from the index.tpl, not Serendipity core. Preventing that would mean patching serendipity_smarty_hookPlugin(), or even hook_event().

Wait a minute... $serendipity['no_events'] already prevents hooks in hook_event(). I'll just wrap that around the methods, too...

Well, that fixed the HTML nugget. It now appears according to plugin order, as expected. I'm still getting the extra footer, but I'll deal with that after I decide what kind of paging to use for newsboxes.

I'll look into splitting the data-massaging portions of serendipity_printEntries() into a no-hooks, no-frills generic function, which we can then call from printEntries for minimum possible impact. Don't hold your breath, though; it looks pretty tough to genericize. Then again, it's only called in 4 places from genpage.inc.php, 1 place in functions_config.inc.php, and old templates.

Posted: Thu Nov 24, 2005 9:56 am
by garvinhicking
Hi Judebert!
judebert wrote:Holy carp, it worked! That little piece of work had just about defeated me. I really appreciate the fast response, too, especially considering the hour. Your commitment to this software amazes me.
Great to hear that! Well...Serendipity is something like my baby, so it gets some special care ;)
I basically implemented part C, since part A's recursion variable was already defined, and I don't understand enough of the printEntries code to duplicate it reliably for part B. (Besides, who wants to duplicate code?)
Yeah, I thought so that this might be the best approach.
Wait a minute... $serendipity['no_events'] already prevents hooks in hook_event(). I'll just wrap that around the methods, too...
I think you'll get more problems by using that 'no_event' hook. Because then, no hooks like frontend_display (for "rendering" an entry with bbcode, emoticons and stuff) will be executed.

I'm afraid you'll be needing to patch some of the core s9y functions. But I see no trouble with that, it will sure be used by other plugins in the future. So have a whirl, and I'm sure we can include it, if it's generic enough.
I'll look into splitting the data-massaging portions of serendipity_printEntries() into a no-hooks, no-frills generic function, which we can then call from printEntries for minimum possible impact. Don't hold your breath, though; it looks pretty tough to genericize. Then again, it's only called in 4 places from genpage.inc.php, 1 place in functions_config.inc.php, and old templates.
I think that might work, if you capsulate the "important" stuff of printEntries() in a "corePrintEntries()" function or so. Then calls to that function can still be made from old plugins/templates, but your new plugin can use the corePrintEntries() function...?

Keep me posted when you have issues with that, or when you have finished it. I would love to see it! :)

Best regards,
Garvin

Posted: Thu Nov 24, 2005 10:02 am
by garvinhicking
I thought a bit on that.

I think it's way easier to patch up serendipity_printEntries().

How about this patch http://nopaste.php-q.net/174358

It abstracts the call fair enough to not pass in all of your other plugins, what do you think? I could commit it to our SVN repository, if you like, but I'll like to get feedback if it works out better for you before.

Regards,
Garvin

Posted: Fri Nov 25, 2005 3:55 am
by judebert
Thanks!

Sorry, it's Thanksgiving here, and my wife insists I spend time with our families. Sheesh. Some people need to get their priorities straight. :wink:

Tomorrow is Black Friday, where I'll be off to buy a 200GB hard drive for $30. So I might not have much time for programming until the weekend.

Anyway, I gave it a shot. It didn't hurt anything, as expected (using just the new functions_entries and the old newsbox code, no noticeable change).

I figured the biggest advantage here was the parameters for no_hooks, smarty_block, and no_fetch (although no_footer might be useful sometime later). I removed the clone wrapper and called:

Code: Select all

serendipity_printEntries($entries, 0, false, 'NEWSBOX', false, false, false);
echo serendipity_smarty_fetch('NEWSBOX', 'entries.tpl', true);
That produces two identical newsboxes, with the HTML top-nugget echoed twice, and the HTML bottom-nugget at the bottom of each newsbox, and no main listing.

I switched to:

Code: Select all

 serendipity_printEntries($entries, 0, false, 'NEWSBOX', false, false, false);
...which produced a single newsbox with properly-placed top and bottom nuggets, but no main listing.

I also tried:

Code: Select all

serendipity_printEntries($entries, 0, false, 'NEWSBOX', true, false, false);
... which was identical.

I double-checked to ensure the patched functions_entries was uploaded, and it is. I'll add debugging and see what I come up with. Right now, it looks like I shouldn't have removed the clone wrapper; but then the new printEntries only buys me no_hooks, and even then the template calls hooks.

We may want to think about wrapping $s9y['no_events'] inside the printEntries function around the Smarty fetch. That way I'll miss the template hooks (like HTML nuggets), as well as the 'entry_display' hook.

Nice job with the parameters. You included not only what I needed, but what future plugins might need, too. I'd like to be able to pass an array for custom entry properties (like links and titles) that I can't set from the $entries array; and perhaps we should be able to assign to a Smarty variable other than 'entries'. But I'll look at those things when I get the time, if someone else hasn't looked at them first.

Oh, and with the function arguments getting so long, we may want to consider using a named-index array instead of arguments. That way we could make calls for only the functionality we need, something like: serendipity_printEntries($myEntries, array('no_hooks' => true, 'smarty_block' => 'NEWSBOX'));

Posted: Fri Nov 25, 2005 1:31 pm
by garvinhicking
Judebert, I have the solution for you.

I forgot about the hooks that are executed within the entries.tpl file.

You could theoretically already bypass those hooks by customizing your entries.tpl file and adding a Smarty variable check like

Code: Select all

{if !$dategroup.skip_hooks}
  {serendipity_hookPlugin hook="entries_header" addData="$entry_id"}
{/if}
But that wouldn't be cool for a plugin, would it?

Thus I just patched the serendipity_smarty.inc.php function and added this:

Code: Select all

    // Smarty hooks can be bypassed via an internal variable (temporarily)
    if (isset($serendipity['skip_smarty_hooks']) && $serendipity['skip_smarty_hooks']) {
        return;
    }

    // A specific hook can also be bypassed by creating an associative array like this:
    // $serendipity['skip_smarty_hook'] = array('entries_header');
    // That would only skip the entries_header event hook, but allow all others.
    // Of course it cannot be used in conjunction with the all-blocking skip_smarty_hooks.
    if (isset($serendipity['skip_smarty_hook']) && is_array($serendipity['skip_smarty_hook']) && isset($serendipity['skip_smarty_hook'][$params['hook']])) {
        return;
    }
That means before you call serendipity_printEntries you can set the skip_smarty_hooks variable, and after the function call, you unset that variable again. Easy, eh?
I'd like to be able to pass an array for custom entry properties (like links and titles) that I can't set from the $entries array; and perhaps we should be able to assign to a Smarty variable other than 'entries'. But I'll look at those things when I get the time, if someone else hasn't looked at them first.
You should be able to pass that already within the entries! All extra array keys will also be assigned to your smarty $entry associative array!
Oh, and with the function arguments getting so long, we may want to consider using a named-index array instead of arguments.
I find those named-index arrays pretty hard to use. One might misspel an option index key and easily get confused, and the phpdoc syntax I'm currently implementing does AFAIK not allow to easily document those function calls.

Plus, it would break BC. :)

Best regards,
Garvin

Posted: Fri Nov 25, 2005 2:12 pm
by judebert
Thanks for all the help, Garvin.

You're right, the extra $serendipity variable wrapping is simple. The same kind of thing we're doing already. I especially like the way specific hooks can be skipped, and the multiple-return implementation makes things super-easy without huge switch statements.

But as I drifted into turkey-induced stupor last night, I realized that the problem isn't Serendipity: it's me. If I ever had this much trouble with a function at my real job, I'd say the design was shot. (Boss, if you're reading: not that I ever do, but if... :wink:)

When I woke up, I realized that's really the problem here, too. I'm fetching the entries and calling printEntries from a hook inside the printEntries method. That's causing all kinds of variable scope, event hook, and recursion problems. (Specifically, I'm overwriting the Smarty 'entries' variable.)

I think we've pretty much resolved the event hook problems, and that's especially good because other plugins could need the same sort of thing in the future. These "newsboxes" are moving S9y closer to a CMS than a blog, and I don't doubt there are more plugins to try the same thing later.

But the other problems are really in my design. I'm going to try moving the newsbox fetch-and-format to another hook. That should resolve the variable scope and recursion problems. I'll let you know how it works out.

Posted: Fri Nov 25, 2005 4:03 pm
by garvinhicking
Okay, that's cool to hear!

You might enjoy that I'm currently working on documenting all functions :)

I'll be away during the weekend, just to let you know. You should probably do the same with your family. *gg*

Best regards,
Garvin

Posted: Fri Nov 25, 2005 4:41 pm
by judebert
Well, I tried the genpage hook, figuring it would cause the least problems. Unfortunately, the $serendipity['smarty'] hadn't been instantiated at that point, so I got an error in functions_entries.inc.php. (Can't call <smarty>->assign('entries', ...) when there's no object to call on.)

That switched me over to the frontend_fetchentries hook. I fetch and 'print' the entries there, save the HTML in an object variable, and echo it on the entries_header hook. This works well, and with the new hook exclusion, I don't get extra nuggets or paging links. It's just about perfect.

I'd like to request an additional hook after Smarty instantiation, before page content is actually generated, something like "init_complete". Plugins would then know that all s9y functionality was ready to call.

I've also copied your hook-skipping code into plugin_api.inc.php. Here's a diff:

Code: Select all

--- plugin_api.inc.php     2005-11-25 09:53:42.207346280 -0500
+++ plugin_api.inc.php  2005-11-25 10:07:12.190210168 -0500
@@ -714,7 +714,15 @@
         if (isset($serendipity['no_events']) && $serendipity['no_events'] == true) {
             return false;
         }
-
+
+        // A specific hook can also be bypassed by creating an associative array like this:
+        // $serendipity['skip_hook'] = array('genpage', 'frontpage_fetchentries');
+        // That would only skip the genpage and frontpage_fetchentries event hook, but allow all others.
+        // Of course it cannot be used in conjunction with no_events.
+        if (isset($serendipity['skip_hook']) && is_array($serendipity['skip_hook']) && isset($serendipity['skip_hook'][$params['hook']])) {
+            return false;
+        }
+
         $plugins = serendipity_plugin_api::get_event_plugins();

         if (is_array($plugins)) {
@@ -722,7 +730,7 @@
             @reset($plugins);
             while(list($plugin, $plugin_data) = each($plugins)) {
                 $bag = &$plugin_data['b'];
-                if (array_key_exists($event_name, $bag->get('event_hooks'))) {
+                if ((array_key_exists($event_name, $bag->get('event_hooks'))) or ($bag->is_set('promiscuous'))) {
                     // Check for cachable events.
                     if (isset($eventData['is_cached']) && $eventData['is_cached'] && array_key_exists($event_name, (array)$bag->get('cachable_events'))) {
                         continue;
You might notice that I also added a 'promiscuous' setting to the propbag. If a plugin is promiscuous, it receives all events. I used it for a debugging plugin that printed all the events and their data. Extremely convenient.
You should be able to pass that already within the entries! All extra array keys will also be assigned to your smarty $entry associative array!
Sorry, I'm not being very clear.

We call:

Code: Select all

            $entry['link']      = serendipity_archiveURL($entry['id'], $entry['title'], 'serendipityHTTPPath', true, array('timestamp' => $entry['timestamp']));
to set the link for the entry. I'd like to be able to customize this; if I added a 'link' array key, it would just get overwritten. Upon reflection, it would probably be better to just set the thing after it came out of printEntries.

Which brings me to the Smarty blocks. I can't find any documentation on the Smarty website, and I'm not sure why I'd ever want printEntries to perform the smarty_fetch on its own (since it doesn't echo after the fetch). It looks like it only adds the formatted HTML to the Smarty variable; since we've already called the Smarty template, what's the point?

Posted: Fri Nov 25, 2005 6:13 pm
by garvinhicking
Hi!

You can call serendipity_smarty_init() in your genpage hook, I don't think another plugin hook is necessary after the function call. This really costs some performance pennies.

The same applies to your promiscuis settung, it does cost a few bucks if you query that for each plugin. The proper way to get all event hooks IMHO would be temporarily patching the plugin API in your way - but I don't really like adding it into the default package...
We call:

Code: Select all

            $entry['link']      = serendipity_archiveURL($entry['id'], $entry['title'], 'serendipityHTTPPath', true, array('timestamp' => $entry['timestamp']));
to set the link for the entry. I'd like to be able to customize this; if I added a 'link' array key, it would just get overwritten. Upon reflection, it would probably be better to just set the thing after it came out of printEntries.
You can change those via 'frontend_display:html:per_entry' event hook. :)

Best regards,
Garvin

Posted: Sat Nov 26, 2005 3:46 am
by judebert
Thanks for everything, Garvin. I wasn't aware of the performance issues.

I'll be passing you the completed plugin once it's done, probably a few weeks.