Half-Elf on Tech

Thoughts From a Professional Lesbian

Author: Ipstenu (Mika Epstein)

  • WordPress Multisite: Block Site

    WordPress Multisite: Block Site

    This came up when I was looking at WordPress.com, where one has the freedom to post anything within their ToS, and I saw someone’s moronic blog about how specific people were evil. Pick whatever you want, it doesn’t matter except assume it was something offensive to a minority.

    The Terms of Use says this:

    In particular, make sure that none of the prohibited items (like spam, viruses, or serious threats of violence) appear on your website.

    This was not a serious threat of violence, it was just ignorant, offensive, and stupid. I looked at the site and thought “What I want most in this moment is a big ass button to block this person from posting on my .com site, and to prevent them from ever being able to comment on any blog I own.”

    It doesn’t exist. (I will note I found BuddyBlock but I have no idea how well that would work, and it’s for BuddyPress only.)

    Part of the cool thing about WordPress Multisite is that you can run your own social network. With that power comes responsibility though. Users should be able to protect themselves while remaining on your network, allowing them to block other users they just don’t want to talk to.

    So why don’t we? Well effective blocking is hard. As I mentioned in my post about how (most) contact forms fail at this, the biggest issue is people can just fake who they are are try again. This is a little harder on a Multisite, where a legitimate email and account can be required to comment, but by default all members of a network can comment on any blog on the network. This means we’re opening ourselves up to the potential to more abuse.

    How would that big block work? There are a few approaches and I think the best route would be two fold.

    Blocking Users

    Everyone should have the ability to mute or block a user. As an end user, if I never want to see comments from John Smith again, I should be able to press ‘block.’ Then I would just see a note like [comment hidden] whenever I run into a comment from him on any blog on the network. On a non Multisite, I’d actually like to see that for any site that requires registration. Allow users to mute each other.

    As an admin, if I block John Smith, then his comments are immediately discarded. If you wanted to get fancy, then you’d hide his comment from everyone who isn’t him, so he thinks he’s still talking to people and just being ignored. A silence mode. Use some JS so an admin has to click to expand and see what’s going on, so if John Smith is escalating, he can be banned.

    That would be the other thing. Banning users from your sites on a Multisite should be totally possible. And on .com a way to report “User X keeps working around my blocks.” would help a lot.

    Also for admins, perhaps they should be able to see “X people have blocked this user” on the Dashboard. That said, I can see a massive possibility for abuse with that. If John Smith was an admin of his own blog and saw ’10 people blocked you…’ it could cause problems. It would be trivial to hide it from the user, so you could never know how many people blocked you, but I can think of a few fast workarounds. Easiest is to add a second admin account to my own blog on the network and check.

    Blocking Blogs

    This is mostly an issue on WordPress.com, since it’s one of the few places I know of that has a ‘reader’ that shows you blogs that you might be interested in. That’s how I found the offending blog, by the way. A friend runs a religious blog on .com and the one we both found appalling was a recommended blog to her. I’ve already talked to some people behind the scenes of .com about that and how the algorithm may need some turning. But even if she had stumbled on to it via a search, should she not be able to say “Ew! Block!”

    I would write it so that if someone clicked ‘block blog’ the following things happen:

    1. The owner of the blog is blocked from commenting on any blog I own
    2. The URL of the blog is placed on my blacklist
    3. Optionally, all admins of the blog are added to my blacklist

    Now I don’t have to see anything anymore.

  • Looking Back at MovableType

    Looking Back at MovableType

    For the first time in years, I looked at Movable Type.

    I walked away, like so many people, in May of 2004 when the restrictions and pay requirements were too much. I’d played with b2 before and WordPress, but that was when I fully moved to WordPress. While I’d remembered that the Open Source version had been fully restored in version 3.3, I forgot that when they released v6 in 2016, they ‘terminated’ the Open Source licensing option. Again.

    In doing normal research of things, I ended up on MovableType.com, and was struck by how modern and out of date the site felt.

    The site isn’t mobile friendly. Or at least not iPad friendly. It does this peculiar zoom in where the content is focused but it still has a sidebar. This means flicking down to read can causes my screen to wobble side to side as well. The zoom also didn’t work consistently, making me have to fix it over and over.

    That said, it has a much nicer design and layout than I expected.

    MovableType.com front page

    I have to say, that’s a much more modern front page than WordPress.org and less cartoony than the current WordPress.com pages. The same can’t be said of navigation, which was a little confusing. If you don’t know you have to purchase to download, seeing the Software License section without clarification is weird. That should be even more obvious, I think. I shouldn’t have to click on “Release Notes” and then see Install MT on the sidebar.

    Once I ended up in the documentation, I poked around and had a laugh at the software requirements.

    PHP 5.0 or higher (5.3 or higher is recommended)

    Sounds familiar, doesn’t it?

    The rest of the install direcrions are incredible weird and hands on. It has none of the simplicity I’ve come used to with WordPress. And please remember, I think that WordPress is far too complex for a new user, still, because WP’s NUX sucks. MT’s is worse.

    What interested me the most is that, while you can’t get MT for less than $900, they have a public GitHub repo available.

    Still, I didn’t install it. Instead I read the documentation to see what using it would look like, and was rather startling to read the author page on creating entries and see an interface that looked old.

    MT's post editor looks like WP 2.x

    It reminded me of WP 2.5. Which I guess is understandable since the documentation on how to import from WP to MT is very old. No, I’m serious, it has screenshots of what looks like WP 2.5 as their documentaion.

    While I still think that MT lost out big time when they decided to separate from the Open Source community, their product doesn’t draw me in. It doesn’t look fun or nice to use, and that’s probably a reason it’s not as popular as it could be. The GitHub page has 22 contributors. WordPress 4.5, led by my coworker and friend Mike, had 298. Even the official, but not really used like that, WP GitHub repo has over 30 contributors.

    I wonder how the web would have looked if Six Apart had never made the license changes.

    I wonder would power 26% of the Internet in that world.

  • Customizing Taxonomies as Dropdowns in Quick Edit

    Customizing Taxonomies as Dropdowns in Quick Edit

    When you go to quick edit for a post, you will automatically see your custom taxonomies as editable fields:

    Custom Taxonomies showing in quick edit!

    But there’s a problem … I don’t want these to be text fields nor do I want them to be checkboxes. When it’s just you running a site, there’s less to worry about with regards to these things. You know what you’re adding, and if you typo, you blame yourself. At the same time, WordPress only has two options for these sorts of things: tags (freeform text) and categories (checkboxes).

    Technically they’re settings for hierarchical, where false is text and true is checkboxes, and I’ve hidden the parent field (a topic for another post). But doing that makes them all check boxes, and I want to restrict people to one and only one checkbox. Except I don’t. The UX of having checkboxes vanish is pretty shitty, and it isn’t logical that you would only check one box.

    Let me explain. In this example, which is practical, I have a taxonomy for human sexuality. I chose taxonomies and not post-meta because that would allow me to sort much more easily on data by groups. Simply, I can grab a list of all people who are flagged as ‘pansexual’ with one function and not have to reinvent the wheel.

    That means I can use radio buttons or a dropdown. I feel a dropdown is a better, cleaner, UX, so that’s what I’m going to do. You can use the same logic here, though, so don’t worry.

    After reading ShibaShake’s documentation on adding in quick edit values I winced. Adding it with quick_edit_custom_box is super easy. The problem is that you have to use an incredibly weird amount of javascript to get the post ID and pass data back and forth. As weird and annoying as that was, it actually works. The example, of course, is comparing a CPT, where as I am using a custom taxonomy, so my own code was a little crazier.

    Remove the taxonomy from Quick Edit

    To do this starts out sounding like the stupidest idea ever, but you want to set show_in_quick_edit to false in your taxonomy. That does what it sounds like, removing the item from the quick edit page. Obviously now I have to add it back.

    It’s important to note that if you don’t have any custom columns in your post list view, the rest of this won’t work. I do, and since part two of all this will be about being able to edit those custom columns, I don’t have to worry. If you do, here’s how you add a fake column… Ready? You add and remove the column. I know, I know, WP can be weird:

    add_filter('manage_posts_columns', 'lezchars_add_fake_column', 10, 2);
    function lezchars_add_fake_column( $posts_columns, $post_type ) {
        $posts_columns['lez_fakeit'] = 'Fake Column (Invisible)';
        return $posts_columns;
    }
    add_filter('manage_edit-post_columns', 'lezchars_remove_fake_column');
    function remove_dummy_column( $posts_columns ) {
        unset($posts_columns['lez_fakeit']);
        return $posts_columns;
    }
    

    Like I said, I know it’s weird.

    Add the Quick Edit Box

    Since I know I’m going to be building out a few more of these, and one is a cross-relational CPT to CPT drama, I’ve broken mine out into a switch/case basis.

    // Add quick Edit boxes
    add_action('quick_edit_custom_box',  'lezchars_quick_edit_add', 10, 2);
    function lezchars_quick_edit_add($column_name, $post_type) {
    	switch ( $column_name ) {
    		case 'shows':
    			// Multiselect - CPT where characters may have multiple
    			break;
    		case 'roletype':
    			// Single Select - Custom Taxonomy
    			break;
    		case 'taxonomy-lez_sexuality':
    			?>
    			<fieldset class="inline-edit-col-left">
    			<div class="inline-edit-col">
    			<span class="title">Sexual Orientation</span>
    				<input type="hidden" name="lez_sexuality_noncename" id="lez_sexuality_noncename" value="" />
    				<?php 
    				$terms = get_terms( array( 'taxonomy' => 'lez_sexuality','hide_empty' => false ) );
    				?>
    				<select name='terms_lez_sexuality' id='terms_lez_sexuality'>
    					<option class='lez_sexuality-option' value='0'>(Undefined)</option>
    					<?php
    					foreach ($terms as $term) {
    						echo "<option class='lez_sexuality-option' value='{$term->name}'>{$term->name}</option>\n";
    					}
    						?>
    				</select>
    			</div>
    			</fieldset>
    			<?php
    		break;
    	}
    }
    

    This is all pretty basic stuff. I’m simply making a form for a drop-down based on the contents of my taxonomy. If you want to make it radio buttons, do that.

    Saving The Changes

    Now we’re getting a little weirder. We need to save our changes, but only if it’s not an auto-save, and if it’s the correct post type (post_type_characters for this page), and if the user can edit the page:

    add_action('save_post', 'lezchars_quick_edit_save');
    function lezchars_quick_edit_save($post_id) {
        // Criteria for not saving: Auto-saves, not post_type_characters, can't edit
        if ( ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) || ( 'post_type_characters' != $_POST['post_type'] ) || !current_user_can( 'edit_page', $post_id ) ) {
    		return $post_id;
    	}
    
    	$post = get_post($post_id);
    
    	// Lez Sexuality
    	if ( isset($_POST['terms_lez_sexuality']) && ($post->post_type != 'revision') ) {
    		$lez_sexuality_term = esc_attr($_POST['terms_lez_sexuality']);
    		$term = term_exists( $lez_sexuality_term, 'lez_sexuality');
    		if ( $term !== 0 && $term !== null) {
    			wp_set_object_terms( $post_id, $lez_sexuality_term, 'lez_sexuality' );
    		}
    	}
    }
    

    The two checks going on here are first to be sure it’s the right POST action to save on, and then to only update if the term already exists. If you wanted to append terms instead of replace, you could add a ‘true’ param to wp_set_object_terms, however I want only set one sexuality per person in this case.

    At this point, the code actually works!

    The new dropdown

    Changing the current selection for the dropdown

    There’s one problem though. If you’re working along with me this far, you’ll have noticed that the default selection is always ‘(Undefined)’ and that’s not what we want. The extension of this problem is we have to use bloody javascript to edit it. Damn it.

    // Javascript to change 'defaults'
    add_action('admin_footer', 'lezchars_quick_edit_js');
    function lezchars_quick_edit_js() {
    	global $current_screen;
    	if ( ($current_screen->id !== 'edit-post_type_characters') || ($current_screen->post_type !== 'post_type_characters') ) return;
    	?>
    	<script type="text/javascript">
    	<!--
    	function set_inline_lez_sexuality( widgetSet, nonce ) {
    		// revert Quick Edit menu so that it refreshes properly
    		inlineEditPost.revert();
    		var widgetInput = document.getElementById('terms_lez_sexuality');
    		var nonceInput = document.getElementById('lez_sexuality_noncename');
    		nonceInput.value = nonce;
    
    		// check option manually
    		for (i = 0; i < widgetInput.options.length; i++) {
    			if (widgetInput.options[i].value == widgetSet) {
    				widgetInput.options[i].setAttribute("selected", "selected");
    			} else { widgetInput.options[i].removeAttribute("selected"); }
    		}
    	}
    	//-->
    	</script>
    	<?php
    }
    
    // Calls the JS in the previous function
    add_filter('post_row_actions', 'lezchars_quick_edit_link', 10, 2);
    
    function lezchars_quick_edit_link($actions, $post) {
    	global $current_screen;
    	if (($current_screen->id != 'edit-post_type_characters') || ($current_screen->post_type != 'post_type_characters')) return $actions;
    
    	$lez_nonce = wp_create_nonce( 'lez_sexuality_'.$post->ID);
    	$lez_sex   = wp_get_post_terms( $post->ID, 'lez_sexuality', array( 'fields' => 'all' ) );
    
    	$actions['inline hide-if-no-js'] = '<a href="#" class="editinline" title="';
    	$actions['inline hide-if-no-js'] .= esc_attr( __( 'Edit this item inline' ) ) . '" ';
    	$actions['inline hide-if-no-js'] .= " onclick=\"set_inline_lez_sexuality('{$lez_sex[0]->name}', '{$lez_nonce}')\">";
    	$actions['inline hide-if-no-js'] .= __( 'Quick&nbsp;Edit' );
    	$actions['inline hide-if-no-js'] .= '</a>';
    	return $actions;
    }
    

    Please don’t ask me to explain that. And yes, I know we should be using Unobtrusive Javascript and hooking into the DOM instead, but I don’t yet know how to do that.

    I do know that if I wanted to add in multiple checks, one way is to duplicate the function set_inline_lez_sexuality( widgetSet, nonce ) and rename it to set_inline_lez_gender and then extend the actions like this:

    $actions['inline hide-if-no-js'] .= " onclick=\"set_inline_lez_sexuality('{$sex_terms[0]->name}', '{$sex_nonce}');set_inline_lez_gender('{$gender_terms[0]->name}', '{$gender_nonce}')\">";
    

    There are more combinations and concatenations one can do here. Knock yourself out. It’s enough javascript for me today!

  • Not Mailbag: Where Contact Forms Fail

    Not Mailbag: Where Contact Forms Fail

    My friend Andy, reading last Friday’s post, remarked no one should have to put up with crap like that. He’s right, and I mentioned that most contact forms don’t allow you to filter via your WordPress blacklists or comment moderation settings.

    Surprised?

    You should be.

    Back in March 2014, I raised this with Jetpack, saying that the Feedback ignores Blacklists.

    You have a moderation list and a blacklist.

    You have a user you want to block from commenting forever. You add them to the blacklist. Surprise! They can still use the feedback form!

    This should behave just like the blacklist on comments: It blackholes them. Done and gone. After all, you didn’t want them around.

    Logically I can see why it doesn’t use the comment checks. If you have a check to only let users who have an approved comment, leave more comments freely, this would be a problem. There’s no ‘pending’ value for feedback.

    And the first reply … Well it made me mad back then. I say this as someone who is good friends with the fellow who commented, but back in 2014, I wanted to smack the back of his head.

    This would be super easy to get around, just changed the alleged from email address. Besides, blacklist tends to be things that shouldn’t be displayed publicly automatically, allowing contacts would let them appeal the blacklist.

    I could see grounds for adding a filter to have grunion follow the commenting blacklist though. Less sold on an admin option.

    Now go back and read last week’s post. I have not blacklisted the rather vile word used in that comment because I have a friend who is dyslexic and often says ‘cuntry’ instead of ‘country.’ It’s an honest mistake on her part. We added in an autocorrect to her phone and tablet. But blocking short words is hard. Still. The IP address? You bet that hit my blacklist.

    If I still had a comment form, that moron could still harass me.

    As I replied to George:

    Sure, and it’s just as easy to get around the current blacklists in WP. The point is, though, if you’ve put someone’s email on your comment blacklist, the assumption can be made that you have a good reason. You DON’T want this person commenting on your site, so why are you making it easy for them to harass you? And yeah, I used ‘harass’ intentionally.

    Certainly I can and do block their emails on the server, but I still have to go in and clean out the messages in feedback once and a while, and I for one get a lot of pretty vile garbage from people. So having one less place to have to read their BS would be beneficial.

    It’s always been relatively easy to work around if you’re a dedicated troll, but if the blacklist just blackholed their contact messages, it does a lot for your mental health.

    Because he’s right that a dedicated asshole will work around the blacklists. They do it today. Still, I feel there’s no reason to make it easier for them. And while I can block from a server level, not everyone has my skills. And for those people, should we not introduce Akismet level scans on feedback forms?

    You see, the reason I was mad at George back then is his argument felt like he was saying “since it can be worked around, this is a bad idea.”

    That is absolutely not what he meant.

    Even if I didn’t know George well, I have simple proof he didn’t think this was a stupid idea, he thought it was an idea that begat caution. What proof? He didn’t close the issue. In fact, he gave it a milestone to review.

    Now, sadly, it’s been two years with no traction. Every so often someone bumps the milestone, which means it’s among the 600+ tickets that need attention. But it lingers. It’s not a priority.

    Jetpack and Akismet are both owned by the same company. If you have the Akismet plugin installed and activated, and have an active subscription, every form submission will be checked for spam.

    They need to take it to the next level. So do all forms plugins. From what I can tell, Ninja Forms has a field simple spam prevention but no blacklists. Gravity Forms has an old, not-updated, 3rd party plugin for a Gravity Forms Email Blacklist.

    In fact … the only contact form plugin I could find that actually uses WordPress’ built in blacklist would be Takayuki-san’s Contact Form 7.

    Let us protect ourselves from abuse.

  • What Are You Paying For With That License?

    What Are You Paying For With That License?

    My friend Andrea recently complained about confusion between support licenses and the GNU Public License:

    This lead to a WP Tavern post about how Commercial WordPress Product Descriptions Can Mislead Customers into Purchasing More Licenses Than Necessary.

    GPL Freedom to Use

    WordPress is licensed as GPLv2 and in the preamble it says, rather boldly:

    The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software–to make sure the software is free for all its users.

    The GPL is intended to be about freedom in the code you acquire (be that for free or for purchase) and your ability to reuse it as you see fit.

    If you really want to understand the GPL and WordPress, I highly recommend you pick up A Practical Guide to WordPress and the GPL. It’s actually written by a lawyer and it’s $25 for the ebook, which seems like a lot until you realize that to hire a lawyer to go over all this would be over $400.

    The point for this post is pretty simple though. The GPL gives the user of a theme or plugin in WordPress the freedom to use the code as many times as they want, on as many sites as they want, for as long as they want.

    The Restrictions of Products

    I purchased a theme called Utility Pro from Carrie Dils. I love that theme. On her site, the various licenses are restricted by support.

    • Support for 1 Site
    • Support for 5 Site
    • Support for 25 Site

    All licenses come with “1 Year Support and Upgrades” and the ‘pro’ version has these extras:

    • DesktopServer Blueprint (quick setup for DesktopServer users)
    • Developer’s Edition (Grunt, Sass, and more)
    • WP Development Workflow course

    What am I paying for here? Support for X sites for 1 year, and updates. It doesn’t say if the updates are for all my sites, but I’m going to assume that if I get support for 5 sites, I get upgrades for 5 sites. The thing here is that the GPL would allow me to install this theme on 250 sites if I wanted, and not only can Carrie do ‘nothing’ about it, but she wouldn’t care. She knows how the GPL works, after all.

    This still leaves me with a couple questions:

    If I pay for support for one site, what happens when I put my license on two sites?

    The best case scenario would be I’d get a message telling me that I’ve used up the sites available to my license, and I’ll have to remove one to add another. I’d add in a link to buy more licenses personally.

    What’s to stop me from lying about the site I’m having a problem on?

    Well … Nothing. And unless I need Carrie to log in, she’ll never know! Even if I did let her log in, I could show her a demo site and explain “I’m working on a new version of my site and this is my code…” Which is a totally legit reason to be testing out her code on an ‘unlicensed’ site.

    Can she stop me from copying the updated version to an unlicensed site?

    Nope! In fact, if I’m super smart, I’ll always leave an unmodified version on a site that gets updates, and then use that as my base to update anything I’ve forked. Oh, and my version is so forked, it’s practically not her theme anymore. But that’s okay. I renamed it from utility-pro to utility-jo (it’s funnier to me).

    Change What We Pay For

    I’m going to propose a different way to handle licenses.

    Instead of paying for X number of sites for support, pay for X years of support + updates + features.

    That’s right, I’m suggesting this price point:

    • $80 for 1 year of support and updates on unlimited sites.
    • $200 for 3 years of support and updates and those nifty things on unlimited sites.

    The word ‘unlimited’ may sound terrifying. If you allow unlimited usage, what’s to stop me from opening a million tickets for my million sites for help? Nothing. Not a damn thing. Except there’s nothing stopping me from doing that right now anyway except my own pathological honesty when it comes to respecting the work of others.

    The people who will abuse this system are, for the most part, the people who already are. All the license has to check is “Is this license valid? Yes? Push the update!” Now the theme developer will always be pushing her latest, most secure, code to everyone, which is a win all around. Oh yes, did you think about that? If everyone always gets an update, then everyone always has the ability to be secure.

    Now there is one big pain point here. What if I give someone else my license key?

    Well… What if you just give away updates anyway?

    Genesis does. No license check needed. I can take my Genesis core theme, install it on any site, and if it’s out of date, I get an update alert.

    If you buy their Pro Plus All-Theme Package, it works like this. You shell out $499.95 at first and then $99.95 per year for access to every single theme they make, plus 3rd party themes, plus theme updates, plus support.

    The thing is I never put in a license number to Genesis core or my children themes. Ever. The updates just happen, even if I don’t have an account. So what am I paying for with Genesis? I’m paying for the code base, the support, the advanced documentation, and the access to everything I may need to make my site damned awesome.

    But What About Big Changes?

    The game is a little different with plugins. See, a theme actually rarely changes. Once you’ve made a theme, it stays roughly the same except for library updates and security issues. A plugin though, they can add new features. So instead, let’s take a page from the Apple. The Apple App Store does not charge you for updates. They charge you for mini-transactions which, love ’em or hate ’em, actually work. If you need to charge for an update, you make a new version.

    Think about that for a second. In the App Store, version 4.1 is a minor release, but version 5.0 is a major release. This is not the same as WordPress’ semantic versions where 4.1 and 5.0 are both major releases, but 4.1.2 is not. When someone has a major release on the App Store, they retire their existing app and add a new one. The upgrade process mostly works. There’s always a weird period of time where things are odd.

    When we look at plugins, it’s a heck of a lot easier but you would have to use a license check to restrict updates. Using your licenses and the plugin headers, you can check “If someone’s on version 4.1 and I have released 5.0 and their license is active, push the update.” That’s the easy check. The fun check would be “If someone’s on version 4.1 and I’ve released both 5.0 and 4.2, but the license is not active, update them to 4.2 only.”

    Hold the phone. Why am I saying this? Because now you’re pushing security updates to your 4.x branch while not giving someone the new 5.x features. You win, because you’ve made the internet safer. The user wins, because they’re safer and possibly inclined to trust you more. Slip in a little alert to the top of the 4.x admin screens to say “There are new features in version 5.x. Upgrade now for 30% off!” and you’ll be converting sales!

    While someone could change their plugin headers to lie and say that their 4.x version is really a 5.x version, there’s no benefit to them to do this if you’re simultaneously requiring an active license.

    So What Does This Have to Do With GPL?

    Going back to what Andrea said, it makes it clear what your freedoms are.

    You can take code, install it where ever you want, and no one should actually give a damn. But by making updates easier, companies have to worry less about people wrangling, leaving them free to handle the egregious issues, like reselling.

    • The GPL allows me to take StudioPress themes and resell them if I want.
    • StudioPress has the right to delete my account and break my ability to update if I do that.

    Without touching on the hot-button topic of the ‘spirit’ of the GPL, we’re talking two separate things. The GPL allows me to do what I want with the code. The terms of use of StudioPress as a service, providing me with updates, is not bound by the GPL, nor should they be. But Andrea’s point, that our terms of use and licensing (billing) structure can confuse people with regards to our GPL freedoms, is totally valid.

    The onus is on the seller, not the buyer, to explain the difference between the GPL freedoms (do what you want, basically), with the Terms of Use freedoms. GPL doesn’t give you the freedom to defraud a company, for example. If they chose to cancel your account because you resold their product, that’s their right. Your freedom to resell is not impinged by the GPL. You can go for it. But they aren’t obligated to give you free updates anymore if that’s the case, and they can probably slap you with a c&d order.

    The point is the GPL and its freedoms can live side by side with making a profit. We just have to be honest about what we’re selling. We’re not selling the code at all, we’re selling the service.