Customizing a Wordpress function
June 30, 2008 6:36 AM   Subscribe

Wordpress/PHP question. I'm working on a Wordpress theme and want to use an algorithm to alter some of the HTML it emits. I've got a general idea of how to do this, and could probably manage a brutish implementation on my own, but my PHP-fu is weak and there is probably a better way.

Here's what's going on. I'm using the blueprint css approach for a gridded layout. And I've got a "bottom-bar" of widgets, laid out in three columns. With blueprint css, the div for the third column needs class="last" added to it in (in addition to class declarations that apply to all divs) order for everything to line up right.

What I want to do is get the count of widgets each time a widget is emitted, and if count mod 3 = 0, insert 'last' into the class. Barring that, if I could insert class="widget-N" (where N=count) in all divs, I could manage, although it would be less elegant. Ideally, all browsers would magically support CSS3 and obviate this problem, but I'm not holding my breath.

Obviously I don't want to touch the core code. I don't mind inserting an altered version of whatever function is necessary into my functions.php file; but if there's a callback that lets me avoid doing that, so much the better.
posted by adamrice to Computers & Internet (16 answers total)
 
for ( $i = 0; $i <> {
$last = ($i % 3) ? '' : 'last';
echo "class=\"widget $last\"";
}

Should do it. No idea where to stick it, though.
posted by Leon at 6:49 AM on June 30, 2008


Stupid brackets. Lets try this. The important linea are the one with the ? and the echo, anyway.

for ( $i = 0; $i < sizeof( $widgets ); $i++ )
{
$last = ($i % 3) ? '' : 'last';
echo "class=\"widget $last\"";
}

Should do it. No idea where to stick it, though.
posted by Leon at 6:52 AM on June 30, 2008


Response by poster: Yeah, it's the "knowing where to stick it" part that gets me too. I could manage the function.
posted by adamrice at 7:06 AM on June 30, 2008


I'm not sure what you're going for leon, your code would just print out:

class="widget "class="widget "class="widget last"class="widget "class="widget "class="widget last"class="widget "class="widget "class="widget last"class="widget "class="widget "class="widget last"

Which doesn't seem very helpful.

PHP has a document object model you can use to manipulate HTML the same way you can in javascript, but unlike Javascript, you have to load the HTML and then manipulate it as a string, rather then modifying the DOM from 'inside' the page.

But you could do something like creating a new PHP page that loads your wordpress blog in the background (using fopen), modifies it using the DOM, and then sends the results to the browser. That has a bonus of not needing to modify any of the wordpress code.
posted by delmoi at 7:17 AM on June 30, 2008


When you register your sidebars in the functions.php file of your theme, you can choose to create unique IDs for each widget (see here: http://automattic.com/code/widgets/api/)

example code:
'before_widget' => '<li id="%1$s" class="widget %2$s">',
'after_widget' => "</li>n",
'before_title' => '<h2 class="widgettitle">',
'after_title' => "</h2>n"
so if your layout only supports a maximum of 3 widgets to float in your bottom bar, you can write your function so that the third widget always is set to clear:both, and widgets 1 and 2 only do if there are not 3 widgets selected. This is, as you mention, less elegant, but keeps most of the functionality in the style sheet as opposed to mucking about with hooks (tho there may be a hook for the end of a sidebar, I'd look into that as well...)
posted by annathea at 8:49 AM on June 30, 2008


Ok, this is majorly evil because it hacks wordpress core and isn't going to allow you to upgrade, but I don't have time to work up a hack using the before_widget callback ... My testing is limited, so this might not be 100%, but you should see how to get it to work.

change line 223 in widgets.php from:
foreach ( $sidebars_widgets[$index] as $id ) {
to:
foreach ( $sidebars_widgets[$index] as $count=>$id ) {
Then, add on after line 237 (right after $classname_ = ltrim($classname_, '_');):
$classname_.=$count%3?' last':''; //note space in front of ' last' 
That should do it.

Can I say again this is TOTALLY evil and that you shouldn't use it because you're going to screw up your upgrade path? Additionally, it doesn't handle cases where you don't have a number of widgets exactly divisible by 3. I'm not sure what the correct solution is but there is probably a way for a widget to get its ordering from the hook system and thus add a plugin that handles the classname logic. In fact, even if it doesn't send a count, you could keep your own count in a separate plug (using a static var) and do it. So do that.

oh psh, i just saw "Obviously I don't want to touch the core code.". Good choice.

well, looks like you're using the 'dynamic_sidebar_params' filter callback to do something similar.

Actually, if you're not writing a template for general consumption, I would probably prefer the annathea method, because your widget order is likely to be pretty static. Just give the 'last' behavior to the last element in each row. Lots faster than mucking about with plugins and a lot nicer than altering core.
posted by fishfucker at 9:14 AM on June 30, 2008


Response by poster: fishfucker is going down the same path I've been going down. I've created a customized version of the dynamic_sidebar function, doing almost exactly what he describes, and stuck that in functions.php (I've also realized the situation could be further complicated by the fact that I'm using two sidebars, and only want to mung the widgets in one of them). Also, fwiw, the layout works fine even if the last widget is not exactly divisible by 3.

What annathea describes happens by default. If this theme were only for my own consumption, I'd probably content myself with her method, but I'd like to make it publicly available.

What delmoi describes sounds interesting, but I have no idea where to start with that. How would I intercept the PHP being emitted by WP?

I also figured out a janky way of handling this problem in CSS with no PHP munging. I'm not thrilled with any of the options right now.

It doesn't seem like there's an orthodox way to get the current widget count and to inject a value returned by an external function into the widget wrapper, which would be ideal. For the time being, I am going with the evil option.
posted by adamrice at 9:53 AM on June 30, 2008


Seconding annathea. The Widgets API specifically includes register_sidebar() arguments for this purpose.
posted by theiconoclast31 at 10:02 AM on June 30, 2008


Messing with core code doesn't have to cut off your upgrade path if you use Subversion for your install. The Wordpress site has instructions. If you're not familiar with Subversion, it's a version control program that tracks changes to software source code. If you make changes in your installation, Subversion will allow you to merge your changes in with any changes made by WordPress. I use Subversion for my WordPress and MediaWiki installations, and it works great.

Even if you don't have command-line access to your server, you can install a Subversion client on your local machine, manage your changes there, and then upload your finished files.
posted by expialidocious at 10:34 AM on June 30, 2008


My method would work if the sprintf directives %1$s and %2$s represented unique numeric values based on count, instead of slugs based on the widget name. But they don't. So we need to edit that code to return a unique numeric value based on the widget count. I'll post back if I figure out how to make that happen with a native WordPress function instead of a core hack.
posted by annathea at 10:34 AM on June 30, 2008


What delmoi describes sounds interesting, but I have no idea where to start with that. How would I intercept the PHP being emitted by WP?

Yeah, you really don't want to do that.

Essentially he's suggesting that you render the sidebar and then work on it using DOM tools.

Because WP echoes to screen, you could simply start an output buffer before the dynamic_sidebar gets called, flush it after that chunk, and then run it through a DOM parser (whose tools might not even work correctly with HTML fragments).

But this is even more hacky than playing with core, imho.

I really think you want to take a look at writing a filter -- that's the one hook that gets called there. My guess is you *could* do something like:
add_filter('dynamic_sidebar_params','filter_add_last');

function filter_add_last(&$params) {
static $count;
$count=($count==null)?0:$count+1;
$count$params[0]['before_widget'].=($count%3)?' last':'';
}
but I haven't tested it. and using statics is kinda ugly.

Also, fwiw, the layout works fine even if the last widget is not exactly divisible by 3

If you're not doing a "last" for each final element, i think you'll have problems once you have an abutting div -- if there's room, blueprint tends to be ok, but if you're not leaving tolerances the 'last' class is pretty essential, particularly for IE6 compatibility. As long as you set 'last' on each of the elements that is last in each row, you should be fine -- even when there are less than 3, although ones without extra elements will fill an entire row, in theory. Obviously I don't know what your markup looks like so this is all supposition.
posted by fishfucker at 10:37 AM on June 30, 2008


Best answer: blah, that last line should read
$params[0]['before_widget'].=($count%3)?' last':'';

posted by fishfucker at 10:39 AM on June 30, 2008


Response by poster: fishfucker—

add_filter is a new one on me. I will explore that. And now I see how the last widget not being a multiple of 3 could be a problem. This is solvable using wp_get_sidebars_widgets(), which is an array of sidebars, each containing an array of widgets. It's possible to get the total number of widgets in each sidebar through that function, and change this:
$count$params[0]['before_widget'].=($count%3)?' last':'';

to (less compactly)
if(($count%3) or ($count == $total_widgets)) {
$count$params[0]['before_widget'].= ' last';
}

(actually, I may wind up using switch for other cases but you get the idea).

In the meantime, I'm happy to get additional help, and grateful for all the help I've gotten so far.
posted by adamrice at 11:56 AM on June 30, 2008


i wrote you a quick plugin. It should work. I have a meeting in 10 minutes so I can't explain it, let me know if you have any probs.

Widget Class Count
posted by fishfucker at 1:52 PM on June 30, 2008


ah crapadapalicious.

Widget Class Count
posted by fishfucker at 1:52 PM on June 30, 2008


Response by poster: Thanks again to everyone, and magical-pony thanks to fishfucker. I haven't checked out that plugin yet, but he got me on the right track.

I've got things working without any obviously breakable kludges. This is an update to a theme I've previously posted to Projects, and I'll have that update live in a few days, in case anyone wants to point and laugh at my code.
posted by adamrice at 2:27 PM on June 30, 2008


« Older Help me bring my comic to life.   |   VTIVX FOF FTW? Newer »
This thread is closed to new comments.