Twine is a semantic web version of del.icio.us.
I've got a few invites floating around. Be aware, it's a real beta - that means rough around the edges.
At first, you might not see the value in it, at least not until you peek under the hood - every item you add is exportable as RDF.
The other side of that is like del.icio.us recommending what other people have tagged an item with, Twine will recommend related or similar items, based on picking out common people, themes, etc.
If you'd like to see, drop me a line.
Half person, half home automation, an under loved blog for all things Ruby, python and more in Adelaide
Monday, April 28, 2008
Monday, April 21, 2008
Accelerando
Charles Stross is a great author. You can read Accelerando for free too, under a creative commons license.
Thursday, April 17, 2008
My Wishlist For Operator
Operator is a firefox addon to detect microformats. For people like me, microformats are an enabler - the enterprise site I work on makes use of them for contact, event, etc information.
The trouble we have is integrating all of our systems. There's the production system, there's the CRM system, there's the Asterix phone system, there's an internal phone/contact list, ten trillion personal outlook calendars, a mix of google calendars, and much more.
Each is its own walled garden.
So, why not use Operator and users to make it easy to import, export granular data - contacts for instance.
To that end, I'd love:
1) A mechanism for Operator to autodiscover actions - like FOAF, OpenSearch, or Greasemonkey user scripts. This should allow me to preview before I install, and have a measure of trust.
2) Actions for popular enterprise open source software - again, get SugarCRM to autogenerate the Operator user-script for its local installation.
3) Make the installation process much much smoother - at the moment, it's a little clunky to add in a new user script.
The trouble we have is integrating all of our systems. There's the production system, there's the CRM system, there's the Asterix phone system, there's an internal phone/contact list, ten trillion personal outlook calendars, a mix of google calendars, and much more.
Each is its own walled garden.
So, why not use Operator and users to make it easy to import, export granular data - contacts for instance.
To that end, I'd love:
1) A mechanism for Operator to autodiscover actions - like FOAF, OpenSearch, or Greasemonkey user scripts. This should allow me to preview before I install, and have a measure of trust.
2) Actions for popular enterprise open source software - again, get SugarCRM to autogenerate the Operator user-script for its local installation.
3) Make the installation process much much smoother - at the moment, it's a little clunky to add in a new user script.
Sunday, April 13, 2008
XMPPHP - Jabber for PHP
Just out, XMPPHP.
Neat.
I'd like to see what I can do today with creating a semi-complicated agent calendaring agent - something which knows when I'm free/busy and can add a simple task / appointment for me.
Neat.
I'd like to see what I can do today with creating a semi-complicated agent calendaring agent - something which knows when I'm free/busy and can add a simple task / appointment for me.
Saturday, April 12, 2008
Operator user script: Add to Google Contacts (GData API)
I wanted to export a hcard to my google contacts with Operator, but there was no such thing.
I went off and tinkered around with the google contacts API, and operator.
I did this all while experimenting with Flock 1.1, (by the way, Flock still sucks, but this time because it's only got a handful of sites it integrates with); so if it breaks in your fancy new firefox, don't look at me!
To use it;
Copy and Paste, save as add-google-contact.js
Install Operator
Tools, Options, Operator
User scripts tab
Find add-google-contact.js
Find a site with hcards (like this one), and export away to your Google account!
I went off and tinkered around with the google contacts API, and operator.
I did this all while experimenting with Flock 1.1, (by the way, Flock still sucks, but this time because it's only got a handful of sites it integrates with); so if it breaks in your fancy new firefox, don't look at me!
To use it;
Copy and Paste, save as add-google-contact.js
Install Operator
Tools, Options, Operator
User scripts tab
Find add-google-contact.js
Find a site with hcards (like this one), and export away to your Google account!
var add_google_contact_login_details = {email: false, password: false, auth_token: false};
/**
* A helper method to send http requests to the google services.
*/
function add_google_contact_send_request(method, url, content, auth, content_type) {
request = new XMLHttpRequest();
request.open(method, url, false);
if (auth) {
request.setRequestHeader("Authorization", "GoogleLogin auth=" + auth);
}
if (content_type) {
request.setRequestHeader("Content-type", content_type);
}
try {
request.send(content);
if (request.status == 200 || request.status == 201 || request.status == 409) {
return request.responseText;
}
dump(request.status);
dump(request.responseText);
} catch (ex) {
dump(ex);
}
return null;
}
/**
* Extracts information out from a hCard semantic object
* and returns a google-friendly XML representation.
*/
function add_google_contact_create_xml_from_vcard(hcard) {
var i;
var full_address;
var email;
var tel;
var url;
var xml = "";
xml += "<atom:entry xmlns:atom='http://www.w3.org/2005/Atom' xmlns:gd='http://schemas.google.com/g/2005'>" + "\n";
xml += " <atom:category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/contact/2008#contact' />" + "\n";
//Parse name
xml += " <atom:title type='text'>" + hcard.fn + "</atom:title> " + "\n";
xml += " <atom:content type='text'>Notes</atom:content>" + "\n";
if (hcard.email) {
for (i = 0; i < hcard.email.length; i++) {
email = hcard.email[i];
type = 'home';
if (email.type && email.type[0] == 'work') {
type = 'work';
}
xml += " <gd:email rel='http://schemas.google.com/g/2005#" + type + "' address='" + email.value + "' />" + "\n";
}
}
if (hcard.tel) {
for (i = 0; i < hcard.tel.length; i++) {
tel = hcard.tel[i];
type = 'home';
if (tel.type && tel.type[0] == 'work') {
type = 'work';
}
xml += " <gd:phoneNumber rel='http://schemas.google.com/g/2005#" + type + "'>" + tel.value + "</gd:phoneNumber>" + "\n";
}
}
if (hcard.adr) {
for (i = 0; i < hcard.adr.length; i++) {
adr = hcard.adr[i];
full_address = "";
if (adr["street-address"]) {
full_address += adr["street-address"] + " ";
}
if (adr["locality"]) {
full_address += adr["locality"] + " ";
}
if (adr["region"]) {
full_address += adr["region"] + " ";
}
if (adr["postal-code"]) {
full_address += adr["postal-code"] + " ";
}
if (adr["country-name"]) {
full_address += adr["country-name"] + " ";
}
if (full_address != "") {
xml += " <gd:postalAddress rel='http://schemas.google.com/g/2005#work'>" + full_address + "</gd:postalAddress>" + "\n";
}
}
}
xml += "</atom:entry>" + "\n";
return xml;
}
/**
* Send a create contact request for the email & auth_token provided.
*
* The contact is described in xml.
*
* @see add_google_contact_create_xml_from_vcard()
*/
function add_google_contact_create_contact(email_address, auth_token, xml) {
url = 'http://www.google.com/m8/feeds/contacts/' + escape(email_address) + "/base";
return add_google_contact_send_request("POST", url, xml, auth_token, "application/atom+xml");
}
/**
* Fetch an authorisation token for a given
* username and password
*
* @return An authorisation token string
*/
function add_google_contact_login(username, password) {
var url = 'https://www.google.com/accounts/ClientLogin';
var content = "";
content += "accountType=HOSTED_OR_GOOGLE";
content += "&Email=" + username;
content += "&Passwd=" + password;
content += "&service=cp";
content += "&source=NoCompany-Operator-0.1";
response = add_google_contact_send_request("POST", url, content, null, "application/x-www-form-urlencoded");
// Sample response
/*
HTTP/1.0 200 OK
Server: GFE/1.3
Content-Type: text/plain
SID=DQAAAGgA...7Zg8CTN
LSID=DQAAAGsA...lk8BBbG
Auth=DQAAAGgA...dk3fA5N
*/
if (response) {
parts = response.split("\n");
return parts[2].substring(5);
}
return null;
}
function add_google_contact_get_login_details() {
var passwordManager = Components.classes["@mozilla.org/passwordmanager;1"]
.getService(Components.interfaces.nsIPasswordManager);
var e = passwordManager.enumerator;
//Ask the existing password manager for google account details
var queryString = 'https://www.google.com';
while (e.hasMoreElements()) {
try {
var pass = e.getNext().QueryInterface(Components.interfaces.nsIPassword);
if (pass.host == queryString) {
email_address = pass.user;
password = pass.password;
//TODO: Check if the email_address is valid (I store my username without the @ details)
return {email: email_address, password: password, auth_token: false};
}
} catch (ex) {
dump(ex);
}
}
//We didn't find the details. Oh dear.
//Better ask nicely.
var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService);
email_address = {value: ""};
password = {value: ""};
check = {value: true};
var result = prompts.promptUsernameAndPassword(null, "", "Enter email and password for your Google Account:",
email_address, password, "Remember password", check);
if (check.value) {
try {
passwordManager.addUser(queryString, email_address.value, password.value);
} catch (ex) {
dump(ex);
}
}
return {email: email_address, password: password, auth_token: false};
}
var add_google_contact = {
description: "Add to Google Contacts",
shortDescription: "Add Google Contact",
scope: {
semantic: {
"hCard" : "fn"
}
},
doAction: function(semanticObject, semanticObjectType, propertyIndex) {
//Do we have login details?
if (add_google_contact_login_details.email == false) {
add_google_contact_login_details = add_google_contact_get_login_details();
//If the user cancelled finding them...
if (add_google_contact_login_details.email == false) {
return false
}
}
if (!add_google_contact_login_details.auth_token) {
add_google_contact_login_details.auth_token = add_google_contact_login(add_google_contact_login_details.email,
add_google_contact_login_details.password);
}
xml = add_google_contact_create_xml_from_vcard(semanticObject);
result = add_google_contact_create_contact(add_google_contact_login_details.email, add_google_contact_login_details.auth_token, xml);
}
};
SemanticActions.add("add_google_contact", add_google_contact);
Tags
firefox,
javascript,
microformats,
mozilla,
open data,
open source,
operator
Sunday, April 06, 2008
PEAR and Gearman
PEAR might be getting a Gearman package.
What is Gearman? It's a quick and easy way to do distributed processing, in use at Yahoo, Digg, and presumably, Livejournal.
There is a server, gearmand, and you run several PHP (or other language) clients. You describe the tasks each client can perform, and gearman helps distribute the work.
An example client:
An example task, to do SQL queries.
Putting it all together
Some use cases which immediately spring to mind for me include:
* Running distributed PHPUnit runs against a common revision of the code
* Running our document generator processes
* Running our webservice/queuing processes
* As part of a scutter (multiple documents to retrieve)
What is Gearman? It's a quick and easy way to do distributed processing, in use at Yahoo, Digg, and presumably, Livejournal.
There is a server, gearmand, and you run several PHP (or other language) clients. You describe the tasks each client can perform, and gearman helps distribute the work.
An example client:
require_once 'Net/Gearman/Worker.php';
try {
$worker = new Net_Gearman_Worker(array('dev01:7003', 'dev01:7004'));
$worker->addAbility('Hello');
$worker->addAbility('Fail');
$worker->addAbility('SQL');
$worker->beginWork();
} catch (Net_Gearman_Exception $e) {
echo $e->getMessage() . "\n";
exit;
}
An example task, to do SQL queries.
require_once 'DB.php';
class Net_Gearman_Job_SQL extends Net_Gearman_Job_Common
{
public function run(stdClass $arg)
{
if (!isset($arg->sql) || !strlen($arg->sql)) {
throw new Net_Gearman_Job_Exception;
}
$db = DB::connect('mysql://testing:testing@192.168.243.20/testing');
$db->setFetchMode(DB_FETCHMODE_ASSOC);
$res = $db->getAll($arg->sql);
return $res;
}
}
Putting it all together
require_once 'Net/Gearman/Client.php';
$set = new Net_Gearman_Set();
function result($resp) {
print_r($resp);
}
$sql = array(
"SELECT * FROM users WHERE username = 'joestump'",
"SELECT * FROM users WHERE username LIKE 'joe%' LIMIT 10",
"SELECT * FROM items WHERE deleted = 0 LIMIT 10"
);
foreach ($sql as $s) {
$task = new Net_Gearman_Task('SQL', array(
'sql' => $s
));
$task->attachCallback('result');
$set->addTask($task);
}
$client = new Net_Gearman_Client(array('dev01'));
$client->runSet($set);
Some use cases which immediately spring to mind for me include:
* Running distributed PHPUnit runs against a common revision of the code
* Running our document generator processes
* Running our webservice/queuing processes
* As part of a scutter (multiple documents to retrieve)
Subscribe to:
Posts (Atom)