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 : */
?>