Half-Elf on Tech

Thoughts From a Professional Lesbian

Tag: multisite

  • Multisite Per Site Registration

    Multisite Per Site Registration

    This post is dedicated to TJ List, who donated to help me get to WCSF. He’s the last post of this ilk, so I’ll stop having these announcements, but I wanted to say thank you all, one more time. My new job starts the 27th, and you all helped!

    Edit: If you want a plugin, see Join My Multisite

    Sometimes people want a way to sign up for the network on specific sites, and not go back to the main site. And I often tell people ‘You need to make a custom page template with the code from the signup page in it.’

    But how? There’s going to be a lot of code in this post, so at the end I’ll give you a link to the whole file you can download and play with. We’re going to go through the steps, however, since we may not be using the same theme, and you’ll need to understand the process.

    Make your Page Template

    First you make a new page template. I always call mine page-template-signup.php because well named files/code don’t need as much inline documentation.

    Since I wanted to build this off of TwentyTwelve, I grabbed the full page template, and pulled that over:

    <?php
    /*
    Template Name: Signup
    */
    
    get_header(); ?>
    
            <div id="primary" class="site-content">
                    <div id="content" role="main">
    
                            My Content Here
    
                    </div><!-- #content -->
            </div><!-- #primary .site-content -->
    
    <?php get_footer(); ?>
    

    Obviously on a first pass, nothing much is going on here. The next step is to open up wp-signup.php and take a look. There’s a lot going on here, but we don’t need all of it.

    First let’s look at what we need to add to the header area. Most of this is just styling and a little tweakage to stop search engines from thinking people need to really find this page, so let’s add the following right above get_header();

    add_action( 'wp_head', 'wp_no_robots' );
    
    // Fix for page title
    $wp_query->is_404 = false;
    
    function wpmu_signup_stylesheet() {
    	?>
    	<style type="text/css">
    		.mu_register { width: 90%; margin:0 auto; }
    		.mu_register form { margin-top: 2em; }
    		.mu_register .error { font-weight:700; padding:10px; color:#333333; background:#FFEBE8; border:1px solid #CC0000; }
    		.mu_register input[type="submit"],
    			.mu_register #blog_title,
    			.mu_register #user_email,
    			.mu_register #blogname,
    			.mu_register #user_name { width:100%; font-size: 24px; margin:5px 0; }
    		.mu_register .prefix_address,
    			.mu_register .suffix_address {font-size: 18px;display:inline; }
    		.mu_register label { font-weight:700; font-size:15px; display:block; margin:10px 0; }
    		.mu_register label.checkbox { display:inline; }
    		.mu_register .mu_alert { font-weight:700; padding:10px; color:#333333; background:#ffffe0; border:1px solid #e6db55; }
    	</style>
    	<?php
    }
    
    add_action( 'wp_head', 'wpmu_signup_stylesheet' );
    &#91;/php&#93;
    
    Just below <code>get_header();</code> we're going to add <code>do_action( 'before_signup_form' );</code>
    
    Then in the 'My Content Here' section, you're going to copy everything from <code><div id="content" class="widecolumn"></code> down to <code><?php do_action( 'after_signup_form' ); ?></code> and put it in there. It's a lot of code, I know. I'm not including it here because of that. Again, check the attached file.
    
    Finally search your file for all instances of <code>wp-signup.php</code> and change them. To what? To whatever you're going to name your page in the next section. I knew I wanted to name my page signup, so I replaced it with that.
    
    <h2>Make Your Page</h2>
    
    <a href="https://halfelf.org/wp-content/uploads/sites/2/2012/08/page-template.jpg"><img src="https://halfelf.org/wp-content/uploads/sites/2/2012/08/page-template-300x222.jpg" alt="Picking Page Template" title="Picking Page Template" width="300" height="222" class="alignleft size-medium wp-image-2509" /></a>Next you make a page. This is easy. You go in to your site, you make a page (I called mine Signup) and you put in <em>no</em> content. Pick the page template called 'signup' and save. I always opt to turn off comments and pingbacks on this page for what I hope are obvious reasons. Don't bother putting in any content to the post. Because we didn't leave in the loop, it wouldn't display.
    
    If that's something you want, then you would leave the loop info in where I had 'My Content Here'
    [php]
    
                            <?php while ( have_posts() ) : the_post(); ?>
                                    <?php get_template_part( 'content', 'page' ); ?>
                                    <?php comments_template( '', true ); ?>
                            <?php endwhile; // end of the loop. ?>
    

    That’s an ‘as you like it’ sort of thing. If you need to leave messages, etc, it’s great.

    And that’s kind of it. Oh, you’ll want to mess with your CSS, but that’s all you need to get started! It even keeps you on the page.

    How it looks…

    Since there are multiple registration options, here’s how they look:

    With Registration Disabled

    User accounts may be registered

    Logged in users may register new sites

    Only Logged In Users Can Make Sites

    Both sites and user accounts can be registered

    Adding Users to Sites

    I know, I said we were done. There’s one more thing. What happens if you want that per site registration to automatically add people to the site they’re on? That is, if someone goes to halfelf.org/signup/ I want them to be added to halfelf.org as a user. The problem with this is when you register for a user account, you may have noticed there’s an ‘extra’ step to confirm. This is where you get that email that says ‘click here…’

    The ‘good’ thing is the URL you get looks like this https://halfelf.org/wp-activate.php?key=0ef92965187a50be and it will take you back to the right site to login! The ‘bad’ thing is it doesn’t actually add you to the site. It will add you any time you create a site, but if you just make a login ID, you get this:

    No Access Is Bad

    As you can see from that URL, it’s sending me back to wp-activate.php, which is something we can’t change in our signup template. If you know you want to add all users to all sites, then you want Multisite User Management, but that isn’t always the case. So what do we do? Option one is to grab a plugin that makes us approve all users, then we can add them as they’re requested. But you don’t want to do manual work, I’d guess.

    The trick is to add an action onto user registration, but only run it per site. So you’d want this in an mu-plugin, and since halfelf happens to be blog , I do this:

    <?php
    global $blog_id;
    if ( $blog_id == 2 ) {
    
      function helf_activate_user( $user_id, $password, $meta )
      {add_user_to_blog( '2', $user_id, get_site_option( 'default_user_role', 'subscriber' ) );}
      add_action( 'wpmu_activate_user', 'helf_activate_user', 10, 3 );
    }
    ?>
    

    That says ‘Hey, if I’m on blog ID 2, when people register, kick them to be a subscriber on this blog. I have mine in a plugin called halfelf-autoregistration.php and it works rather nicely. If someone registers on ipstenu.org, even if they try to login to halfelf, they can’t login because their registration process was on the other site.(I suppose if someone was clever, they could take https://halfelf.org/wp-activate.php?key=0ef92965187a50be and change it to https://ipstenu.org/wp-activate.php?key=0ef92965187a50be to register there, but there’s no change in ‘risk’ between being a network psudeo subscriber and a real subscriber. If I was adding in editors, I’d probably change my mind here and manually validate every new user. I’m trusting in the fact that most people aren’t that clever.)

    Do you have any tricks that help you do this?

    Oh and here’s the file to download. It’s in .txt for security reasons.

    Download Here

  • My Custom PostTypes Live in MU

    My Custom PostTypes Live in MU

    This post is dedicated to Boone Gorges (aka ‘WP Boone’ to me), who donated to help me get to WCSF. My brother is also named Boone, so even if WP Boone wasn’t so awesome, I’d like him.

    Brain - You must useCustom Post Types. I really dig them, as a great way to make ‘kind of’ pages, without making a million pages. They don’t ‘order’ as well as pages, and default to publish date, but really that could be adjusted. The point, and I have one, is that they’re often a great alternative to Multisite, and I use them a lot.

    There are lots of plugins that can make these for you, but I prefer to do it myself in a function file, becuase it gives me more flexibility for what is, let’s face it, a complicated sort of thing.

    In this example, I’m going to make a ‘drawing’ custom post-type, like the one I just added to my photoblog. First I made a file called photo-cpt.php and in it put a header:

    <?php
    /*
    Drawing Name: Photos CPTs
    Drawing URI: http://photos.ipstenu.org/
    Description: All Photos custom code.
    Version: 1.0
    */
    ?>
    

    Notice I am not telling you to put this in a functions file. I never put my CPT in my theme’s function, becuase I always make my CPTs able to work with any theme. By making it a stand-alone ‘plugin’ file, I can put it in mu-plugins and run it automatically. More on this in a minute.

    The code itself is split into two sections. I learned this method from Justin Tadlock, who has a nice, if very techy, primer on Custom Post Types in WordPress. I freely admit, once I figured this code out, I saved it off line and copy/paste it where I need, replacing the terms (Drawing/s) for what the new CPT is.

    <?php
    	add_action( 'init', 'create_photos_post_types' );
    
    	function create_photos_post_types() {
    
             /* Labels for the Drawing post type. */
            $drawings_labels = array(
                    'name' => __( 'Drawings', $domain ),
                    'singular_name' => __( 'Drawing', $domain ),
                    'add_new' => __( 'Add New', $domain ),
                    'add_new_item' => __( 'Add New Drawing', $domain ),
                    'edit' => __( 'Edit', $domain ),
                    'edit_item' => __( 'Edit Drawing', $domain ),
                    'new_item' => __( 'New Drawing', $domain ),
                    'view' => __( 'View Drawing', $domain ),
                    'view_item' => __( 'View Drawing', $domain ),
                    'search_items' => __( 'Search drawings', $domain ),
                    'not_found' => __( 'No drawings found', $domain ),
                    'not_found_in_trash' => __( 'No drawings found in Trash', $domain ),
            );
    
            /* Arguments for the Drawing post type. */
            $drawings_args = array(
                    'labels' => $drawings_labels,
                    'capability_type' => 'post',
                    'public' => true,
                    'has_archive' => true,
                    'can_export' => true,
                    'query_var' => true,
                    'rewrite' => array( 'slug' => 'drawings', 'with_front' => true ),
                    'taxonomies' => array( 'post_tag', 'category'),
                    'supports' => array( 'title', 'editor', 'excerpt', 'thumbnail', 'custom-fields', "photos-post-settings" ),
            );
    
            /* Register the Drawing post type. */
            register_post_type( apply_filters( 'photos_drawings_post_type', 'drawings' ), apply_filters( 'photos_drawings_post_type_args', $drawings_args ) );
    	}
    ?>
    

    Looking at this, it’s actually surprisingly straightforward what I’m adding and where. The weird code of $domain is a variable I’ve defined elsewhere, and lets me translate if I need to. I probably won’t but it’s a good practice to get into. By splitting out my labels into their own variable, I’m able to break them up and make it more readable. As Otto says, good code doesn’t need inline documentation becuase it’s readable. You can see the names, and the fields, I’m adding, and they magically become self-explanatory. Then in my drawing arguments, I again make a variable with the settings. Pull in the complex labels, then I can break out the next arguments into something readable.

    Trucks and Buses Must Use Low Gear You can read all the various options in the codex article for register_post_type(), which is the function I finally call at the end.

    If you wonder why I have the whole thing wrapped in an action, it’s becuase in other places I actually add multiple post types to a site. This lets me put them all in one action, call it once, and walk away. As long as each CPT has a label, arguments, and registration, they’ll all run.

    Below that action, I have one to add my CPT to my ‘right now’ section on the dashboard. I got this from James Laws over at WP Ninjas, and really it’s one of my favorite things.

    <?php
    // Adding to Right Now
    	add_action( 'right_now_content_table_end', 'photos_right_now' );
     
    	function photos_right_now() {
      
              // drawings
              $num_drawings = wp_count_posts( 'drawings' );
              $num_p = number_format_i18n( $num_drawings->publish );
              $text_p = _n( 'drawings', 'drawings', intval($num_drawings->publish) );
              if ( current_user_can( 'administrator' ) ) {
                $num_p = "<a href='edit.php?post_type=drawings'>$num_p</a>";
                $text_p = "<a href='edit.php?post_type=drawings'>$text_p</a>";
              }
              echo "\n\t".'<tr class="first">';
              echo "\n\t".'<td class="first b b-drawings">' . $num_p . '</td>';
              echo "\n\t".'<td class="t drawings">' . $text_p . '</td>';
              echo "\n\t".'</tr>';
    
    	}
    ?>
    

    Taking a step back, there’s this interesting line in my arguments:

    'taxonomies' => array( 'post_tag', 'category'),
    

    All this does is say ‘I want to use post tags and categories in my CPT.’ And in this case, it’s the same ones as I use for my normal posts. You can do a lot more with it if you wanted, but I believe in KISS.

    To loop back around, however, why do I put this in an mu-plugin? First and formost, it’s portable. No matter what theme I use, it comes with me. As I talk about this method in No Children Necessary, it really comes into play here more than anywhere else. On a single, traditional, WP install, I just toss it in and walk away. It’s code, I don’t want my end-users playing with it, so a non-editable file is perfect. For Multisite, I really just add if ( $blog_id == 2 ) { ... } around the whole thing. I could do it just on the actions, but this is easier for me. I can see right away ‘Oh! Site 2.’

    This is obviously not going to work for everyone, but sometimes just looking at the next option will give you a new idea.

  • Amazoned

    Amazoned

    Amazon.com IconI don’t actually like Amazon. However the Kindle people spoke and I listened. After checking that, yes, I can release my ebook for sale on the Kindle legally (that was a weird conversation), I sat about doing it. I really wanted to self-publish on lulu, but … well, Amazon was easier. There. I said it. They actually know what they’re doing, and made it surprisingly painless! Of course, that they want you to write it in .docx, export to html, then upload a zip, was a little nuts. They also let you upload ePubs, which I have, so that was a lot easier.

    I am now a Amazon Author or publisher. Whatever. And I have two books up on Amazon:

    There’s no DRM on these puppies, and while, yes, it costs $7.99 to download, that’s because Amazon takes a really hefty chunk of change. See for every $5 you donated to me, I got $4.50 from Paypal and WePay. Amazon takes 30% or 65% of your money for every sale. Now I know, you’re thinking ‘Why would anyone pick the 35% option!?’ In the ‘real world,’ publishers give writers around 15% in royalties. Seems like a rip off at first, but look at what goes into all this. Writing, editing, typesetting, printing, binding, shipping, advertising, etc. And I’m skipping all that to go at it alone.

    So why would I pick 35% for epublishing, when all they’re doing is webhost plus bandwidth? If you pick 70% royalties, then you get 70% of the cost, minus ‘shipping.’ Shipping, by the way, is electronic delivery. You’re paying for the bandwidth. Fair enough, works out to about $0.05 for every country. But also with 70%, I can only charge between $2.99 and $9.99 US for my book. So the max I can charge is $9.99, which will ‘net’ me $6.96. With 35% I can charge up to a couple hundred ($299.99). Now here’s the dillema. How much to charge. If I max out and go to $9.99 (damn it, I wish they’d just say $10!), I may look greedy. Then again, I did all the work here, do I not deserve my $7 per book? To get the $4.50 I was getting via donations, I’d have to charge about $6.75 for the book, at which point I may as well charge $7. Then again, the average donation I got was actually closer to $8.

    Which is how I ended up at charging $7.99 you see. I don’t expect to break the bank. Hell, I don’t expect lightning to hit like that again. But it was kinda cool. By the way, you don’t get 70% for all countries, just the ones that allow it. The rest get you 35% instead.

    The biggest hassle, other than price, was sorting out the various fields, which in and of themselves, weren’t that hard. Took me an afternoon, and I had the books approved by later that day. I found an error (I’d uploaded the wrong cover, go me) and made a fix, for that I waited a day for the republish and the image to show up. Weird, but not complicated.

    I have to say, Amazon is a hassle, but far less than other places that say ‘You need to make an ISBN’ and ‘You must follow these formatting guidelines.’ which are cryptic and … You know, if they really wanted to make things easy, someone would make a nice form where you could upload your book, sans cover and author pages and copyright. Just the book content, right? Then once it’s uploaded, from epub or docx, you create your author ‘page’ and place it where you want (front of TOC or behind) and then copyright page ditto. Finally you upload your cover. If an ISBN is required, you make it for them. Remember, self publishing is something that you should be helping people do! Otherwise what’s the point?

    Amazon (mostly) hits that one on all the marks. Why not iBooks, you may ask? The tool sucked. No, really. I wrote a doc in pages (Apple’s version of Word) and you’d think I could upload to iBooks and it would auto-format. Nope. Also there was the restrictive bit. Apple’s terms say that any content produced with iBooks Author that is for sale is to only be available on the iBookstore. Free ones can be distributed anywhere you like, but iBooks Author only exports to the .ibook format (and PDFs) so if you want to free-distribute (which technically is what I do here), you have to use a flat PDF. Okay, that isn’t really terrible, the epub to pdf ratio is 1:70. But then I can’t ‘sell’ on Amazon! Amazon, by the way, only locks me down if I’m using the KDP Select program. At least I think so. I read the legalese a few times.

    Fairness to be had, I don’t feel that it’s ‘wrong’ for Amazon or Apple to restrict what you can do when selling your books. It’s free, you get what you pay for, as it were. Both Amazon and Apple take a 30% cut, and all things being equal, Amazon is a better choice. Of course you can still download them ‘for free’ if you want to from my ebooks page. Donations, as always, are welcome.

  • WordPress Multisite 110 – Electric Boogaloo

    Multisite 110

    Introducing WordPress Multisite 110

    I promised a sequel and I delivered. I hope you guys find it helpful. The sequel is out, and it’s longer than the original. Weighing in at over 80 pages, WordPress Multisite 110 has even more information about WordPress Multisite!

    Why a sequel and not a second edition? There were more things to add than a few extra plugins. Branching into a little more philosophy and explanations to the whys, Multisite 110 hopes to be the second handbook you’ll need. The funny thing is most people who read my site regularly don’t need this at all. But your clients might. Imagine being able to hand that off to them saying ‘I’ve got you started, here’s how you can make it epic.’

    Oh yes, with a Creative Commons release, you can pass this on to your clients as you like. I won’t stop you, just don’t sell it to them. If you want to get into the shenanigans, you can bill them for it, but not sell it. Yeah, licenses will kill us all one day. Speaking of licenses, there’s code in this one, and it’s all under GPL2 (most of it’s also on this site already).

    What’s not in it? Deep diving into the database. Fixing everything… There’s no way to cover everything. This one gets into the machinations of how you make a multisite where everything looks the same, or where your admins aren’t admins at all. Favicons? Got that covered too! The White Page of Death? How to figure out what plugin you want? Man, you know I got your back! Backups, control, security, uploads… the list isn’t endless, but there’s a lot going on.

    Check out WordPress Multisite 110

    The post title of ‘Electric Boogaloo’ comes from a movie that came out in 1984: Breakin’ Two: Electric Boogaloo. All sequels should have that sub-title

  • Jetpack Menu Stats

    Jetpack Menu Stats

    If you run a Multisite, you have a list of all your sites in the ‘My Sites’ menu. Adding your own custom menus to that isn’t all that complicated. Here’s a quick, practical, bit of code:

    <?php
    /*
    Plugin Name: Jetpack Menu Stats
    Plugin URI: https://halfelf.org/hacks/jetpack-menu-stats/
    Description: Show 'stats' in the per-site menu bar.
    Version: 2
    Author: Mika 'Ipstenu' Epstein
    Author URI: https://ipstenu.org/
    */
    
    function jetpack_stats_my_sites( $wp_admin_bar ) {
            global $wpdb;
    
            foreach ( (array) $wp_admin_bar->user->blogs as $blog ) {
    
                    $menu_id  = 'blog-' . $blog->userblog_id;
                    $args = array(
                       'parent' => $menu_id,
                       'id'     => $menu_id . '-stats',
                       'title'  => __( 'Site Stats', 'jetpack' ),
                       'href'   => get_admin_url( $blog->userblog_id ).'admin.php?page=stats',
                    );
                    $wp_admin_bar->add_node($args);
            }
    }
    
    add_action( 'admin_bar_menu', 'jetpack_stats_my_sites', 90 );
    
    ?>
    

    You can tweak this to anything you want, obvious. If you change the numerical value in the add_action() call, you can move it up or down the menu. A value of 10 will put it at the top. You could probably toss in a check if the plugin was activated per-site as well (I didn’t bother for Jetpack, since I know how to tell if Jetpack’s active via is_plugin_active(), but not a sub-plugin).

    As with all my code, is licensed GPL2. Use, abuse, tweak and customize. Don’t expect support, though.

    Edited to fix i8n and use add_node, thanks to Thomas and kessiemeijer!

  • WordPress Multisite 101

    WordPress Multisite 101

    So there’s this thing. I blog a lot, but sometimes the ‘lessons’ I want to teach would take up a few thousand words. I’ve sorted out that any blog post over 1200 words is ‘too long’ and I try to split it up. But then how do I organize it? Let’s face it, books are useful for a reason.

    After compiling and colating all the emails, IMs, forum posts, and blog posts Andrea and I have made over the last couple years, we realized we had a novel. The problem was organizing it so the scope wasn’t maddening and daunting for us to write, nor for the user to read. Finally inspiration struck. If you’re using Multisite, you really need to know WordPress first. You have to walk before you can run, as they say, and with Multisite, you have to already know how to do the basics.

    This book will not teach you how to pick a host, copy files up, create a database, or any of those things. It won’t even tell you if you should or should not use Multisite. What it will do is help you go from WordPress to WordPress Multisite, configure the options, understand what they mean, sort out the standard problems, and help you figure out what you need to know and where you need to be in your own head in order to do this thing.

    And it’s free. Well, no. It’s not. It’s pay what you want.

    That’s the other thing. I could go the traditional route with a book, find someone to publish it, etc etc. Or I could self-publish on the iBook store or eJunkie and take a hit for the overhead and the hassles of all that. Or… Or I could address the real problem about making ‘money’ with books. Obscurity. I have a whole philosophy about paying for ebooks and you can read it if you want. But the tl;dr for you is this.

    Pay me whatever you think the ebook is worth. If you aren’t going to pay, you weren’t anyway, and that’s nothing lost from my end. I’d appreciate a fiver if you find it useful. I totally support you downloading it first, reading it, then paying later. After all, how do you know it’s what you wanted without reading it?

    Grab a copy of WordPress Multisite 101, it’s in ePub and PDF. You know the drill. Right click and save as.