SocialSharePrivacy and WordPress

March 15, 2014 12:32 pm

As I mentioned previously ("Your (the Reader's) Privacy"), I've set up this blog with the SocialSharePrivacy toolkit to allow these posts to have convenient "share" buttons, but without allowing those services to track your movements involuntarily.

And, as I mentioned, using that toolkit isn't entirely friendly yet.

This is what I did to get it working properly:

I downloaded the toolkit and placed it in the "js" subdirectory of the child theme I'm using to customize the standard "twentyfourteen" theme.

Directory structure:

/path/to/wordpress/wp-content/themes/twentyfourteenchild/js/socialshareprivacy

In that directory are the files for the toolkit:

.../socialshareprivacy/css/jquery.socialshareprivacy.min.css
.../socialshareprivacy/images/[whole bunch of images here]
.../socialshareprivacy/jquery.socialshareprivacy.min.js

I then updated my theme's functions.php file to load the toolkit:

add_action( 'wp_enqueue_scripts', 'social_share_privacy' );
function social_share_privacy() {
        wp_register_script(
                'social-share-privacy',
                get_stylesheet_directory_uri() . '/js/socialshareprivacy/jquery.socialshareprivacy.min.js',
                array('jquery'),
                '14.02.04',
                true
        );
        wp_enqueue_script('social-share-privacy');

        wp_register_script(
                'social-share-privacy-init',
                get_stylesheet_directory_uri() . '/js/socialshareprivacy/socialshareprivacy-init.js',
                array('jquery', 'social-share-privacy'),
                '14.02.04',
                true
        );
        wp_enqueue_script('social-share-privacy-init');
        wp_localize_script('social-share-privacy-init', 'socialshareprivacyinit', array(
                'title' => wp_title("", false),
                'image' => site_url() . '/apple-touch-icon-114x114-precomposed.png',
                'path_prefix' => get_stylesheet_directory_uri() . '/js/socialshareprivacy/',
                'css_path' => 'css/jquery.socialshareprivacy.min.css'
        ));
}

This enqueues two scripts to load: 'social-share-privacy' and 'social-share-privacy-init' both of which depend on jQuery already being loaded.  I use dates for version numbers because it's easy to keep track of how old things are.

After enqueuing the scripts I also make a call to "wp_localize_script" for the 'social-share-privacy-init' script.  This allows me to pass in data that needs to be calculated by WordPress to function properly (things like the directory location of the socialshareprivacy files ["path_prefix" and "css_path"]).

Now we need to actually create that 'social-share-privacy-init' script which lives at:

.../socialshareprivacy/socialshareprivacy-init.js

I wasn't able to get the documented global settings variable (combined with auto-wiring) to work correctly so instead I did things manually:

jQuery(document).ready(function($) {
        socialshareprivacyinit.info_link_target = '_blank';
        socialshareprivacyinit.order = ['gplus', 'facebook', 'pinterest', 'mail'];
  socialshareprivacyinit.services = {
                gplus:{status:true},
                facebook:{status:true},
                mail:{status:true, body:function(options, uri, settings){return "Thought you might find this interesting: " + uri;}},
                pinterest:{status:true},
                twitter:{status:false},
                buffer:{status:false},
                delicious:{status:false},
                disqus:{status:false},
                flattr:{status:false},
                hackernews:{status:false},
                linkedin:{status:false},
                reddit:{status:false},
                stumbleupon:{status:false},
                tumblr:{status:false},
                xing:{status:false},
                fbshare:{status:false}
        };
        $('div.share').socialSharePrivacy(socialshareprivacyinit);
});

Notice that I only configure gplus, facebook, pinterest, and mail.  Also, notice that I'm assigning to an assumed existing javascript variable called "socialshareprivacyinit" this is the variable that WordPress will create and populate from the wp_localize_script call we made and will be available before socialshareprivacy-init.js is executed.

We just finish populating the object and then wire up the share divs.

It appears that you have to manually set every service you don't want to "false" as the default seems to be "true" (which I think should be switched).

The final piece is to actually put divs on the page that will get wired up; in this case, divs that will match the $('div.share') selector.

In each of the layout pages you use (content.php, content-image.php, content-gallery.php, content-video.php...) add the following code just after the closing tag for the <div class="entry-meta"> div, so it's still inside the <header></header> tags:

</div><!-- .entry-meta -->
                <div class="share" data-uri="<?php echo esc_attr(get_permalink()); ?>" data-title="<?php echo esc_attr(the_title(false))?>"></div>
        </header><!-- .entry-header -->

In that tag I use the "data-*" attributes to pass useful information to the socialSharePrivacy code.  I set "data-uri" to the post's permalink and I set "data-title" to the post's title.  SocialSharePrivacy will use these values when a user clicks a "share" button to pre-populate that share.

I believe that was everything.  So you should be all set at this point.

Your (The Reader's) Privacy

February 6, 2014 4:02 pm

As I mentioned in an earlier post, one of my goals in moving to a self-hosted blogging platform was to protect the privacy of my readers.  WordPress, by default, has a few tendrils that slither out across the Internet and violate that privacy letting any number of 3rd-party websites track your movements across the web.

By default it: loads special fonts from Google's servers; uses the Gravatar service to show an image next to your name if you leave a comment; sends your email address and other data off to a third party to run heuristics as part of the bundled anti-spam tool.

I've excised my installation of these issues.  When you load this blog I do not enable any other services to track your movements.

Instead of using the bundled anti-spam tool, Akismet, which sends your data to a 3rd party, I'm using some less powerful, but locally controlled, methods that include things like a simple checkbox on the comment page to confirm you're not a spam bot.  These more primitive methods of fighting spam would probably not work for a high traffic site (thus high value target), but will probably be just fine for us.

You may have noticed the social media sharing buttons at the top of each post.  Normally this would allow each of those services to track your visit to this site.  But these buttons are a little different than what you're probably used to.

These buttons require two clicks to work.  One click activates the button and connects to the associated social media service (which in turns means that they are now able to track your visit to this site).  A second click does the normally expected behavior for sharing something to that service.  This privacy-protecting method of enabling social media sharing is powered by the Social Share Privacy project.  The project is still a little rough around the edges, but it does work (though it took me longer than I expected to configure).

The final piece that I'm working on is migrating all the old media to my server.  Currently the pictures and videos in old posts are still hosted on Blogger's (a.k.a., Google's) servers.  I'm slowly working through bringing those files to my server so that you don't need to interact with any 3rd party to visit this blog.

Also, if you really like, you're free to visit this site using an encrypted (https) connection.  However, at the moment I'm still using a self-signed certificate (so your browser will warn you that it's not secure, just add an exception).  It's a lie to say it's not secure, what it really means is that I just haven't paid one of the certificate companies to verify my identity.  The connection will still be encrypted as any other.  If you really care I'd be happy to take your phone call and verify that the certificate your browser sees is in fact the one I created.

Criteria Aggregator: Dynamic Criteria Queries in Grails

February 5, 2014 2:42 pm

A few years ago I wrote about dynamically building criteria queries in Grails.  Well the use case we had disappeared and I didn't do much of anything with dynamic criteria queries until recently.  As I've mentioned in an update on that post, you probably just want to use DetachedCriteria now instead of taking this route, but DetachedCriteria don't support the same range of functionality that normal criteria queries do.

I ran into this limitation when I needed to use subqueries which DetachedCriteria do not support but standard criteria queries do.  After spending hours trying to work around the limitation and still use DetachedCriteria I decided to give up and create a nice way of using the dynamic criteria queries based on my past experience, the comments on that post, and my greatly enlarged knowledge of Grails.

So I created a nice clean class called "CriteriaAggregator":

package org.example.package
import grails.orm.HibernateCriteriaBuilder

// Aggregate query criteria for a Domain Class
// Example: def qa = new CriteriaAggregator(MyDomainClass)
//    qa.addCriteria { idEq(12345L) }
//     def results = qa.get()
public class CriteriaAggregator {
    private Class forClass
    private List<Closure> criteriaClosures

    // forClass should be a Grails DomainClass; but since Grails injects rather than inherits I can't specify the type better than "Class"
    public CriteriaAggregator(Class forClass) {this.forClass = forClass; criteriaClosures = new ArrayList<Closure>(10)}

    // criteriaClosure is the exact same type of closure you'd pass to DomainClass.withCriteria(criteriaClosure)
    public void addCriteria(Closure criteriaClosure) {criteriaClosures << criteriaClosure}

    public long count() {return runQuery('get') {projections {rowCount()}}}
    public def get(Closure additionalCriteria=null) {return runQuery('get', additionalCriteria)} // Query must return only a single row
    public def list(Closure additionalCriteria=null) {return runQuery('list', additionalCriteria)}

    private def runQuery(String method, Closure additionalCriteria=null) {
        HibernateCriteriaBuilder criteriaBuilder = forClass.createCriteria()
        def critClosures = criteriaClosures // Bizarre that criteriaClosures won't evaluate properly inside the "$method" closure, but it won't so this works around that issue
        criteriaBuilder."$method" {
            critClosures.each{closure -> closure.delegate = criteriaBuilder; closure()}
            if (additionalCriteria) {additionalCriteria.delegate = criteriaBuilder; additionalCriteria()}
        }
    }
}

This wraps and defers the creation of the normal criteria builder allowing you to build up the criteria dynamically and execute it when desired.

Suppose you had a Customer domain class with many Orders.  Orders have a date, paymentMethod, and totalPrice.

def customerQueryAggregator = new CriteriaAggregator(Customer)
customerQueryAggregator.addCriteria {
  orders {
    def now = new Date()
    between('date', now-7, now)
  }
}
customerQueryAggregator.addCriteria {
  orders {
    eq('paymentMethod', 'cash')
  }
}

def numCustomersInPastWeekPayingCash = customerQueryAggregator.count()
println numCustomersInPastWeekPayingCash

def highRollersInPastWeek = customerQueryAggregator.list{
  orders {
    gt('totalPrice', 50000)
  }
}
println highRollersInPastWeek

This is obviously a trivial example where you don't need to use the aggregator.  But it's just to illustrate the usage.  I'm using it to build up a query based on a set of options provided by a caller as part of a reusable and flexible service.

Using this I can handle subqueries like so:

import grails.gorm.DetachedCriteria as GrailsDetachedCriteria
import org.hibernate.criterion.Subqueries
import grails.orm.HibernateCriteriaBuilder

def customerQueryAggregator = new CriteriaAggregator(Customer)

GrailsDetachedCriteria largeOrderSubquery = new GrailsDetachedCriteria(Order).build {
  eqProperty 'order.id', 'this.id'
  gt 'totalPrice', 50000
  projections {property 'id'}
}

customerQueryAggregator.addCriteria {
  add(Subqueries.exists(HibernateCriteriaBuilder.getHibernateDetachedCriteria(largeOrderSubquery)))
}

Update April 4, 2014:

I ended up having some trouble with the proper definition of table aliases (it always wanted to call "order" "this" even though we already had a "this") while handling the GrailsDetachedCriteria subqueries.  To work around it I ended up dropping back to straight Hibernate, but it still works with the CriteriaAggregator.

import org.hibernate.criterion.DetachedCriteria as HibernateDetachedCriteria
import org.hibernate.criterion.Restrictions
import org.hibernate.criterion.Disjunction
import org.hibernate.criterion.Subqueries
import org.hibernate.criterion.Projections

def customerQueryAggregator = new CriteriaAggregator(Customer)

HibnerateDetachedCriteria largeOrderSubquery = new HibernateDetachedCriteria.forClass(Order.class, 'order')
largeOrderSubquery.add(Restrictions.eqProperty('order.id', 'this.id'))
largeOrderSubquery.add(Restrictions.gt('order.totalPrice', 50000))
largeOrderSubquery.setProjection(Projections.property('order.id'))

customerQueryAggregator.addCriteria {
  add(Subqueries.exists(largeOrderSubquery))
}

Self-Hosted Blogging Software Options

January 27, 2014 2:56 am

I spent some time this weekend exploring self-hosted blogging software alternatives to Blogger.  I set up a virtual machine and took a look at Ghost, Habari, Chyrp, and WordPress.  I also looked in to, though didn't install or play with, AnchorCMS, Couch CMS, Wolf CMS, Bolt, Monstra, and Pyro CMS.

I was interested in some of the lesser known "next-gen" platforms that seek to be lighter weight than WordPress.  They also have some new approaches to how blogging software should function which I found intriguing.

I didn't get very far with Ghost.  Ghost is built on node.js; which I hadn't used before and it took me an inordinate amount of time to get the thing running just to play with.  And once I finally did get it running, I quickly discovered it does not have multi-user support yet (slated for summer 2014).  Since a Blogger replacement would need to allow both Jess and I to write posts this was a deal breaker.

I tried Habari next and it was less painful to get installed.  Unfortunately, it also is missing some needed functionality.  And this part is about to become a very common theme: video uploading and playing.  I was able to upload a video to Habari's media "silo" (as they call it) quite easily, but I had to write the video player HTML myself.  For just me that's not a big deal, but that's not going to work for Jess.  I probably could try to write a plugin for Habari to do the video player code, but I don't like writing PHP and don't feel like it.  So I kept looking.

Chyrp is similar to Habari in many ways.  I like the simple, clean aesthetics (shared by Habari and Ghost).  But again, video uploading and playing was not a working-out-of-the-box experience and there didn't seem to be any plugins providing the functionality.

The thinking of all of these lightweight blogging platforms is that no one wants to host their own videos so rather than support video uploading and players they provide rich support for easily embedding YouTube videos (and other services).  Which, okay, I understand the bandwidth issues; but it also somewhat defeats the purpose of self-hosting the blog if you're still going to rely on a 3rd party.

For me the point of self-hosting is that it is entirely under my control on my systems doing what I say.  And since I doubt any of our little videos of Heather will ever draw any meaningful crowd I'm not particularly concerned with the bandwidth usage.

Hopefully with a few more development cycles Ghost, Habari, and Chyrp will all reach a feature level that would enable me to use them if I wanted to.

The other ones I listed but didn't install all looked like they'd end up in the same boat with lack of video uploading and playing readily available.  Some had demo installations I played with that made it clear they were less focused on blogging than I was (CMSs, or Content Management Systems, can really cover a lot of territory).

So I finally broke down and installed WordPress.  I had been avoiding WordPress mainly because WordPress has evolved much more into a general purpose CMS than I need.  But it does support video uploading and embedding out-of-the-box and does a decent job of media management.  However, it also comes with a bunch of stuff I don't need and I will need to spend some time cleaning out the stuff I don't want and configuring the stuff I do want.

With that decision made I'll need to install WordPress to my actual server instead of my sandbox environment.  Then I'll need to do the configuration and customization.  After that I should be able to import all of our existing blog posts and then switch over this blog to use that new system.  But I don't currently have a timeline for that.

Reducing Tracking

December 13, 2013 6:52 pm

As a software developer and particularly a web developer it is my responsibility to set a good example and try to be a force for good.

As of today, none of my sites use Google Analytics anymore.  The tipping point, aside from improving general web privacy, was that the NSA is supposedly hijacking the Google tracking data for their own purposes.

While I don't really need any analytics, I do get curious as to what kind of traffic my websites are getting.  So I instead stood up my own instance of Piwik.  This is a self-hosted analytics solution.  Now my websites simply report to another one of my systems when they're accessed (instead of Google).  And I know that my Piwik installation respects the "Do Not Track" setting you can use in your browser (I've tested it myself).  Also, my Piwik analytics won't track you all over the Internet.

I realize this may seem a little silly coming from a blog hosted on Google's servers via Blogger, but I'm also working on that.  I'm looking into using Habari and migrating this blog to be self-hosted as well.  Given that Blogger seems to be a dead product in Google's eyes (little to no updates or changes in many, many months), it's probably better to get off it anyway as it may get shut down at some point.  However, I'll probably wait a few months until the more stabilized Habari 1.0 release is finished.  Such a move will probably coincide with standing up a family cloud using ArkOS and OwnCloud.

It's not that I'm particularly paranoid, but the various pieces of software are reaching a point where for someone like me, it's not particularly difficult or burdensome to self-host things.  So I might as well do it and encourage an Internet model closer to its original design: interacting, decentralized systems.  A model which is harder for any one organization (government or otherwise) to infiltrate, break, or commandeer.