Half-Elf on Tech

Thoughts From a Professional Lesbian

Tag: widget

  • Always Validate Data

    Always Validate Data

    In the making of my REST API plugin’s extension, I had written out a way to select what happened on a specific date. While adding that kind of code into a shortcode was something I’d done before, I struggled to try and understand how I could do that with a widget in a way that was safe.

    On Data Validation

    The main function that does all the real work expects a date parameter in the format of MM-DD which it converts to YYYY-MM-DD so I can run date() calls on it later on. The reason that works is that the PHP date() function can parse the YYYY-MM-DD format on it’s own. To that end, I have to write the widget and shortcode in such a way that it’s impossible to pass on bad data.

    This is a matter of an age old problem: sanitize, escape, and validate.

    If you’ve written a plugin and think that sounds familiar, yes, it is a common sticking point of mine. You must sanitize your data before you save it. You must escape it before you display it. You should validate it to make sure you’re not letting people make silly mistakes.

    The ‘problem’ is that there are a lot of ways to do that, and the ultimate question is this “What is the data I’m trying to save supposed to look like?” If it’s a URL, you sanitize for a URL. If it’s a number, you sanitize for a number. That’s the easy part. It’s just really time consuming and difficult. Still. Sanitize early, escape late, and always validate.

    The Main Function Sanitizes and Validates

    Instead of only having the shortcode or the widget make sure the date was right, I decided to attack the matter further up the chain. Or down the chain, depending on your point of view. Both the shortcode and widget call the same master function so for that, I make sure that the value of $this_day is never anything than what I want:

    	public static function on_this_day( $this_day = 'today' ) {
    		$this_day = sanitize_text_field( $this_day );
    		if ( $this_day !== 'today' ) {
    			$month = substr( $this_day, 0, 2);
    			$day = substr( $this_day, 3, 2);
    			$this_day = ( checkdate ( $month, $day , date('Y') ) == true )? $this_day : 'today' ;	
    		}
    
    	[... the rest of the code continues on]
    	}
    

    By using checkdate(), I verify that the value is either a valid date or I force it to be ‘today’. This prevents someone from passing bad data in the shortcode. I sanitize it as well as validate, using sanitize_text_field() because there’s no reason to even allow non-safe data anywhere further in my code.

    The Shortcode Validates and Sanitizes

    The way a shortcode works in WordPress is you can pass attributes to it and the shortcode parses that for you. My shortcode is pretty basic: [on-this-day date="MM-DD"] But since a shortcode lives in the world of ‘users can enter whatever they want’, I have to play Captain Beaver Dam and stop them from using [on-this-day date="Elvis"] for example.

    I do this by sanitizing the attribute and then validating it’s the format I think it is:

    	public function on_this_day_shortcode( $atts = [] ) {
    		$attributes = shortcode_atts([
    			'date' => 'today',
    		], $atts);
    
    		$this_day = sanitize_text_field($attributes['date']);
    		if ( $this_day !== 'today' ) {
    			$month = substr( $this_day, 0, 2);
    			$day = substr( $this_day, 3, 2);
    			$this_day = ( checkdate ( $month, $day , date('Y') ) == true )? $this_day : 'today' ;
    		}
    		$onthisday = $this->on_this_day( $this_day );
    		return $onthisday;
    	}
    

    If you’re thinking that looks the same as what I do in the main widget, you’re right! I do it here to make sure I’m never sending bad data to the function. I do it in the function since I’m not actually psychic and people have done some pretty weird things. Also they may decide to extend my code one day and write something that calls the function.

    The Widget Validates and Sanitizes

    At this point, you’re expecting this, right? It’s actually more important that the widget do this than anything else. The code on my server won’t accept incorrectly formatted data, so the worst that happens is you get a bad data reply. The reason all this matters more for the widget though is that the widget stores data in the database.

    If you’re saving data and you’re not making it safe, you’re putting yourself at risk. Repeat that until it’s second nature.

    In my widget, there’s an update function that saves the data to the database.

    	function update( $new_instance, $old_instance ) {
    		$new_instance['title'] = strip_tags( $new_instance['title'] );
    
    		$new_instance['date'] = substr( $new_instance['date'], 0, 5);
    		$month = substr( $new_instance['date'], 0, 2);
    		$day = substr( $new_instance['date'], 3, 2);
    		if ( checkdate( $month, $day, date("Y") ) == false ) $new_instance['date'] = '';
    		$new_instance['date']  = strip_tags( $new_instance['date'] );
    
    		return $new_instance;
    	}
    

    You’ll notice that, yet again, I use checkdate() to make sure the date is a valid value. But I’m also using strip_tags() to remove any HTML. You could use wp_strip_all_tags() which is even more hardcore, but in this moment, they both work.

    The Data Is Cleaned Three Ways

    In a way, it’s overkill. I don’t need to validate the data in the shortcode really, because I’m doing that in the main function. I do need to validate in the widget, since I want to make sure I’m not saving bad data, and I don’t want people to see invalid data on the widget settings page. At the same time, by making sure everything is as sane and secure and valid, I’m limiting possible vulnerabilities down the road.

  • It Has Been X Days Widget

    It Has Been X Days Widget

    Yesterday I talked about ordering posts by post meta in a weirdly unique situation. Today I’m going to tell you about an even weirder one.

    As I mentioned, what I’m working with is a list of all the dead characters recorded on a site. One of the aspects of this is to allow for us to show a widget that tells visitors how many days it’s been since the last death, and who it was. There are a lot of ways to do this, and one of them is manually updating, but no one likes this. If an event can be automated, everyone wins.

    Since I’d already done the lion’s share of the work, forking it over into a widget and grabbing the last (i.e. most recent) death was easier.

    The Code

    I broke this up into two classes because it’s habit. I have no other reason.

    class Dead {
    	public function __construct() {
    		add_action( 'widgets_init', array( $this, 'register_widget' ) );
    	}
    
    	public function register_widget() {
    		$this->widget = new Dead_Widget();
    		register_widget( $this->widget );
    	}
    
    	public static function dead_queers() {
    		// Get all our dead queers
    		$dead_chars_loop = new WP_Query( array(
    			'post_type'       => 'post_type_characters',
    			'posts_per_page'  => -1,
    			'meta_query'      => array(
    				array(
    					'key'     => 'chars_death_year',
    					'value'   => $thisyear,
    					'compare' => 'REGEXP',
    				),
    			),
    		) );
    		$dead_chars_query = wp_list_pluck( $dead_chars_loop->posts, 'ID' );
    
    		// List all queers and the year they died
    		if ( $dead_chars_loop->have_posts() ) {
    			$death_list_array = array();
    
    			// Loop through characters to build our list
    			foreach( $dead_chars_query as $dead_char ) {
    
    				// Date(s) character died
    				$died_date = get_post_meta( $dead_char, 'lezchars_death_year', true);
    				$died_date_array = array();
    
    				// For each death date, create an item in an array with the unix timestamp
    				foreach ( $died_date as $date ) {
    					$date_parse = date_parse_from_format( 'm/d/Y' , $date);
    					$died_date_array[] = mktime( $date_parse['hour'], $date_parse['minute'], $date_parse['second'], $date_parse['month'], $date_parse['day'], $date_parse['year'] );
    				}
    
    				// Grab the highest date (aka most recent)
    				$died = max( $died_date_array );
    
    				// Get the post slug
    				$post_slug = get_post_field( 'post_name', get_post( $dead_char ) );
    
    				// Add this character to the array
    				$death_list_array[$post_slug] = array(
    					'name' => get_the_title( $dead_char ),
    					'url' => get_the_permalink( $dead_char ),
    					'died' => $died,
    				);
    			}
    
    			// Reorder all the dead to sort by DoD
    			uasort($death_list_array, function($a, $b) {
    				return $a['died'] <=> $b['died'];
    			});
    		}
    
    		// Extract the last death
    		$last_death = array_slice($death_list_array, -1, 1, true);
    		$last_death = array_shift($last_death);
    
    		$diff = abs( time() - $last_death['died'] );
    		$years = floor($diff / (365*60*60*24));
    		$months = floor(($diff - $years * 365*60*60*24) / (30*60*60*24));
    		$days = floor(($diff - $years * 365*60*60*24 - $months*30*60*60*24)/ (60*60*24));
    
    		$days_since = '';
    		if ( $years != 0 ) $days_since  .= $years .' '. _n( 'year, ', 'years, ', $years );
    		if ( $months != 0 ) $days_since .= $months .' '. _n( 'month', 'months', $months );
    		if ( $years != 0 ) $days_since  .= ',';
    		if ( $months != 0 ) $days_since .= ' and ';
    		if ( $days != 0 ) $days_since   .= $days .' '. _n( 'day', 'days', $days );
    
    		$return = 'It has been <strong>'. $days_since .'</strong> since the last death: <a href="'.$last_death['url'].'">'.$last_death['name'].'</a> - '.date('F j, Y', $last_death['died'] );
    
    		echo $return;
    	}
    }
    new Dead();
    
    class Dead_Widget extends WP_Widget {
    
    	protected $defaults;
    
    	function __construct() {
    		$this->defaults = array(
    			'title'		=> 'Bury Your Dead',
    		);
    
    		$widget_ops = array(
    			'classname'   => 'dead-queer deadwidget',
    			'description' => 'Displays days since the last death',
    		);
    
    		$control_ops = array(
    			'id_base' => 'dead-person',
    		);
    
    		parent::__construct( 'dead-person', 'Days Since Last Dead', $widget_ops, $control_ops );
    	}
    
    	function widget( $args, $instance ) {
    		extract( $args );
    		$instance = wp_parse_args( (array) $instance, $this->defaults );
    
    		echo $before_widget;
    
    		if ( ! empty( $instance['title'] ) ) {
    			echo $before_title . apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base ) . $after_title;
    		}
    
    		echo Dead::dead_chars();
    
    		echo $after_widget;
    	}
    
    	function update( $new_instance, $old_instance ) {
    		$new_instance['title']      = strip_tags( $new_instance['title'] );
    		return $new_instance;
    	}
    
    	function form( $instance ) {
    		$instance = wp_parse_args( (array) $instance, $this->defaults );
    		?>
    		<p>
    			<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>">Title:</label>
    			<input type="text" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" value="<?php echo esc_attr( $instance['title'] ); ?>" class="widefat" />
    		</p>
    		<?php
    	}
    }
    

    Which looks like this:

    Bury Your Queers

    An Explanation

    Some things to note.

    While I only allow for customizing the title, I’ve left my arguments as an array in order to let me extend it later. What if, for example, I want to display the featured image as well?

    I’ve also left in some ‘default’ calls to Genesis’ StudioPress theme with regards to the widget layout. That’s because I wrote this for a StudioPress theme.

    The whole reason the death year is an array is that, as I mentioned the other day, people may die multiple times. Insert Buffy singing “Hey I died twice” here. I have to get all the dates, pick the largest number (which is the most recent date, remember Unix timestamps are forever increasing), and save that one. Once I find the ultimate largest number, I know that’s my character to save. Problem is I have to go through them all to figure out who died last.

    I’m inordinately happy about getting to use array_slice() to grab the final item in an array, and ditto array_shift() to move the multidimensional array to a single dimension.

    Using array_shift() changed this:

    Array ( 
        [gina] => Array (
            [name] => Gina 
            [url] => https://example.dev/character/gina/ 
            [died] => 1481068800 
        )
    )
    

    to this:

    Array ( 
        [name] => Gina 
        [url] => https://example.dev/character/gina/ 
        [died] => 1481068800 
    ) 
    

    And getting to finally use _n() in a non-translation sense was fun.

    The reason the dead characters are in a function in the first class is that I plan to extend this code later on, maybe making a shortcode or a JSON API call. Who knows!

    Finally, I mentioned that there were two ways to list the dead characters. One was by a taxonomy of character clichés and the other was by the death date post meta. In this case, I opted to sort by the taxonomy because if someone has a death date but is not marked as dead, then they were dead and came back to life.

  • No More PHP Code (In Widgets)

    No More PHP Code (In Widgets)

    I consider Otto one of my friends. He’s a guy I don’t mind hanging out with in a bar for hours. His code advice (and debugging advice) has furthered my career. He’s also one of the more realistic folks out there when it comes to work/life balance. Enjoy your beers, bro. So you can guess my surprise when, a couple years ago, he lamented to me about his plugin, PHP Code Widget, and how he wished everyone would quit using it. “I use it.” I replied, and earned an Otto-Grumpy Cat glare. “Don’t.”

    25508154Further conversations illuminated the situation. The code works, but it’s not great since people can use it to insert anything PHPish. Sure, in the wrong hands that is hella dangerous. I was about to broadly declare “I’m not the wrong hands!” when I thought back on everything I do, and where I do it, and I sheepishly replied, “I guess I’m just lazy.”

    And that’s the crux. I am lazy, and I looked for the easier way to include a PHP file in my widget areas. I was using it to show ads (the ones you see all over this site) via include("/home/foo/public_html/blah.php");. Why? Because I use the same ads on multiple places. But that’s it for my PHP usage. Which means for me, replacing it with anything else is super easy!

    Shortcodes

    They work in widgets, so hey! I knew I just needed to include a specific PHP file from a specific location, so for me, this was pretty simple. Also it meant I could call a do_shortcode in other places in my theme functions to add it in.

    // My Ads [myads name="name"]
    function myads_func( $atts ) {
            extract( shortcode_atts( array(
                    'name' => 'placeholder',
            ), $atts ) );
    
            $filename = '/home/foo/public_html/ads/'.$name.'.php';
    
            if ( !file_exists($filename) ) { return '<!-- Ad would go here, but you messed up! '.$filename.' not found -->'; }
    
            ob_start();
            include($filename);
            $content = ob_get_clean();
            return '<div id="'.$name.'">'.$content.'</div>';
            }
    
    add_shortcode( 'myads', 'myads_func' );
    

    I put in the little fail check to be hidden, so I would know where to look. This obviously works well for me since I’m pretty limited in how I was using Otto’s code. Before this, though, I was also using it for some BuddyPress sidebar trickery which could not be done (easily) with shortcodes, and really nor should it be, so that brings us to number two…

    Make Your Own Widget

    phpcode-287392Widget code is … weird. It’s not as easy as a function, and it’s way the heck larger than shortcode code, for many things. But you should remember that better or worse is subjective, I know, but for me it wasn’t worth the time to do it. It takes me way longer to master widget code, which I can’t use everywhere (in post content, in footers etc). But Otto’s general advice has been to make a widget.

    It’s also probably way safer than doing an include like I am, but when I started needing the shortcode all over the place, that’s what it was.

  • Sidebar Login Widget

    Sidebar Login Widget

    This comes up a lot.

    The basic concept is you want to allow users to log in and out via a sidebar, and never see the admin-end of WP. There’s an awesome plugin called Sidebar Login that already does this, but I decided to play around and make a dead simple widget. There’s no ajax going on here, and very little code since it all calls the built in functions and filters.

    It lets you make a few choices, as to what verbiage you want to use, and if you want to show login/registration info or not. The registration link won’t work if you have registration turned off, natch.

    All the code is here at Hack: Sidebar Login Widget

    Really those hacks are more ‘mu-plugins’ that I don’t want to support, but still wrote, but you get the idea.

    I’ve taken to writing up the code, when I can, for people in the forums a little more, since it helps me as a developer get better with WordPress. I’m still new at wrangling widgets, so this was a new, and interesting, experience for me. That brought up questions for me, as to where people like the plugin ‘settings’ to be.

    [polldaddy poll=6676462]

  • Mailman Newsletter Widget

    Mailman Newsletter Widget

    I read How to Add a Newsletter Signup Box After Your Posts by Brian Gardner and thought to myself “Self,” I said, “I really would love to be able to add a signup widget for my mailman newsletter.”

    And so I did. The following code is plain HTML. Just drop it into a text widget wherever you want it to show up, and magically it will. If you’re using a Genesis theme, this is your replacement for Step 3.

    &lt;div id=&quot;newsletter&quot;&gt;
        &lt;div class=&quot;white-border&quot;&gt;
            &lt;div class=&quot;newsletter-wrap&quot;&gt;
                &lt;h4&gt;Newsletter&lt;/h4&gt;
                &lt;p&gt;Get my awesome newsletter!&lt;/p&gt;
                &lt;form action=&quot;http://example.com/mailman/subscribe/newsletter_example.com&quot; method=&quot;post&quot; id=&quot;mc-embedded-subscribe-form&quot; name=&quot;mc-embedded-subscribe-form&quot; class=&quot;validate&quot; target=&quot;_blank&quot;&gt;
                &lt;input type=&quot;email&quot; value=&quot;&quot; name=&quot;email&quot; class=&quot;email&quot; id=&quot;mce-EMAIL&quot; placeholder=&quot;Email Address&quot; required&gt;
    			&lt;input name=&quot;pw&quot; type=&quot;password&quot; class=&quot;password&quot; id=&quot;mce-PASSWORD&quot; placeholder=&quot;Enter Password&quot; required&gt;
    			&lt;input name=&quot;pw-conf&quot; type=&quot;password&quot; class=&quot;password&quot; id=&quot;mce-PASSWORD&quot; placeholder=&quot;Confirm Password&quot; required&gt;
    			&lt;input type=&quot;hidden&quot; name=&quot;digest&quot; value=&quot;No&quot;&gt;
                &lt;input type=&quot;submit&quot; value=&quot;Sign Up&quot; name=&quot;subscribe&quot; id=&quot;mc-embedded-subscribe&quot; class=&quot;button&quot;&gt;
                &lt;/form&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    

    One important thing to note here, I wanted everyone to get the emails as they happened, no digest, so I set this: . If you want to make it an option, the down and dirty way is to use this:

    Digest: &lt;select name=digest&gt;
    &lt;option value=1&gt;Yes&lt;/option&gt;
    &lt;option value=0&gt;No&lt;/option&gt;
    &lt;/select&gt;
    

    The rest is pretty much Brian’s CSS, tweaked a little since my size requirements were different. Don’t change the ‘name’ values, as it makes Mailman cry. And how does it look?

    Looks nice, don’t it?