JSON-RPC 2.0 Implementation

December 5th, 2009

Here’s another quick braindump post. A custom JSON-RPC library I wrote for a project at my last job. It should follow the JSON-RPC 2.0 Spec pretty closely. It does require the jQuery library as well.

You can pretty much ignore the MIKU references. Basically it’s just a way of namespacing objects to make them globally available. Try reading up on the YUI library for more information.

As always, feel free to post with any questions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
var MIKU;
 
/*
 * Method to allow namespacing of new objects within MIKU;
 *
 * Example:
 * MIKU.namespace('objectname');
 * MIKU.objectname = function() { return {'...'} }
 *
 */
MIKU = function() {
  return {
    namespace: function(name) {
      try {
        if (MIKU[name]) {
          throw 'Namespace exists.';
        }
        return {}
      }
      catch (e) {
        this.throwError(e);
      }
    }
  }
}();
 
MIKU.namespace('throwError');
 
/*
 * Method to catch MIKU errors
 */
MIKU.throwError = function(e) {
  if (console) {
    console.error((e.message || 'MikuError:'), e);
  }
};
 
/*
 * Init the JsonRpc namespace into the MIKU object.
 */
MIKU.namespace('JsonRpc');
 
/*
 * Miku Json-RPC Implementation
 */
 
MIKU.JsonRpc = function() {
  var _url = 'json-rpc/call';
  var _timeout = false;
  var _requests = [];
  var _responses = [];
  var _callbacks = {};
  var _requestId = 0;
  var _failures = 0;
 
  function _send() {
    _request();
    _t = false;
    _requests = [];
  }
 
  function _request() {
    try {
      if (!_url) {
        throw('undefined post url.');
      }
      $.ajax({
        type: 'POST',
        url: _url,
        data: ({
          request: JSON.stringify(_requests)
        }),
        dataFilter: function(data, type) {
          //check for php errors
          try {
            return JSON.parse(data);
          }
          catch (e) {
            var phpError = /^.*?(Error|Warning|Notice).*?\:\s*(.*?) in .*?(\/[0-9A-Za-z\/\.\-\_\ ]+).* on line .*?([0-9]+).*$/i;
            var lines = data.split(/\n/g);
            $.each(lines, function() {
              var error = this.match(phpError);
              if (error) {
                MIKU.throwError({
                  message: error[1] + ': ' + error[2],
                  file: error[3],
                  line: error[4]
                });
              }
            });
          }
        },
        success: function(data) {
          _success(data);
        },
        error: function(XMLHttpRequest, textStatus, errorThrown) {
          // retry after connection failures
          if (XMLHttpRequest.status != '200') {
            _failures ++;
            if (_failures < 3) {
              _request();
              return;
            }
          }
 
          // give up and throw errors...
          MIKU.throwError([XMLHttpRequest, textStatus, errorThrown]);
        }
      });
    }
    catch(e) {
      MIKU.throwError(e);
    }
  }
 
  function _success(response) {
    $.each(jQuery.makeArray(response), function() {
      try {
        if (this.error) {
          // check for error
          throw(this.error);
        }
        else if(this.result) {
          // trigger callback
          var callback = _callbacks[this.id];
          callback(this.result);
        }
      }
      catch(e) {
        MIKU.throwError(e);
      }
    });
 
    // reset callbacks
    _callbacks = {};
  }
 
  function _genId() {
    return ++_requestId;
  }
 
  return {
    version: '2.0',
    delay: 10,
    setUrl: function(url) {
      _url = url;
    },
    call: function(args) {
      var id = _genId();
 
      var request = {
        jsonrpc: this.version,
        method: args.method,
        params: args.params,
        id: id
      }
 
      _requests.push(request);
      _callbacks[id] = args.onSuccess;
 
      if (_timeout) {
        clearTimeout(_timeout);
      }
      _timeout = setTimeout(_send, this.delay);
 
      return request;
    }
  }
}();
 
/*
 * Testing Below
 */
 
$(document).ready(function() {
  var rpc = MIKU.JsonRpc;
 
/*
  rpc.call({
    method: 'System.getTitle',
    params: [
      'this is a title'
    ],
    onSuccess: function() {
      console.log('w00t');
    }
  });
*/
});

Writing a custom Wordpress multi-instance widget

December 3rd, 2009

wplogoblue-hoz-rgb

While working on a soon-to-be finished Wordpress theme for a client, I ran into a situation where it would be useful to create a custom widget that they could use to organize content on their site. Basically, they wanted to be able to select a random post or page and display some associated meta data. Essentially a custom image and content teaser. They were going to be doing this several times throughout the site, but in slightly different configurations. A post here, a page there. It seemed silly to hard-code these features. Using a widget would allow them to swap them out for a Twitter stream, or an RSS news feed in the future.

Making multi-instance widgets in Wordpress 2.8+ couldn’t be easier. Here is a good example to start with from the Wordpress codex.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
class My_Widget extends WP_Widget {
	function My_Widget() {
		// widget actual processes
	}
 
	function widget($args, $instance) {
		// outputs the content of the widget
	}
 
	function update($new_instance, $old_instance) {
		// processes widget options to be saved
	}
 
	function form($instance) {
		// outputs the options form on admin
	}
}
register_widget('My_Widget');
?>

And here is my finished widget (evolved from the above example).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
<?php
/*
 * Custom mini-post widget
 */
class FGX_MiniPost_Widget extends WP_Widget {
	function FGX_MiniPost_Widget() {
		// widget actual processes
		parent::WP_Widget(false, $name = 'Floragenex MiniPost', array(
			'description' => 'Display a teaser for a post or a page.'
		));
	}
 
	function widget($args, $instance) {
		// outputs the content of the widget
			global $post;
 
			extract( $args );			
			$type = $instance['type'];
			$include = (!empty($instance['include']) ? explode(',', $instance['include']) : '');
			$category = (is_numeric($instance['category']) ? (int)$instance['category'] : '');
 
                        // Set up query for posts with the provided filters
			query_posts(array(
				'post_type' => $type,
				'post__in' => $include,
				'post__not_in' => array($post->ID),
				'cat' => $category,
				'post_status' => 'publish',
				'meta_key' => 'teaser_value',
				'meta_value' => '',
				'meta_compare' => '!=',
				'orderby' => 'rand',
				'posts_per_page' => '1'
			));
 
			echo $before_widget;
 
                        // Output widget, if a post exists that matches our query
			if ( have_posts() ) :
				while ( have_posts() ) : the_post();
					$post_meta = get_post_custom($post->ID);
					echo (!empty($post_meta['image_value'][0]) ? '<a href="' . get_page_link() . '">' .
							 '<img alt="image" src="' . get_bloginfo('template_url') . '/scripts/timthumb.php?src=' . htmlentities($post_meta['image_value'][0]) . '&h=62&w=180&zc=1" />' .
							 '</a>' : '') .
							 '<h3><a href="' . get_page_link() . '">' . get_the_title() . '</a></h3>' .
							 '<p>' . htmlentities($post_meta['teaser_value'][0]) . '</p>' .
							 '<p><a class="learn-more" href="' . get_page_link() . '">learn more</a></p>';
				endwhile;
			else:
				echo '<p>No posts found.</p>';
			endif;
 
                        // Very important to reset the query here.
			wp_reset_query();
 
			echo $after_widget;
	}
 
	function update($new_instance, $old_instance) {
		// processes widget options to be saved
		return $new_instance;
	}
 
	function form($instance) {
		// outputs the options form on admin		
		$type = esc_attr($instance['type']);
		$include = esc_attr($instance['include']);
		$category = esc_attr($instance['category']);
 
                // Get the existing categories and build a simple select dropdown for the user.
		$categories = get_categories();
 
		$cat_options = array();
		$cat_options[] = '<option value="BLANK">Select one...</option>';
 
		foreach ($categories as $cat) {
			$selected = $category === $cat->cat_ID ? ' selected="selected"' : '';
			$cat_options[] = '<option value="' . $cat->cat_ID .'"' . $selected . '>' . $cat->name . '</option>';
		}
 
		?>
			<p>
				<label for="<?php echo $this->get_field_id('type'); ?>">
					<?php _e('Content type:'); ?>
				</label>
				<select id="<?php echo $this->get_field_id('type'); ?>" class="widefat" name="<?php echo $this->get_field_name('type'); ?>">
					<option value="post"<?php echo ($type === 'post' ? ' selected="selected"' : ''); ?>>Post</option>
					<option value="page"<?php echo ($type === 'page' ? ' selected="selected"' : ''); ?>>Page</option>
				</select>
			</p>
			<p>
				<label for="<?php echo $this->get_field_id('include'); ?>">
					<?php _e('Include post IDs (optional):'); ?>
				</label>
				<input id="<?php echo $this->get_field_id('include'); ?>" class="widefat" type="text" name="<?php echo $this->get_field_name('include'); ?>" value="<?php echo $include; ?>" />
			</p>
			<p>
				<label for="<?php echo $this->get_field_id('category'); ?>">
					<?php _e('Include category (optional):'); ?>
				</label>
				<select id="<?php echo $this->get_field_id('category'); ?>" class="widefat" name="<?php echo $this->get_field_name('category'); ?>">
					<?php echo implode('', $cat_options); ?>
				</select>
			</p>				
		<?php
	}
}
 
// register widget
register_widget('FGX_MiniPost_Widget');
?>

Just paste this code in your theme’s functions.php file and the widget should appear under your available widgets.

This was a quick braindump post, so please feel free to post with any specific questions.

Create and list new tickets through the Assembla API

October 23rd, 2009

A while back I created a PHP library for interacting with the Assembla Ticket API. I thought it might be useful to someone, so here it is.

You can use this library to create and list tickets that exist for one of your Assembla spaces. It’s useful for integrating bug reporting and other feedback features into an application that you may be developing. This code is very much a work in progress, so please let me know if you encounter any bugs or have any new feature requests.

You can view and download the code on GitHub:

github-logo

Moving your blog off Blogger to a self-hosted Wordpress site

October 19th, 2009

wplogoblue-hoz-rgb

I have a client who absolutely loves Wordpress, but one of her long-time blogs has been hosted at Blogger since the dawn of time. Luckily, thanks to some persistence and a handy Wordpress plugin, she’s now happily blogging with Wordpress.

Step 1: Set up and configure Wordpress
First thing to do is get yourself a self-hosted Wordpress install. Many hosts these days even offer easy “one-click” installs of Wordpress through their control panels. Otherwise check out the Wordpress docs for help getting started.

If you need hosting recommendations shoot me an email or leave a comment. I’d be glad to recommend some of my favorites.

Step 2: Import your Blogger content
Once you’ve got Wordpress installed and hooked into your database, it’s time to import your old Blogger content. Log into the Wordpress admin panel and go to “Tools->Import->Blogger.” Follow the onscreen directions to complete this step.

Double check the imported posts and pages to make sure that all your content is there. Pay special attention to images in posts.

Step 3: Install plugins
Now, we all know you’ve developed a huge following on your blog, at least, that’s what you tell your friends. You don’t want all your incoming links to go dark once you move off Blogger, ideally you’d like them to point to the same posts on your new Wordpress site. The solution here is to use a nifty plugin called “wp-maintain-blogger-permalinks.” Following the directions at the link below will make this happen:

http://justinsomnia.org/2006/10/maintain-permalinks-moving-from-blogger-to-wordpress/

Now, if you don’t want to mess around with your .htaccess file to go “.html-less.” You can simply append “.html” to the end of your permalink structure. In Wordpress go to “Settings->Permalinks” and check the “Custom Structure” radio button. Modify this structure by removing the trailing “slash” and adding “.html” to the end. Don’t forget to save your changes!

Step 4: Update the DNS settings for your custom domain
It took me a while to find out how to update the DNS settings for a custom domain bought through the Blogger interface. It’s actually pretty easy. Just go to the following URL:

http://www.google.com/a/cpanel/yourdomainhere.com/

Log in to Google Apps using your Blogger ID and password. Then navigate to “Domain Settings ->Domain Names” and click on the “Advanced DNS settings” link.

google-blogger-custom-domain-dns-settings

Step 5: Get your custom domain transferred to a new registrar

You can certainly leave your domain hosted with Google’s affiliate registrar, but if you are hosting other domains elsewhere, it may make sense to consolidate your resources. To initiate the transfer process, you’ll need to obtain your domain’s authorization code from Google’s affiliate registrar.

If you’d like to transfer your domain to another domain registrar, you’ll need your domain’s authorization code. To obtain your authorization code, sign in to the DNS console of your domain host. Instructions vary by domain host.

https://www.google.com/support/a/bin/answer.py?hl=en&answer=112049

Your domain will either be registered at ENOM or GoDaddy through Google. The Google Apps. panel will tell you where your domain is registered. You’ll need to contact the registrar directly to obtain the transfer authorization code. ENOM can be contacted at GoogleClients@enom.com or by phone at 425-974-4623. Although you may need to pester them to get a response.

Good luck and let me know if you have any advice I may have overlooked!

Elisabeth Diaz-Herrera | Dec. 19, 1934 – Sept. 7, 2009

September 15th, 2009

Elisabeth Diaz-Herrera

This is my grandmother Bettina. She passed away last week on labor day morning.

Technically she was my step-grandmother, as my mom’s birth mother Ellen passed away when my mom was a teenager 7. So I’ve always known Bettina to be my grandmother.

Bettina was a fantastic lady, who, I’m sorry to say, I was just beginning to get to know. She was always cheerful, even through the last few years of her life, when she was very sick.

Whenever I spoke to her on the phone she would happily answer “Hola, hola!”, which always made me smile. She always had something wonderful to say about how I was doing, even when I was feeling at the end of my rope.

I remember taking walks with her and my grandfather in the woods behind her house when I was little. There was this fantastic stone wall way out behind their house that opened up into a field. They had a huge basement, with all sorts of exciting things to discover. I used to ride around on this long wooden car, that was sort of like a bench with wheels and handlebars. I’d ride around their basement and peruse my uncle’s old toys – everything from a box of old electric trains, to long abandoned remote-controlled airplanes.

Bettina understood the importance of computers and new technology, even if it advanced faster than she could. She gave my family our first computer, an old 486 running MS-DOS, which sat idle for months with no monitor. Finally, Christmas morning, I woke up to find a big CRT under the tree, courtesy of my uncle Mike. That computer was undoubtedly one of the most important gifts I ever received.

I regret that I wasn’t able to see Bettina one more time before she left us. While she had been sick for quite some time, her passing seemed sudden and unexpected. I had lots of things I hoped to tell her, like how I had discovered Mate with my friend Josh, a beverage she drank almost every morning. I also hoped she would live to see me graduate from college, something that would have been very difficult for me financially, if it weren’t for her and my grandfather’s support.

I’ve included a copy of the obituary below, written collectively by my family, which will go out to the local paper sometime this week.

Thank you so much for all your love and care Bettina. We’ll miss you.

Bettina was born December 19, 1934 in Buenos Aries, Argentina to Ricardo and Elizabeth Diaz Herrera. Bettina led a varied and unusual life as a young woman in Buenos Aries and on the family ranch in Argentina. It was here, on the family ranch, that she developed a deep appreciation and love of animals and nature that continued throughout her life. She became an adept equestrian and learned to pilot bush planes as a young woman on the Pampas of Argentina.

Bettina came to the United States in 1966 were she pursued graduate studies in macroeconomics and business at the University of Wisconsin, Madison and at Stanford University. She worked with women and minorities in small business development and consulting.

Bettina was very active in and served as President of the League of Women Voters of Dane County. After growing up under repressive regimes in Argentina she truly valued the democratic process and freedom of speech she experienced here. She became a citizen of the United States in 1983.

Elisabeth is survived by her husband: Don Kanel, her children: Mike Kanel and Elizabeth Kanel, her grandchildren: Avery and William Kanel and Tristan and Galen Waddington. Many friends and family in Argentina and the United States also survive Elisabeth. Her own reflections on her life included the following; “ For myself, living in this world has been very interesting, very demanding and very rewarding. I think that I would do it all again (given the advantage of this practice run!)”. We will all miss her.

Elisabeth Diaz-Herrera

New Post on Open-Source in Academia [EMU Marketing]

September 5th, 2009

Just wanted to direct your attention to my new post over at EMU Marketing on open-source software in academia:

http://emumarketing.uoregon.edu/tristan/2009/09/04/coding-open-the-benefits-of-free-and-open-source-software-in-academia/

Check it out and leave us a comment!

Virtuemart – Shipping Method List – Invalid Markup

August 19th, 2009

virtuemart-shipping-method-list

A user recently reported an odd problem on a Virtuemart site I maintain. They weren’t able to properly select the bottom two shipping methods listed (both USPS options).

I took a gander at the cart and quickly found the problem. The UPS shipping module was outputting the radio buttons wrapped in a <label> tag. Problem was, it wasn’t doing this consistently, and the last UPS shipping option had an open tag that was encompassing all the remaining radio buttons. This meant that anywhere you clicked in that region would result in the last UPS option being selected.

Now for some reason the USPS shipping module didn’t output labels at all. So I figured the most consistent solution would be to simply prevent the UPS module from outputting those <label> tags.

The method you’re looking for is ups::list_rates(), which is in the ./administrator/components/com_virtuemart/classes/shipping/ups.php file.

You can see the changes I made below on lines 362 and 384. The lines that are commented out are the original ones.

356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
$shipping_rate_id = urlencode(__CLASS__."|UPS|".$value['ServiceName']."|".$charge);
$checked = (@$d["shipping_rate_id"] == $value) ? "checked=\"checked\"" : "";
if (count($shipment) == 1 ) {
        $checked = "checked=\"checked\"";
}
//$html .= '<label for="'.$shipping_rate_id.'">'."\n<input type=\"radio\" name=\"shipping_rate_id\" $checked value=\"$shipping_rate_id\" id=\"$shipping_rate_id\" />\n";
$html .= "<input type=\"radio\" name=\"shipping_rate_id\" $checked value=\"$shipping_rate_id\" id=\"$shipping_rate_id\" />\n";
 
$_SESSION[$shipping_rate_id] = 1;
 
$html .= $value['ServiceName'].' ';
$html .= "<strong>(".$value['TransportationCharges'].")</strong>";
if (DEBUG) {
        $html .= " - ".$VM_LANG->_('PHPSHOP_PRODUCT_FORM_WEIGHT').": ".$order_weight." ". $weight_measure.
        ", ".$VM_LANG->_('PHPSHOP_RATE_FORM_VALUE').": [[".$charge_unrated."(".$fsc_rate.")]+".UPS_HANDLING_FEE."](".$taxrate.")]";
}
// DELIVERY QUOTE
if (Show_Delivery_Days_Quote == 1) {
        if( !empty($value['GuaranteedDaysToDelivery'])) {
                $html .= "&nbsp;&nbsp;-&nbsp;&nbsp;".$value['GuaranteedDaysToDelivery']." ".$VM_LANG->_('PHPSHOP_UPS_SHIPPING_GUARANTEED_DAYS');
        }
}
if (Show_Delivery_ETA_Quote == 1) {
        if( !empty($value['ScheduledDeliveryTime'])) {
                $html .= "&nbsp;(ETA:&nbsp;".$value['ScheduledDeliveryTime'].")";
        }
}
if (Show_Delivery_Warning == 1 && !empty($value['RatedShipmentWarning'])) {
        //$html .= "</label><br/>\n&nbsp;&nbsp;&nbsp;*&nbsp;<em>".$value['RatedShipmentWarning']."</em>\n";
        $html .= "<br/>\n&nbsp;&nbsp;&nbsp;*&nbsp;<em>".$value['RatedShipmentWarning']."</em>\n";
}
$html .= "<br />\n";

14 Social Media Icon Sets

August 16th, 2009

As many of you know, there are some crappy icon sets out there. Here are a few that I deem acceptable for general consumption. Enjoy!

Aquaticus Social icons
AquaticusSocialIcons

High Gloss Web 2.0 Icons
High Gloss Web Icons

108 Free Matte White Square Social Networking Icons
108 Free Matte White Square Social Networking Icons

154 Matte Black Social Media Icons
166__608x608_01-matte-black-social-media-icons-webtreats-preview

Web 2 Icons
web2_icons

Webtoolkit Icon Set Vol. 1
Webtoolkit Icon Set Vol. 1

Free Vectors – 20 Free Social Bookmarking Icons
Free Vectors - 20 Free Social Bookmarking Icons

Social Media Icons Pack
Social Media Icons Pack

34 (ou plus) boutons gratuits pour votre site
34 (ou plus) boutons gratuits pour votre site

Socialize Icons
Socialize Icons

Web social icons
Web social icons

Social Post Stamps: Free icon set
Social Post Stamps: Free icon set

Polaroid icon set
Polaroid icon set

Set of social icons no.3
Set of social icons no.3

Bonus: Twitm Icon (very slick app-icon)
Twitm Icon

New Skins – EMU Marketing

July 26th, 2009

emu-marketing-destyle-theme

I decided it was time to freshen up the EMU Marketing blog a bit, so after a little hunting, I swapped out the old theme for the fantastic (and free) deStyle Wordpress theme – from ThemeShift.

I was pleasantly surprised by the quality of the deStyle theme. It features a custom theme options page; integrated Twitter and Flickr modules; ad-support; author bios; and really nice looking markup. The whole theme is just nicely polished.

ThemeShift has two other premium themes, deGusto and deCasa.

EMU Marketing is where I work. We do web development and and print design for student groups and University services housed within the Erb Memorial Union. Keep an eye out for some exciting news in the next few days. Hopefully we’ll have something fun to show you.

Left 4 Dead Freezing [SOLVED]

July 24th, 2009

left-4-dead

The Problem
So for the past few months I’ve been having horrendous problems playing Left 4 Dead with my friends. The game would completely lock-up at the most random intervals. At first I thought this was due to the heat. Then I pegged it on a bad video card that was being pushed for the first time by my new Dell 21.5″ Monitor (1920 x 1080). I’ve been running the latest graphics drivers, so I knew that wasn’t the issue.

Troubleshooting Ensued
My desktop PC has the following specs:

Windows XP 32-bit (Service Pack 3)
AMD Athlon 64 X2 3800+
3.00 GB of RAM
XFX GeForce 7800 GTX

The system is aging fast, but should still be able to handle Left 4 Dead without crashing. Needless to say it was irritating the hell out of me.

The Solution
Finally I did some digging and found a few forum posts that suggested I disable the game’s “Multicore Rendering” setting under “Options->Video->Advanced.” While I was at it, I set all the texture settings to “low.” Everything else was set using the “Recommended Settings” button. Now I’m able to play the game smooth as butter at full resolution on my widescreen monitor.

High-def zombies are a beautiful thing – rawr.