Half-Elf on Tech

Thoughts From a Professional Lesbian

Author: Ipstenu (Mika Epstein)

  • Mapping Domains Without a Plugin

    Mapping Domains Without a Plugin

    Recently I wrote WordPress Murder Mystery. The day I released it, I got on a plane to fly to Miami, and proceeded to have a pretty awful travel day, thanks to a storm that pretty much knocked travel to the SouthEast out of commission. While I waited for my flight to DFW, I got pinged. “Hey, I can’t add anything from your store to my cart!”

    I pulled up my laptop and thought about what I’d changed. Oh. I’d turned off domain mapping. But not really.

    Ghost Cookies

    You see, I love the WordPress Domain Mapping plugin. But I’m not using it here. No, I’m actually doing something I expressly and patently tell people not to do … because time has changed with 3.9 and it’s almost okay to do things this way. The change here is that WordPress is smarter now, and it’s safer, and you actually can just edit the home and site URLs in the Network Dashboard. But you know how I said ‘almost’ back there? Your ability to shoot yourself in the foot is directly proportional to how smart you think you are.

    What I did? It’s basic but assumes you already know how to add a domain onto another.

    1. Go to Network Admin -> Sites
    2. Edit the site in question
    3. Change the URL to the mapped domain, check the ‘change home and site URL’ box, and click update

    That was it! Three steps and everything still worked! This also let me force change a site to https all the way, and since I didn’t have content, I didn’t bother with a search/replace. If I had, I’d use that Interconnectit Script or WP-CLI for it. Still, like a wise person, I always get the domain ‘right’ before I add content.

    And at this point, everything seemed to work just fine! And so I left it as is and published my book. And as you know, it all failed, spectacularly. I rolled everything back (because I knew what I’d changed last, see? always remember that!) and it worked again, so I knew I had to have missed something big here. Since I was stuck in an airport with choppy wifi, I disconnected everything and fired up my localhost version of my site. Banging on that for a while, I saw I missed a small, but hugely important factor in the plugin.

    See most of what it does is that pretty interface to say “Pull domain.foo content from site (aka foo.example.com)” and force redirects. It also lets you do cool things like “Allow users to log in from foo.example.com instead of domain.foo” but really I didn’t need any of that. The meat of the code is in the sunrise.php file, which when I studied, I realized was just doing the redirects “Send Site to domain.foo” and for me, by renaming the home and siteURLs, I was already doing that.

    So what had I missed?

    define( 'COOKIE_DOMAIN', $_SERVER[ 'HTTP_HOST' ] );

    That was it. I forgot to tell it “Cookies belong to the domain you’re on.” What this means is that if you log in at example.com, the cookie you get is for example.com and not domain.foo! For the most part, this isn’t a problem since no one logs in but me … until you try to make a purchase and it validates a cookie which doesn’t match the domain. I added that to my wp-config.php (down at the end of my Multisite section, where the SUNRISE define had been earlier) and everything magically worked.

    Two lessons! First, test everything. Second, you can map domains without a plugin, safely.

    I will note that, over time, it’s possible those settings for home and site URL may vanish from display. They’re powerful and dangerous settings, and you should not mess with them without a good backup.

  • Defaults Matter

    Defaults Matter

    We tout decicions and not options. We laud people who make it work, simply and efficiently. So how do we decide what the default decisions should be?

    I was looking at a new plugin for an affiliate program, and it set up the links based on region. The way this plugin worked, if you don’t have an account specifically for that country, the plugin author had it set to use his ID if the user didn’t enter one. This is a small issue, in that the user may, unwittingly, be letting the dev earn money of their site. It’s also just not permitted. You don’t put up links or ads on someone else’s site without them checking a box to allow it, it’s gauche and against the .ORG guidelines.

    Timer settingsWithout knowing the issues of the affiliate app, I pushed back, and was surprised that the plugin dev was doing this because if he didn’t, the code would break. The affiliate program didn’t have a fallback to show a default location if you didn’t have an ID for that area, it just errored out. Thankfully, this developer and I worked out a solution. If there was no ID for the region, the plugin wouldn’t display the affiliate links. There was also a checkbox “Use the developer’s affiliate code in regions where you don’t have one!” that explained this would help feed and clothe the dev.

    Another example. A plugin made a shortcode to play MP3s, and by default if you don’t have an MP3, it played one of him from his domain. Besides the fact that you’ll crash your server if you get popular enough, the fallback for that should be for the shortcode to output “Oh noes! No MP3 picked!”

    This got me thinking about how we determine the defaults for anything. We pick what we want to support most, we guess at what people will use most, and we test what we can think of. But that isn’t easy at all. Start factoring in upgrades. You add a new feature and you want people to use it, so you turn it on to aid discovery (Jetpack does this). Not everyone likes that and gets mad that the first thing they have to do is disable it! So you think about alert boxes “Hey, you upgraded and there’s a new thing!” but then people hate dismissing those alerts.

    Picking the right defaults for your intended audience matters. The affiliate code guy knew about an error if he didn’t do that, and compensated in a way that would be seamless for the users, but a little unethical (I felt) because it took advantage of their ignorance to make money for him. I’m kind of hip on educating the masses. The shortcode guy just didn’t think about the long-term ramifications. Neither actually meant to be harmful, but both were thinking about their users and their familiarity with things. Both were cognizant of the fact that a product not working because of incomplete settings should not break.

    Firetruck SettingsDetermining your default settings is a race between education and simplification. It should be as simple and straightforward as possible to make things easier for the new users. At the same time, it should be made totally obvious in straightforward ways what the defaults can be changed to. This can be done with help screens in your plugins, but also in the welcome pages and the in-line explanations of setup. You can hide aspects of the code from users until they’ve finished pre-requisites.

    Defaults are the one place where you have to actually try to know what the users will be thinking, so don’t worry if you get it wrong. You can always iterate.

  • Mailbag! .htaccess questions

    Mailbag! .htaccess questions

    New thing! So many people email me for tech support, which I’m pretty clear on how you’re not going to get it. But Ken (the web mechanic) asked some pretty basic questions, and I’ve decided to answer some of them.

    A loopIn public. Lucky you, Ken. Don’t worry! These were good questions. See, one of the (many) reasons I love WordPress and the support forums is that the answers are public so everyone can see what the question was and how it was answered! This is hugely important to foster a community, so that’s why I’m going to answer this in public, with your personal information removed, of course.

    Ken’s basic concern is that .htaccess is confusing, and is there a preferred order? The answer is yes. The basic idea is that .htaccess rules are a top-down process. The server reads the file from the top on down, in order, 1-2-3. For this reason alone, it’s obvious why you don’t want a super long .htaccess file: more to read takes longer!

    The WP permalink area… Should that always be dead last?

    Yes! WordPress rules always go last. Remember what I said about top-down? If you were to put WordPress at the top, you would load WordPress, process it, and then do the rest of the rules. Which once you say that, it’s pretty obvious eh?

    Deny IP addresses/ referrers. To me it would make sense for them to be at the beginning… Would that be true?

    True! The access controls (including IP blocks) first. Redirects go next, starting from most specific (about-me to about) first to general last. Then your Rewrite rules.

    Compression/Caching/mod_expires… I haven’t a clue where they most appropriately go. Securing wp-config, htaccess itself, other files, etc. … Before? After? the WP permalink block.

    I put them before my re-writes. Since I use a deny to secure .svn type files, it’s an access control so it goes first.

    So how does this work? Here’s a practical example. You want to do the following: remove www from your domains, protect your wp-config file, protect your comments and login from direct attacks, redirect some old pages from before you were WordPress, redirect your old permalink formats, and gzip/compress things. Oh and run WordPress!

    The order would go like this:

    1. Access Control: This is the part where we’re protecting specific files, but also blocking IPs. Basically it’s ‘Security First.’
    2. Remove WWW: We want to make sure everyone’s redirected to the non-www page. If you’re redirecting specific domains (like I send tech.ipstenu.org to halfelf.org), you do it here as well.
    3. GZIP: I do my compression here, though it woudl work just as well swapped out with the next one.
    4. Redirect: Here we’re talking one-off redirects like sending ‘about-me’ to ‘about’.
    5. Rewrite: The ReWrite rules are the ones where you say “Send http://example.com/2014/01/10/postname/ to http://example.com/postname/” with those rules with regex.
    6. WordPress Rules: Last. Always always last.

    And that is .htaccess!

    If you want a look at how my .htaccess is structured, see My super-secret .htaccess file, which hasn’t changed much since 2013. I do a couple things out of order, but they’re minor enough. As long as I can limit any recursive loops with the .htaccess checks, I’m doing good.

  • New Smilies?

    New Smilies?

    While the arguments for and against are pretty hot and heavy (see the trac ticket for more), it’s no secret I’m pro-new smilies. The old ones are too small, too pixelated, and too (pardon the phrase) web 1.0. They look old and out of date on my site.

    Here they are at normal size:

    Old Smilies, normal sized

    And here’s double:

    The smilies at twice size

    These are both gifs, to illustrate the size issue. Notice how they’re fuzzy?

    I want to make one thing clear, pun intended. None of my issues have to do with Retina. I don’t like ‘small’ anything. The smilies are too small, much like the font on many people’s websites, the pixelization is hard for me to identify. In fact, while I’m using the new smilies here, you’ll notice they’re bigger. Sure, cause I tossed this in my CSS:

    #wp_grins img, img.wp-smiley {
        height: 25px;
    }
    

    And that’s all it took to make them something I felt was readable. But the beauty, and why I like the new SVG images, is that I can make them larger like that, without losing readability.

    Thankfully, due to the curmudgeony efforts of the new smilies’ biggest naysayer, Otto, we have a filter! I’ve talked about this before, in how I handled both SVG and PNG as smilies for IE. I have a couple options here. I could write some code myself, or I could use New WordPress.com Smileys (which is on GitHub for reasons, the code is fine!).

    I like the plugin a lot. I like it so much, I refactored my SSL Grins plugin to use them with the fancy pants new span code (instead of images). But… Well, I already had this done by the time his plugin came out, and I like how I accounted for things differently. The only real difference though is he uses spans, and I just swap out the images:

    <?php
    /*
    Plugin Name: Custom Smilies
    Plugin URI:  https://ipstenu.org
    Description: I like the new smilies, shut up Otto.
    Version: 1.0
    Author: Mika Epstein
    Author URI: https://ipstenu.org/
    */
    
    // Move Smilies
    add_filter('smilies_src','helf_smilies_src', 1, 10);
    function helf_smilies_src($img_src, $img, $siteurl) {
        if ( strpos( $_SERVER&#91;'HTTP_USER_AGENT'&#93;, 'MSIE 8' ) || strpos( $_SERVER&#91;'HTTP_USER_AGENT'&#93;, 'MSIE 7' ) || strpos( $_SERVER&#91;'HTTP_USER_AGENT'&#93;, 'Android' ) ) {
            $img = 'ie/'.$img;
        }
        else {
             $img = str_replace("png", "svg", $img); // Remove PNG
        }
        return $siteurl.'/code/images/smilies/'.$img;
    }
    
    // This is in a paren for my sanity....
    {
        global $wpsmiliestrans;
    
        if ( !get_option( 'use_smilies' ) )
            return;
        
        if ( !isset( $wpsmiliestrans ) ) {
            $wpsmiliestrans = array(
            ':mrgreen:' => 'icon_mrgreen.png',
            ':neutral:' => 'icon_neutral.png',
         // removing b/c new versions
            ':twisted:' => 'icon_evil.png',
              ':arrow:' => 'icon_arrow.png',
              ':shock:' => 'icon_eek.png',
              ':smile:' => 'icon_smile.png',
                ':???:' => 'icon_confused.png',
               ':cool:' => 'icon_cool.png',
               ':evil:' => 'icon_evil.png',
               ':grin:' => 'icon_biggrin.png',
               ':idea:' => 'icon_idea.png',
               ':oops:' => 'icon_redface.png',
               ':razz:' => 'icon_razz.png',
               ':roll:' => 'icon_rolleyes.png',
               ':wink:' => 'icon_wink.png',
                ':cry:' => 'icon_cry.png',
                ':eek:' => 'icon_surprised.png',
                ':lol:' => 'icon_lol.png',
                ':mad:' => 'icon_mad.png',
                ':sad:' => 'icon_sad.png',
                  '8-)' => 'icon_cool.png',
                  '8-O' => 'icon_eek.png',
                  ':-(' => 'icon_sad.png',
                  ':-)' => 'icon_smile.png',
                  ':-?' => 'icon_confused.png',
                  ':-D' => 'icon_biggrin.png',
                  ':-P' => 'icon_razz.png',
                  ':-o' => 'icon_surprised.png',
                  ':-x' => 'icon_mad.png',
                  ':-|' => 'icon_neutral.png',
                  ';-)' => 'icon_wink.png',
            // This one transformation breaks regular text with frequency.
            //     '8)' => 'icon_cool.png',
                   '8O' => 'icon_eek.png',
                   ':(' => 'icon_sad.png',
                   ':)' => 'icon_smile.png',
                   ':?' => 'icon_confused.png',
                   ':D' => 'icon_biggrin.png',
                   ':P' => 'icon_razz.png',
                   ':o' => 'icon_surprised.png',
                   ':x' => 'icon_mad.png',
                   ':|' => 'icon_neutral.png',
                   ';)' => 'icon_wink.png',
                  ':!:' => 'icon_exclaim.png',
                  ':?:' => 'icon_question.png',
            // New for me
                  '>:(' => 'icon_mad.png',
                  'o_O' => 'icon_surprised.png',
                  'O_o' => 'icon_eek.png',
                  '^^‘' => 'icon_redface.png',
                  ':‘(' => 'icon_cry.png',
                  ':’(' => 'icon_cry.png',
       ':whiterussian:' => 'icon_whiterussian.png',
                  '|_|' => 'icon_whiterussian.png',
                   ':/' => 'icon_uneasy.png',
                  ':-/' => 'icon_uneasy.png',
          ':developer:' => 'icon_developer.png',
            ':burrito:' => 'icon_burrito.png',
            ':martini:' => 'icon_martini.png',
                  '>-I' => 'icon_martini.png',
              ':blush:' => 'icon_redface.png',
              ':heart:' => 'icon_heart.png',
                //'&amp;lt;3' => 'icon_heart.png',
               ':bear:' => 'icon_bear.png',
               ':star:' => 'icon_star.png',
                  '(w)' => 'icon_wordpress.png',
                  '(W)' => 'icon_wordpress.png',        
            );
        }
        
        if (count($wpsmiliestrans) == 0) {
            return;
        }
    }
    

    But why don’t they all show up in my comments section? Where’s the martini!? I also upgraded my plugin SSL Grins with an ‘easter egg’ of code that hides anything in this array of smiled_hide. This works on both my version of the smilies plugin and Avryl’s. and looks like this:

    $smiled_hide = array("bear", "wordpress", "martini", "developer", "whiterussian", "burrito","icon_bear.gif", "icon_wordpress.gif", "icon_martini.gif", "icon_developer.gif", "icon_whiterussian.gif", "icon_burrito.gif", "icon_bear.png", "icon_wordpress.png", "icon_martini.png", "icon_developer.png", "icon_whiterussian.png", "icon_burrito.png", "icon_bear.svg", "icon_wordpress.svg", "icon_martini.svg", "icon_developer.svg", "icon_whiterussian.svg", "icon_burrito.svg");
    

    Now this is possibly the jankiest, stupidest, code I’ve written in a while. I’d rather just have the words be there, and filter “If any of the values in this array is a partial match to the name of the smilie we’re looking at right now, don’t show it.” But I’m not clever enough with arrays and preg matching at this point, so I did that ugly list, and used this:

    if (!in_array($grin, $smiled) && !in_array($grin, $smiled_hide) ) {...}
    

    So at this point, pull requests are welcome because I’d love to clean that up!

  • Bad Browser Complacency

    Bad Browser Complacency

    Back in the day, you may have seen notices on browsers like “This site best viewed in Internet Explorer v4.” Since then, we’ve moved into the belief that a website should work on as many browsers as possible, and degrade nicely when it can’t. So imagine my surprise when I’m looking at photos online and I get a message saying I’m using an unsupported browser.

    I happened to be using Chrome on my iPad, so I clicked the link which took me to http://www.corbisimages.com/BadBrowser and I saw this:

    Bad Browser

    I was taken aback. Not that they say ‘IE 7 and up’ as it’s something I try to support as well. Frankly making sites look awesome on IE is about as easy and fun as a dental student doing your root canal. You’ll get there in the end, but it may hurt like hell.

    Now, Corbis has certainly one of the more out of date designs for a photo sites I regularly visit (there are 10), but it’s not the most egregious. Yes, there are worse ones. Still for a site to have an alert like this in 2014 and to omit Chrome is rather shocking. Someone remarked it looked like that site was designed circa 2003 and Chrome, if you didn’t know, only came out in 2008. So while I remember this new design for Corbis being rather recent (2012 or 2013, but that may have just been some tweaks), it’s clear they’ve not visited that page in at least six years.

    I talked to my family about web design at a recent brunch, stressing that I do not do website design per se, but I am happy to help them find people and upload their content. At that time, I pointed out that the trick to a website was to frequently update it and make it more modern. “It’s like the runway shows,” I replied. “What’s in this season was weird last season and may be out by next season. So making a site and never changing it is as smart as never updating your wardrobe.”

    Older fashion, not that good looking todayPeople judge by how things look. If someone only wears a black turtleneck and jeans (Steve Jobs), we create a specific mindview of them and it rarely changes. Someone who always wears avant-garde clothes that are nearly unwearable (Katy Perry), we create another. If that person always wears a suit jacket (Tim Gunn), we have yet another view. Neither is right or wrong, of course, and they all have their places.

    We update our wardrobes when we gain and lose weight, when we decide we want a change, when we feel different, when we have to change, when we want to. While I tease that Brian Gardner is never satisfied with a web design and is always changing, I’m often just as guilty of this as I don’t feel things fit forever. If I’m not afraid of changing my wardrobe, why would I be afraid of changing my website?

    And yet. We worry a lot more about the change of design, to the point that sites like Corbis haven’t significantly changed or adapted since 2002, when the site was born. Since then, 12 years have gone by, browsers have changed, security changed, and the viewing experience is wildly different. Corbis on a phone? Yeah not a great experience.

    When your site never changes with the times, never grows to adapt to it’s new audience, you lose respect in the world of the Internet. We have to keep up with the times, test and retest on as many browsers as humanly possible, and make sure that it all works. We can’t just say “Yes, this one design is good” and more so, we can’t say “This site works best on…” anymore.

    Unless you’re the sort to say “This jeans only work with turtlenecks.” Then, by all means, never change.

  • You Are The Weakest Link

    You Are The Weakest Link

    I sit by people who are on the phone with customers about their accounts all day. Each and every day I hear them talk to customers who are past due or need to activate a new credit card or had an account closed by not paying. In general, I marvel at how consistently nice these coworkers of mine are, considering the number of times I hear people screaming at them. But I also get to hear some fairly impressive conversations.

    Locked safeMost of the time, the conversations are mild, a reminder that you actually have to pay, here’s how you pay, off you go. But once in a while you get to hear the tale of someone who wants to cancel an account. This is only interesting because we don’t cancel your account for you. You have to log in and cancel the charges and billing. About once a day, someone asks why we can’t just accept they are who they say they are and close the account, and I hear my coworker explain over and over that it’s not secure. We can’t verify you over the phone, we called you, and… well there’s a reason you have to call your bank and not the other way around.

    Recently I had a credit card jacked, causing me to miss a payment I didn’t know I owed. I was called by a bill collector after 60 days and they asked for my account information. I balked and asked what card he was calling about, what was the amount, when was it charged, and so on. Then I hung up (I tried to tell him thank you, he kept talking) and called the card company directly. They confirmed the situation, I explained it wasn’t me, we got the bill reversed, everyone was happy… except me. I pointed out having some random number call me and claim to represent them was not safe or secure. They agreed, and also remarked that I shouldn’t have had a collection agency after me at only 60 days, so clearly they had a billing problem.

    I’m a little thoughtful about my security, if you can’t tell. So when I read my friend Gary’s story about Paypal’s lack of security, it surprised me. There’s no true Two Factor authentication on something that has access to my bank account, just a dongle I’d have to pay for (and keep handy) or SMS. And when you read more into why Gary turned on SMS (see the story about how @N lost his user name and $50k), it gets more disturbing.

    Social engineering to figure out passwords and pose as other people is easy. I do it all the time when I need to set up something for myself and my wife, and the company says she has to call too. I happen to have all of her information, down to access to her email (after all, I’m her sysadmin), and we actually made a specific joint email address for things like our bank account. If you have the information, it’s not hard to do.

    Chains around a railingWe can blame GoDaddy and Paypal all we want for this. Should they accept the last four digits of my credit card as identification? Should they accept my social security number? What about my password (which means they can read it, by the way), or what about a special password used only for verification? Now I have to remember more, carry more, and know more all the time. It’s information overload. And because of that, because we’ve complained, they do less.

    People see our credit cards all the time. You handed your debit card to someone to pay your eye-doctor’s co-pay, or for that latte. We tweet about our first pets and mother’s maiden names, and we Facebook everything. It’s pretty obvious that the weakest link in security, and the reason social engineering can easily exploit them with companies, is us.

    There isn’t a perfect way to protect ourselves, though. Last year my phone got wiped and I lost my Google Authenicator settings. I had secondary login codes for Google itself, but not WordPress.com (I’d never set them up) so I was unable to log in. After skyping and emailing two of my good friends who work there, I was able to get back in, but had they not been able to know it was me, what could have happened?

    Every time someone asks me what I do to be safer online, I tell them this “I am working to eradicate my ability to be stupid.”