Half-Elf on Tech

Thoughts From a Professional Lesbian

Author: Ipstenu (Mika Epstein)

  • Sharing WordPress Content with any PHP App

    Sharing WordPress Content with any PHP App

    Last week I explained how I shared my WordPress content with Hugo. Now, that was all well and good, but there is an obvious way I can do this when I don’t have a tool that understands dynamic content?

    I’ve got PHP so the answer is “You bet your britches, baby!”

    The First Version

    If you just want to grab the URL and output the post content, you can do this:

    $curl = curl_init();
    curl_setopt_array( $curl, array(
    	CURLOPT_FAILONERROR    => true,
    	CURLOPT_CONNECTTIMEOUT => 30,
    	CURLOPT_TIMEOUT        => 60,
    	CURLOPT_FOLLOWLOCATION => false,
    	CURLOPT_MAXREDIRS      => 3,
    	CURLOPT_SSL_VERIFYPEER => false,
    	CURLOPT_RETURNTRANSFER => true,
    	CURLOPT_URL            => 'https://example.com/wp-json/wp/v2/pages/12345'
    ));
    
    $result = curl_exec( $curln);
    curl_close( $curl );
    
    $obj = json_decode( $result );
    
    echo $obj->content->rendered;
    

    Now this does work, and it works well, but it’s a little basic and it doesn’t really sanitize things. Plus I would be placing this code in multiple files per site (not everyone themes as nicely as WordPress, ladies and gentlemen). So I wanted to write something that was more easily repeatable.

    The Advanced Code

    With that in mind, I whipped up a PHP file that checks and validates the URL, makes sure it’s a wp-json URL, makes sure it’s JSON, and then spits out the post content.

    <?php
    
    /* This code shows the content of a WP post or page.
     *
     * To use, pass the variable ?url=FOO
     *
     */
    
    if (!$_GET || !$_GET["url"]) return;
    
    include_once( "StrictUrlValidator.php" );
    
    $this_url = (string) filter_var( $_GET['url'], FILTER_SANITIZE_URL );
    
    if (strpos( $this_url, 'wp-json') == FALSE ) return;
    
    function do_curl ( $url ) {
    	$curl = curl_init();
    
    	curl_setopt_array( $curl, array(
    		CURLOPT_FAILONERROR    => true,
    		CURLOPT_CONNECTTIMEOUT => 30,
    		CURLOPT_TIMEOUT        => 60,
    		CURLOPT_FOLLOWLOCATION => false,
    		CURLOPT_MAXREDIRS      => 3,
    		CURLOPT_SSL_VERIFYPEER => false,
    		CURLOPT_RETURNTRANSFER => true,
    		CURLOPT_URL            => $url
    	) );
    
    	return curl_exec( $curl );
    	curl_close( $curl );
    }
    
    if ( StrictUrlValidator::validate( $this_url, true, true ) === false ) {
    	$return = "ERROR: Bad URL";
    } else {
    	$obj = json_decode( do_curl ( $this_url ) );
    
    	if ( json_last_error() === JSON_ERROR_NONE ) {
    		$return = $obj->content->rendered;
    	} else {
    		$return = "ERROR: Bad JSON";
    	}
    }
    
    echo $return;
    

    You can see I have some complex and some basic checks in there. The URL validation is done via a PHP Library called StrictUrlValidator. If I was using WordPress, I’d have access to esc_url() and other nifty things, but since I’m running this out in the wild, I make do with what I have.

  • Local: For When You Don’t Need The Kitchen Sink

    Local: For When You Don’t Need The Kitchen Sink

    The app formerly known as Pressmatic was bought by Flywheel and converted to be Local.

    I’d been wanting to try Pressmatic for a while, but had some ethical concerns about paying when I knew the owner wasn’t paying his other employees. At this point, however, the damage has been done in a way I can live with, so I downloaded Local to play with.

    Local Is an App

    The best thing about Local is that it’s an app. Vagrant is awesome, but it’s 100% command line, and while I’m fine with that, not everyone else is. The learning curve for VVV is steep and, given what you can do with it, it’s not a bad thing. I love it but it can be overkill when I want to work on my plugins. Local is an app, it looks like a Mac app and behaves like a Mac app, so it makes it more obvious how I use it and what I do with it.

    Syncing Data

    My biggest issue with VVV (and VVV2) is I can’t sync folders from Vagrant to my desktop. This is a workflow issue and I know it. For me, I have dedicated folders for my plugin development, all saved in ~/Development/wordpress/plugins-git/ (with a folder per plugin obviously). That makes it easy for me to script updates by saying “For every git repository in this folder, do a pull.”

    Since I do my development work on multiple computers, having as much of this automated as possible is important for my sanity, no matter how much Sara Lance tries to destroy it. Every time I log in to my computer, it runs the update which pulls everything down and syncs.

    And all that means, for VVV, I have two options.

    1. checkout a git repository into the plugin locations for my site and edit those, syncing them back up.
    2. manually copy files over every time I update them

    But with Local, I can use the add-on Volumes which lets me map a folder on Local’s virtual machine to my local desktop. Now it’s not perfect, but with this script by Andy Fragen, I was able to set up my site with actual honest to goodness symlinks:

    [gist]https://gist.github.com/afragen/748e4780b6057d4c41cf9e466557042a[/gist]

    Now I just edit my repositories and magically they’re updated on Local. Faster development for the win.

    (My own fork of Andy’s code is available here on Github – I made some changes but not much.)

    Conclusion

    I use both.

    VVV is great. I love it when I’m working on WordPress core, or god help me, WordPress.org itself.

    But when it’s just me developing my own software and testing? I like Local. It’s good.

  • Sharing WordPress Content with Hugo

    Sharing WordPress Content with Hugo

    When your life is just WordPress, there’s not a lot of headaches involved in making some posts and updating widgets and keeping your whole site in sync.

    When your life isn’t just WordPress, it gets a little weird.

    When you want to use WordPress to run your life a little more, you un-weird it by making it weirder.

    Rethinking Where the Content Lives

    Normally we think about content living on its own site. Well, I have a ‘message’ that needs to be the same on five different domains. Just work with me here. The point is, if I want to update the header message on the five sites, I have to update five sites. Yuck.

    Now, there are a lot of solutions to this. I decided I wanted one, and only one, place to update the header message. The most obvious is making a static text file that I could update when needed and import/include it in everywhere. But as I started looking into how I do that, my eyes drifted to WordPress.

    What if I used JSON? What if instead of a file, I made a page on WordPress, grabbed the content from https://example.com/wp-json/wp/v2/pages/12345 and parsed that so I didn’t have to do a whole mess of editing anywhere but on WordPress?

    Hugo

    Guess what. That works. And it works extra well for Hugo (a static site generator I’m fond of) because Hugo understands dynamic content. The one drawback is that it can’t live refresh remote data, so I will always have to push a change to the site to trigger this rebuild.

    However if you ever wondered how to include WordPress’ JSON data into a Hugo theme, here’s what I have in my template for utility-bar.html:

    {{ $wordpressURL  := "https://example.com/wp-json/wp/v2/pages/12345" }}
    {{ $wordpressJSON := getJSON $wordpressURL }}
    
    <div class="utility-bar">
    	<div class="wrap">
    		<section id="text-16" class="widget widget_text">
    			<div class="widget-wrap">
    				<div class="textwidget">
    					{{ $wordpressJSON.content.rendered | safeHTML}}
    				</div>
    			</div>
    		</section>
    	</div>
    </div>
    

    The reason safeHTML is there is that otherwise Hugo wants to escape my HTML. Which is a wise choice! Default to not trusting.

    This outputs the post content and I have a happy (enough) day. The more I look at it, the more I realize how much I can do with WordPress and Hugo, since regenerating the site just takes a push of Hugo content.

  • Genesis Themes: Author Box Shortcode

    Genesis Themes: Author Box Shortcode

    In building out a network of sites, I was struck upon by a feature of multisite I love, a feature of a theme I adore, and an inconvenience of the combination.

    Author Box

    StudioPress’ Genesis themes include a feature called “Author Box” which allows authors to create bios from their profiles and show them at the bottom of posts. When you have multiple authors on a site, this is a great way to make sure everyone gets credit and that they can control it.

    The code to make this show up is included in most (if not all) StudioPress themes, but if you need to add it for your post archives and single posts, it looks like this:

    add_filter( 'get_the_author_genesis_author_box_single', '__return_true' );
    add_filter( 'get_the_author_genesis_author_box_archive', '__return_true' );
    

    Multisite Magic

    Once the code is enabled, and once someone’s written a bio, their author box shows up for all sites on the network. This is great for what I needed, as it meant everyone had control and I could just set it and forget it. The only ‘annoying’ part is it’s the same bio for all sites, so if you have wildly different sites on your network, this may not be right for you.

    This does harken back to my age old comment: WordPress Multisite is for a network of somewhat related sites.

    By this I mean if all the sites on your network are related, let’s say for a school, then it doesn’t matter that everyone’s bio talks about their school work. But if you combine the school with hobbies, then it gets weird to announce that the champion archer has a PhD in neuroscience. Although that is pretty cool.

    Display The Author Box Anywhere

    The other problem with the author box is you can only use it on pages or posts as context. Which is not what I wanted here. So I made it a shortcode.

    function author_box( $atts ) {
    
    	$user = username_exists( sanitize_user( $atts['user'] ) );
    
    	if ( !$user ) return;
    
    	wp_enqueue_style( 'author-box-shortcode', plugins_url( 'author-box.css', __FILE__ ) );
    
    	$authordata    = get_userdata( $user );
    	$gravatar_size = 'genesis_author_box_gravatar_size' ;
    	$gravatar      = get_avatar( get_the_author_meta( 'email', $user ), $gravatar_size );
    	$description   = wpautop( get_the_author_meta( 'description', $user ) );
    	$username      = get_the_author_meta( 'display_name' , $user );
    
    	$author_box    = '
    		<section class="author-box author-box-shortcode">'
    		. $gravatar
    		. '<h4 class="author-box-title"><span itemprop="name">' . $username . '</span></h4>
    		<div class="author-box-content" itemprop="description">'. $description .'</div>
    		</section>
    	';
    
    	return $author_box;
    }
    

    This is obviously skewed towards Genesis themes, but realistically other than the code in $gravatar_size you can use this for any theme anywhere. The benefit of Genesis here is that most, if not all, of the CSS is done for you. The shortcode is [author-box user="ipstenu"] and it dumps out a full width user box of your named author.

    Display Multiple Boxes Instead

    But… What if you wanted a grid? Or a group of IDs? Taking advantage of the fact that Genesis comes with columns, the code looks like this:

    function author_box( $atts ) {
    
    	if ( $atts['users'] == '' ) return;
    
    	wp_enqueue_style( 'author-box-shortcode', '/wp-content/mu-plugins/css/author-box.css' );
    
    	$users = explode(',', $atts['users'] );
    	$user_count = count( $users );
    
    	$columns = 'one-half';
    	if ( $user_count == 1 ) $columns = '';
    	if ( $user_count % 3 == 0 ) $columns = 'one-third';
    
    	$author_box = '<div class="author-box-shortcode">';
    
    	foreach( $users as $user ) {
    		$user = username_exists( sanitize_user( $user ) );
    		if ( $user ) {
    			$authordata    = get_userdata( $user );
    			$gravatar_size = 'genesis_author_box_gravatar_size' ;
    			$gravatar      = get_avatar( get_the_author_meta( 'email', $user ), $gravatar_size );
    			$description   = wpautop( get_the_author_meta( 'description', $user ) );
    			$username      = get_the_author_meta( 'display_name' , $user );
    
    			$author_box   .= '
    				<section class="author-box '. $columns .'">'
    				. $gravatar
    				. '<h4 class="author-box-title"><span itemprop="name">' . $username . '</span></h4>
    				<div class="author-box-content" itemprop="description">'. $description .'</div>
    				</section>
    			';
    		}
    	}
    
    	$author_box .= '</div>';
    
    	return $author_box;
    }
    

    The shortcode here is [author-box users="ipstenu, liljimmi"] and that puts out a column of either fullwidth, half, or a third. The default is a half, and if there’s only one item, it goes to full width, but I only put in a 1/3rd check because I didn’t feel the need to cover everything. If you want to nick the CSS, StudioPress put it up online, and you can extend it as you want.

  • The New SEO Scam

    The New SEO Scam

    The email looked innocuous.

    I was just browsing Ipstenu.Org and saw in this post (link from 2002) you were interested in tech, and so I thought you might also be interested in linking to a resource we put together on the ways technology is improving health.

    It went on to tell me about how they were comprehensive, up to date, etc etc. I admit, I wondered how they got my email in the first place, since it’s not listed on my site on purpose. But barring anything nefarious, I assumed they guessed, and since it wasn’t important to me that a personal blog post from 2002 get updated, I deleted the email.

    Every five days after then I got a ‘follow up’ email from this person, Camilla Hathaway, and it was strange. I didn’t reply so why would they keep doing that?

    But then I got an email from another company about a different post, asking me if I wanted to link to their article about ddos protection. And another from a third company for a post about cPanel.

    They all sent follow up emails and they all were very ‘nice’ about it, praising my writing and telling me about broken links.

    Spam by any other name…

    If the email was about running a banner ad on CNN for $725, you’d know it was spam.

    If it was from the FBI telling you the corrupt government owed you millions, you’d know it was spam.

    This appeared to be from a real person, a real reader. Except for the fact that there was no way they should have been able to find that particular email address. Except for the fact that they kept email. Except for the fact that who the heck reads old posts on a personal site from as far back as 2001 (I’ve been blogging a long time) and tell me that a link is broken or the information is out of date.

    It’s weird, isn’t it?

    Well, it’s spam.

    The New Spam Game

    The old SEO spam was a lot more overt.

    We are a Leading SEO & Web Development Company and one of the very few companies which offer organic SEO Services with a full range of supporting services such as one way themed text links, blog submissions, directory submissions, article writing and postings, etc.

    or

    I was doing some research on [Subject] and landed on your website.

    You know the obvious ones. These new ones are more clever. They sound more like people. And the worst part is they aren’t all fake people.

    You see … A real company, a legit company, run by real people in the UK, spammed the hell out of me with offers like this. Every day for almost two weeks before I blocked the accounts. This was after I pinged them on Twitter and asked them to leave me alone.

    I shouldn’t have to.

    If I don’t reply, I’m probably not going to. But I surely am not going to reply within a day if you email me daily. The new spam game, the new scam game is to be nice and hammer you with a request over and over and over.

    If It Looks Too Good To Be True, It Is

    The bottom line is that if it looks too good to be true, it is. No probably about it.

  • The Grammar of URLs in Email

    The Grammar of URLs in Email

    At many points in time I’ve complained that if you have a URL in your email, don’t use a period at the end of it because that can break links. That led to me being asked what the proper usage of URLs with punctuation actually is.

    This is not the law but it’s the rules I’ve come up with to ensure readability, linkability, and sanity when emailing links.

    Assume no HTML

    It would be easier if I just said “Visit example.com” and it was a link. You’d know what to do. You click on a link. The problem is not all email clients are HTML friendly. I’m aware it’s 2017. The fact is, the world is not as advanced globally as you might wish. Thus we have to assume that we will be emailing someone who cannot see HTML.

    Arguably that means they’d see <a href="http://example.com">example.com</a> and that may be okay to some of you. It’s not to me, since I aim for the lowest common denominator, and I know that modern email clients will convert http://example.com to a link for me. Therefore the correct solution is to send only the URL, without HTML surrounding it.

    Style Manuals

    The Chicago Manual of Style, which has been updated a few times, suggests you format footnotes with URLs as follows:

    • Fiona Morgan, “Banning the Bullies,” Salon, 15 March 2001, http://www.salon.com/news/feature/2001/03/15/bullying/index.html (accessed 24 Feb. 2003).

    Now in their example, there’s a space before the accessed date, so it’s easy to prevent errant trailing characters, but also they have a space before the link to make sure there’s no mistake there as well. The lesson to take away from this is that your URLs shouldn’t be marred with punctuation.

    Punctuation

    I believe the correct use of a URL is to never prepend or append punctuation. Or in other words: Do not end your sentence wth a URL.

    • Good: Please visit http://example.com/
    • Bad: Please visit http://example.com/.

    That trailing period? That’s bad. That will break on a lot of mail readers. But back to my gleanings from the manual of style, the correct usage is with a space on either side. In order to force that we can just remove the period but then do we use a capital letter for the next sentence?

    The Best We Can Do

    Grammar means we put words in a specific order to have a specific meaning. The same holds true for using URLs in our content. We must be aware of their context and placement.

    Some good examples:

    Please visit our site at http://example.com for more information.

    Or

    If you look at their website – http://example.com – you can see the magnitude of their errors.

    In both of those cases, we’ve put the URL in the middle of the sentence either prefacing it with an ‘at’ or using hyphens to indicate the URL is something special. The second way highlights the URL more in a text-only environment.

    Alright, what if you want to tell someone to download a link?

    Lorem ipsum blah blah blah
    Download the code here: http://example.com

    Notice how I put the download link on it’s own line? That breaks it out visually as well as generating a call to action. Download the code here.