Half-Elf on Tech

Thoughts From a Professional Lesbian

Category: How To

  • Presentations Are Not Transcripts

    Presentations Are Not Transcripts

    After my review of SEO Slides (they’re pie, not cake), someone remarked to me that my slide decks are useless because I don’t put all the information on the slides. They pointed out, correctly, that my slides are image heavy with, at most, a couple lines of text (with one notable exception: WordCamp Chicago 2012), except my ‘Who am I?’ slide. I told them “Yes, this is true.” and then Tweeted about it.

    SlidesMy belief (and this is shared by a lot of people) is that slides should accent and relate to my talk. If you just need to read the slides to get all the information, why am I there? Coming to a live presentation to just watch someone reading off slides seems counter intuitive to me. Heck. I could go pester my coworker and get the studies showing that when you have a slide with a lot of text, people read the text, then look at you. That means that they aren’t listening until they read, and if, when they’re done, they come to find you’re just reading what they read? They’re probably bored.

    When I give a presentation, it’s usually on a topic I’ve written about before. Actually, that’s generally how I decide what I want to talk about! I picked “Don’t use WordPress Multisite” for WordCamp SF 2013 because it is still, to this day, the most popular post on my site. And it’s my most popular presentation. Some may ask “Why would you give a presentation on something I could read?”

    BoredPeople learn in different ways. I, personally, suck at learning from videos. However I learn well from presentations in person, where someone talks to the room, pays attention to our energy, and teaches, using the slides as an emphasis. I also learn well from a written post. Finally, I learn best by doing things. So for me, if I’m blowing up a site, it means I’m learning in a speed unparalleled. There’s a converse to this, and if I hit a blocker were I can’t do something, I get really upset.

    What does this have to do with slides and why mine are mostly pretty pictures with a sentence for emphasis? I don’t write my slides to be a transcript because I’m going to write a much longer blog post on the topic, with the same pictures probably, if I haven’t already. So for the person who wants to read the content, I’ll have you covered. And for the person who wants to be inspired by looking at slides? Well I have that. Finally for the person who wants to watch a video, WordCamp does that for me, thankfully.

    This is not a perfect system, of course. Like I said, people learn in different ways, so there’s probably someone out there who loves slideshows of text on pictures who is grumpy. They probably also like infographics. Which I don’t. You see a trend here? I don’t like getting my information from pictures. Because of that I suck at writing them, so I just don’t.

  • Extending Chrome

    Extending Chrome

    I had a problem.

    If you know me at all, you know this is how 99% of my code lessons start. I have a problem I can’t solve easily, so I dig into it and write about it. In this case, my problem was one of those really stupid, messy things where I needed to force some code to run on certain pages and I didn’t have any access to those pages. Why? Well … let’s just say I wanted to make something like the Hey Girl chrome extension okay? Yes, I need an extension for Google Chrome.

    It doesn’t really matter what you’re making, what matters is how easy it is to make a Chrome Extension. It really only needs two files, a manifest.json and then your content files. Let me explain by making Google Red.

    Manifest

    The manifest is where you list what’s in the Extension. It’s going to have the name, summary, what files to call, when and where. A really simple example would be like this:

    {
    "name": "Half-Elf's Chrome Extension",
    "description": "This is an example extension that doesn't do anything useful at all.",
    "version": "1.0",
    "manifest_version": 2,
    "content_scripts": [
        {
          "matches": [
                     "http://www.google.com/*",
                     "https://www.google.com/*"
                     ],
          "css": ["mystyle.css"],
          "js": ["myscript.js"]
        }
      ]
    }
    

    This is pretty straight forward. Name, description, etc. The value for manifest_version has to be the number 2, no quotes, no matter what. This is mandated by Google. They don’t use it yet, but they will. Then we get to the fun stuff, content_scripts, which is also pretty obvious. Using “matches” says “Only run this when the URL matches…” This is an inclusive match. For a different example, here’s an extension I want to run on every website, but not when I’m in wp-admin:

    "content_scripts": [ {
    	"matches": [ "http://*/*", "https://*/*" ],
    	"exclude_globs": [ "http://*/wp-admin/*"],
    	"css": [ "mystyle.css" ],
    	"js": [ "myscript.js" ]
    } ]
    

    You can take this even further and only have it take action at specific actions (like when you make a tab active). The content_scripts documentation is pretty overwhelming, but it has all sorts of options. Today, though, saying we want to match just Google’s pages is okay.

    In order to do this, I made a blank file called manifest.json and saved it in my ‘Development’ folder called~/Development/Chrome/extensions/GoogleRed but this doesn’t actually matter. Save it anywhere on your computer. Just know where it is. I also put blank files for mystyle.css and mystyle.js in there because they’re my content files! They’re blank, we’ll get to the real code soon.

    Content Files

    This is the fun part. The content files are what makes the magic happen and the super cool thing? It’s all basic JS or CSS or HTML. Seriously. Anything you can do in those languages, you can do in an Extesion and call it a day. For example, here’s one I used to strip referrers from URLs on Facebook (I was experimenting):

    document.body.innerHTML = document.body.innerHTML.replace(new RegExp('?fb_source=pubv1" target="', 'g'), '" target="');
    

    I set this to only run on Facebook.com and it was exactly what I needed. I admit, there are better JS ways to do this, but I suck at JS still, so this was as far as I got.

    The Example

    Google actually already made the files, so you can download them from Chromium “Make Page Red” and toss them in your folder. My files are as follows:

    mainfest.json

    {
    	"name": "Page Redder",
    	"description": "Make the current page red",
    	"version": "2.0",
    	"permissions": [
    		"activeTab"
    	],
    	"background": {
    		"scripts": ["myscript.js"],
    	    "persistent": false
    	},
    	"browser_action": {
    	    "default_title": "Make this page red"
    	},
    	"manifest_version": 2
    }
    

    myscript.js

    // Copyright (c) 2011 The Chromium Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    	
    // Called when the user clicks on the browser action.
    chrome.browserAction.onClicked.addListener(function(tab) {
      // No tabs or host permissions needed!
      console.log('Turning ' + tab.url + ' red!');
      chrome.tabs.executeScript({
      code: 'document.body.style.backgroundColor="red"'
     });
    });
    

    That’s it. Two files. But how do I get it installed?

    Adding it to Google Chrome

    Go to chrome://extensions/ in Chrome and you get this kind of view:

    chrome-extensions

    See the box on the upper right? Developer Mode? Check it. Now you get extra information!

    chrome-extensions-developer

    What interests us the most here is “Load unpacked extension…” but also that ID code jcpmmhaffdebnmkjelaohgjmndeongip is really important. This tells me where the extension lives on my computer, because I can go to ~/Library/Application Support/Google/Chrome/Profile/Extensions/jcpmmhaffdebnmkjelaohgjmndeongip and see all the code for the other extensions. This makes it super easy for me to fork them. Since this is all just for me, and I have no plans to release them, I’m less worried about licenses than I might be, but I still am in the habit of checking what they are and so should you. Your folder locations will vary depending on your OS, and how many Google Profiles you have (I actually have 3 – me, work, fansite).

    Click that “Load unpacked extension…” and select the folder:

    upload

    Once you upload it, you get this:

    sample-ext

    Now you know where the file is, but also you get a special link for Reload (⌘R) which is a life saver. Click that and any changes you made to your source files are re-installed into Chrome! This is great because Google’s example of making pages redder? Doesn’t work.

    Okay, that’s not fair, it does work, just not on every page. It didn’t work on https pages for me until I edited the manifest and changed permissions from "activeTab" to "activeTab", "tabs", "http://*/*", "https://*/*" and then clicking on the magic button worked:

    click-icon

    It’s kind of nice to be able to bully things around that way.

    I’m just dabbling my toes into the Extensions water, and I find it highly powerful to push Chrome into my paths right now. Have you written any extensions?

  • Command Line Cleaning WP

    Command Line Cleaning WP

    Blob-Town-The-Blob-1958-Documentary-@-Phoenixville-Pennsylvania-by-James-RolfeI’m a huge fan of the scorched earth clean up for WordPress. By which I mean when I clean up WP, I rip it out, scrub it, and reinstall. This scares the heck out of people sometimes, and if you’re doing it in a GUI, yeah, it can be sucky and time consuming. Me? I do it in 5-10 minutes, depending on if my cat wants to be petted.

    I’ve been asked ‘How do you do it that fast?’ so here are my steps for cleaning up WP, with the following assumptions:

    1. I’m working in the folder where WP is installed
    2. wp-config.php is in this folder
    3. WP is in ‘root’ (i.e. I’m not giving WP it’s own folder)

    If any of those aren’t true for you, adjust the folder locations in the commands:

    Download WP: wget -P ../ http://wordpress.org/latest.zip

    Unzip it: unzip -qq -d ../ ../latest.zip

    Backup DB: wp db export

    Pause. Here I’m using WP CLI, which makes my life way easier. If you’re not, you’ll need something like this: mysqldump --opt --user=username --password=password --host=yourMySQLHostname dbname > domain_com.sql

    Zip up the files I want to backup: zip -r ../domain.zip *.sql wp-config.php .htaccess wp-content/

    Set glob. Glob is scary, I know, but read about glob before you dismiss it (if you’re on korn, you can usually skip this): shopt -s extglob

    Delete files: rm -rf !(wp-config.php|wp-content)

    Pause. At this point, It’s probably wise to consider that my hack may be in my theme and/or plugin. If so, I want to nuke them and JUST keep my uploaded files, so I use this instead…

    Delete files: rm -rf !(wp-config.php|wp-content) wp-content/!(uploads|blogs.dir)

    Pause again. No matter what, want to scan for evil files, but this way I do it over a much smaller group of files. Either way, though, I do want to scan the folder for evil, because leaving behind hacks in themes and plugins is really common. Also it’s a good idea to delete every plugin you don’t use, and theme as well. Since you really can’t delete all themes but one on a Multisite, this gets harder. Generally I don’t delete the themes automatically, but instead go in and nuke them one at a time, so I run this…

    Delete files: rm -rf !(wp-config.php|wp-content) wp-content/!(uploads|blogs.dir|themes|mu-plugins)

    Now we can move on, knowing our personal files are clean.

    Copy it back: cp -r ../wordpress/* .

    Clean it up: rm -rf ../wordpress ../latest.zip

    And now you’re done! When you want to reinstall plugins and themes, I do via wp-cli because it’s faster: wp plugin install NAME and wp theme install NAME

    Then I activate as needed and I’m off to the races. If I deleted my mu-plugins, I copy those back from my backup zip, one at a time, checking each file for hacks.

    The best thing about this is you can apply the logic to any CMS out there. Just know what you have to delete and keep. The downside? It doesn’t touch your database. Rarely is this an issue for me, except in the case of the Pharma hack. I’ve not had a DB infected yet.

    Do you have a solid methodology for cleaning it up?

  • Email PopUp

    Email PopUp

    Edited to note: The popup is NOT on this site. You’re not missing anything.

    Blame Chris Lema. Not for “making” me do anything, but for making a good article that was insightful and inspiring. But then again, he’s good at that.

    When I read the article he wrote about Growing your Email List, I was interested. While I don’t have the same needs he does, I do want to pull in more subscribers to one site. Look, in general, I don’t worry about who’s following me on my personal sites. However I do have a site, one site, where I do care about the followers, especially since I broke my old mailing list a while back.

    Revisiting My Options

    The old mailing list worked, but no one could sign up any more, and it was becoming a hassle. It was finally time to move off the old and embrace some new. But what new? I’m a huge proponent of self hosting, but to be frank, I hated managing email server stuff and Mailman is both long in the tooth and not exactly user friendly for the non-technical people. It was time to accept that this was not my forte, and you know what? I didn’t want to learn it. Since 100% of the news on that site was pushed via WordPress, it was logical to use something I already had: Jetpack. It had subscriptions, done.

    “I broke it.”

    That was in the body of the email I sent.

    I emailed everyone on that old list and explained the situation. I apologized for my screwup, and explained how they could re-subscribe if they wanted. Within 12 hours, 50% of them did. That was good enough for me, and while I’m watching more people trickle in, I think it’s going to top out around a 70% retention rate. That’s not bad at all. A handful of people emailed me back laughing (literally ‘LOL’ was the entirety of more than five replies) and saying it was okay, thank you for letting them know. I was heartfelt, I was honest, and I was deprecating.

    I’ve always had a link to my email list in my sidebar as an alias, domain.com/updates linked to domain.com/mailman/list/updates, so changing that to an actual page all it’s own with a subscription form was crazy simple. New page, delete redirect, done.

    Back to Chris though. See, the most enlightening thing I gleaned from his email was the horrifying fact that pop-ups work. He didn’t give me stats or anything, but I believe him. When I read that, I believed him enough that I went and read other articles about those ‘non-annoying’ pop-ups. Chris and I are vastly different people, passionate about different things, and obsessive about others. But we share a talent for writing, telling stories, and engaging. We also share a hate of pop-ups. His is certainly not annoying.

    Actually I’ve barely noticed his, except to go “Oh, there was a pop-up to get him in my email.” No, I didn’t sign up, I like getting Chris in my RSS box.

    jquery

    This was the hardest part so let’s tackle it first. All I have to do is show it in a pop-up. Except it’s not really a pop-up, it’s a sliding tab. Sure, I could use anything I wanted, but like Chris, I hate pop ups.

    This part stumped me hard. Chris got it easy by having a third-party hand him the code. I, like many people, have an email list and want to use that. But I’m still very much a rookie when it comes to jquery, so when I ran into this information about how to code a wordpress.com follow button and Follow me button on WordPress with MailChimp, I did a little dance.

    The jquery actually comes straight from WordPress.com (I viewed a lot of source to reverse this one):

    jQuery.extend(jQuery.easing,
    {
     easeOutCubic: function (x, t, b, c, d) {
      return c*((t=t/d-1)*t*t + 1) + b;
     }
    });
    jQuery(document).ready(function($) {
    	var isopen = false,
    	    bitHeight = $('#bitsubscribe').height(),
    		$bit = $('#bit');
    	setTimeout(function () {
    		$bit.animate({
    				bottom: '-' + bitHeight - 10 + 'px'
    			}, 200);
    		if ( document.location.href.indexOf('subscribe=') > -1 ) {
    			open();
    		}
    	}, 300);
    	var open = function() {
    		if (isopen) return;
    		isopen = true;
    		$('a.bsub', $bit).addClass('open');
    		$('#bitsubscribe', $bit).addClass('open')
    		$bit.stop();
    		$bit.animate({
    			bottom: '0px'
    		   },{duration:400, easing:"easeOutCubic"});
    	}
    	var close = function() {
    		if (!isopen) return;
    		isopen = false;
    		$bit.stop();
    		$bit.animate({
    			bottom: '-' + bitHeight - 10 + 'px'
    		}, 200, function() { 
    			$('a.bsub', $bit).removeClass('open');
    			$('#bitsubscribe', $bit).removeClass('open');
    		});
    	}
    	$('a.bsub', $bit).click(function () {
    		if ( !isopen )
    			open();
    		else
    			close();
    	});
    	var target = $bit.has('form').length? $bit : $(document);
    	target.keyup(function(e) {
    		if (27 == e.keyCode) close();
    	});
    	
    	$( '#loggedout-follow' ).submit( function() {
    		email = $( '#loggedout-follow-field' ).val();
    		if ( '' === email || !email.match( /^.*@.*\..*$/ ) ) {
    			var error = LoggedOutFollow.invalid_email;
    			$( '#loggedout-follow-error' ).text( error ).css( 'opacity', 1 ).fadeIn( 'slow' );
    			$( '#loggedout-follow-field' ).focus( function() { $('#loggedout-follow-error').fadeOut(); } );
    			return false;
    		}
    		return true;
    	});
    });;
    

    If I enqueued that in my theme, then all I’d have to do is call this somehow in my theme:

    <div id="bit" class=""><a class="bsub" href="javascript:void(0)"><span id="bsub-text">TITLE</span></a><div id="bitsubscribe">CONTENT</div></div>
    

    The jquery would automatically handle placement and everything, so I struck upon the dead-simple solution.

    Widget

    Since I only need to use this on a WordPress site, I put it in a widget, and slapped some CSS around it to make it sexier. The very simple (Genesis skewed) widget is as follows:

    //* Register side-up-bit area
    genesis_register_sidebar( array(
    	'id'            => 'slide-up-bit',
    	'name'          => __( 'Slide Up Bit', 'mygenesis' ),
    	'description'   => __( 'This is a widget area that slides up.', 'mygenesis' ),
    ) );
    
    //* Hook after post widget area after post content
    add_action( 'genesis_after_footer', 'my_slide_up_bit' );
    
    function my_slide_up_bit() {
        genesis_widget_area( 'slide-up-bit', array(
            'before' => '<div id="bit"><a class="bsub" href="javascript:void(0)"><span id="bsub-text">Follow SITE</span></a><div id="bitsubscribe">',
            'after' => '</div></div>',
    	) );
    }
    

    Yes, I hard coded in the “Follow Site” title bit. Couldn’t figure out how not to, since I needed the link in the title. If this was a normal widget, I’d use the ‘before_title’ and ‘after_title’ trick, and while that’s supposed to work with Genesis too, I hit a wall and was in a time-crunch. That said, afterwards, all I had to do was drop Jetpack subscribe widget into the widget area, and the jquery code went into a function to show in my footer. Done. Time for the elf to study up on jquery, though, as I still don’t understand it all.

    Bonus note:

    if (document.location.href.indexOf('subscribe=') !== -1) 
    

    That little bit of code says “If someone has subscribed, make sure the popup is up when they visit.” It shows them that they have subscribed successfully, which is great, but I quickly realized I could tweak that to pop up when someone visited my site from, say, Facebook or RSS links:

    if ( (document.location.href.indexOf('subscribe=') > -1) || (document.location.href.indexOf('fb_source=') > -1) || (document.location.href.indexOf('utm_source=rss') > -1) ) 
    

    The RSS folks are pretty small, and I doubt they care, but the FaceBook people were absolutely delighted. I want to stress that I was doing this specifically because I have very non-technical people, and even having a link for email updates was beyond them. But having this slide-up is non-offensive and apparently much needed. My email subscribers tripled within a week.

  • Torrenting

    Torrenting

    I torrent.

    Look. There are hundreds of legit reasons to do this. I’m not going to argue if it’s right or wrong, because we all know that taking something that people charge for, without paying, is called stealing. Let’s put that aside. I’ve got a torrent I want to download, because downloading this way will be faster than a normal FTP of a 1G file.

    The first thing you need is a Torrent client. I like uTorrent personally, as it’s small and easy for me to use.

    The next thing you need is a torrent file. Let’s say I want to get the DRM free music from SXSW. I could download them all one at a time, or I could use a torrent from http://www.sxswtorrent.com/

    2005-sxsw

    That’s 2.6 gigs of music. And you’d think this would take a long time to download. You’d be wrong. What you download is “SXSW_2005_Showcasing_Artists-Release_1.torrent” and open that tiny file in your client.

    uTorrent lets me select what files I want to download (all of them today):

    uTorrent

    I click okay, and it starts to download. This is where it gets fun. The more people who are sharing this file, the faster my download goes.

    utorrent-dowloading

    Here it is, trucking along at 1M a second and it’ll download in about 45 minutes. Just enough time for me to take a shower.

    The magic in the torrent sauce is that instead of FTP where it’s one person asking one server for one file, this is a million people asking a million people for a file, as bits, not a file. So I can get one snippet of a file from Bob in Minnesota, and another from Arman in Atlanta. The more of us who download, the more of us who are sharing what we just downloaded!

    But torrenting is really my favorite way to share large files to the masses. And yes, when I’m done downloading, I leave it up to ‘seed.’

    I am aware, by the way, that as of 2010, SXSW was a little iffy about distribution via torrent, but it’s been three years and no one’s shut the site down. They’re all well aware of it so if no one’s been slapped with a C&D by now, they’re all okay. Anyway, all these songs are available to download off SXSW’s official site. The issue was with the distribution, which I can understand. I try really hard to only download legal files from legit sources, because I appreciate the value of the work that went into what I’m enjoying.

    Now if you’ll excuse me, I have some new music to listen to!

  • 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.