wordpress
Version:
A client for working with WordPress.
360 lines (292 loc) • 8.32 kB
JavaScript
var url = require( "url" ),
xmlrpc = require( "xmlrpc" ),
fieldMap = require( "./fields" );
// http://codex.wordpress.org/XML-RPC_Support
// http://codex.wordpress.org/XML-RPC_WordPress_API
function extend( a, b ) {
for ( var p in b ) {
a[ p ] = b[ p ];
}
return a;
}
function parseArguments( args ) {
return [].slice.call( args, 1 )
// Remove null arguments
// Null values only exist for optional fields. As of WordPress 4.4,
// null is no longer treated the same as omitting the value. To
// compensate for this, we just drop the argument before calling
// into WordPress. See #25.
.filter(function( value ) {
return value !== null;
});
}
function Client( settings ) {
[ "url", "username", "password" ].forEach(function( prop ) {
if ( !settings[prop] ) {
throw new Error( "Missing required setting: " + prop );
}
});
var parsedUrl = Client.parseUrl( settings.url );
this.rpc = xmlrpc[ parsedUrl.secure ? "createSecureClient" : "createClient" ]({
host: settings.host || parsedUrl.host,
port: parsedUrl.port,
path: parsedUrl.path,
rejectUnauthorized: settings.rejectUnauthorized !== undefined ? settings.rejectUnauthorized : true,
servername: settings.host || parsedUrl.host,
// Always set Host header in case we're pointing to a different server
// via settings.host
headers: {
Host: parsedUrl.host
},
basic_auth: !settings.basicAuth ? null : {
user: settings.basicAuth.username,
pass: settings.basicAuth.password
}
});
this.blogId = settings.blogId || 0;
this.username = settings.username;
this.password = settings.password;
}
Client.parseUrl = function( wpUrl ) {
var urlParts, secure;
// allow URLs without a protocol
if ( !(/\w+:\/\//.test( wpUrl ) ) ) {
wpUrl = "http://" + wpUrl;
}
urlParts = url.parse( wpUrl );
secure = urlParts.protocol === "https:";
return {
host: urlParts.hostname,
port: urlParts.port || (secure ? 443 : 80),
path: urlParts.path.replace( /\/+$/, "" ) + "/xmlrpc.php",
secure: secure
};
};
extend( Client.prototype, {
call: function( method ) {
var args = parseArguments( arguments ),
fn = args.pop();
if ( typeof fn !== "function" ) {
args.push( fn );
fn = null;
}
this.rpc.methodCall( method, args, function( error, data ) {
if ( !error ) {
return fn( null, data );
}
if ( error.code === "ENOTFOUND" && error.syscall === "getaddrinfo" ) {
error.message = "Unable to connect to WordPress.";
} else if ( error.message === "Unknown XML-RPC tag 'TITLE'" ) {
var additional = error.res.statusCode;
if (error.res.statusMessage) {
additional += "; " + error.res.statusMessage;
}
error.message = "(" + additional + ") " + error.message;
}
fn( error );
});
},
authenticatedCall: function() {
var args = [].slice.call( arguments );
args.splice( 1, 0, this.blogId, this.username, this.password );
this.call.apply( this, args );
},
listMethods: function( fn ) {
this.call( "system.listMethods", fn );
}
});
extend( Client.prototype, {
getPost: function( id, fields, fn ) {
if ( typeof fields === "function" ) {
fn = fields;
fields = null;
}
if ( fields ) {
fields = fieldMap.array( fields, "post" );
}
this.authenticatedCall( "wp.getPost", id, fields, function( error, post ) {
if ( error ) {
return fn( error );
}
fn( null, fieldMap.from( post, "post" ) );
});
},
getPosts: function( filter, fields, fn ) {
if ( typeof filter === "function" ) {
fn = filter;
fields = null;
filter = {};
}
if ( typeof fields === "function" ) {
fn = fields;
fields = null;
}
if ( filter.type ) {
filter.post_type = filter.type;
delete filter.type;
}
if ( filter.status ) {
filter.post_status = filter.status;
delete filter.status;
}
if ( filter.orderby ) {
filter.orderby = fieldMap.array( [ filter.orderby ], "post" )[ 0 ];
}
if ( fields ) {
fields = fieldMap.array( fields, "post" );
}
this.authenticatedCall( "wp.getPosts", filter, fields, function( error, posts ) {
if ( error ) {
return fn( error );
}
fn( null, posts.map(function( post ) {
return fieldMap.from( post, "post" );
}));
});
},
newPost: function( data, fn ) {
this.authenticatedCall( "wp.newPost", fieldMap.to( data, "post" ), fn );
},
// to remove a term, just set the terms and leave out the id that you want to remove
// to remove a custom field, pass the id with no key or value
editPost: function( id, data, fn ) {
this.authenticatedCall( "wp.editPost", id, fieldMap.to( data, "post" ), fn );
},
deletePost: function( id, fn ) {
this.authenticatedCall( "wp.deletePost", id, fn );
},
getPostType: function( name, fields, fn ) {
if ( typeof fields === "function" ) {
fn = fields;
fields = null;
}
if ( fields ) {
fields = fieldMap.array( fields, "postType" );
}
this.authenticatedCall( "wp.getPostType", name, fields, function( error, postType ) {
if ( error ) {
return fn( error );
}
fn( null, fieldMap.from( postType, "postType" ) );
});
},
getPostTypes: function( filter, fields, fn ) {
if ( typeof filter === "function" ) {
fn = filter;
fields = null;
filter = {};
}
if ( typeof fields === "function" ) {
fn = fields;
fields = null;
}
if ( Array.isArray(filter) ) {
fields = filter;
filter = {};
}
if ( fields ) {
fields = fieldMap.array( fields, "postType" );
}
this.authenticatedCall( "wp.getPostTypes", filter, fields, function( error, postTypes ) {
if ( error ) {
return fn( error );
}
Object.keys( postTypes ).forEach(function( postType ) {
postTypes[ postType ] = fieldMap.from( postTypes[ postType ], "postType" );
});
fn( null, postTypes );
});
}
});
extend( Client.prototype, {
getTaxonomy: function( name, fn ) {
this.authenticatedCall( "wp.getTaxonomy", name, function( error, taxonomy ) {
if ( error ) {
return fn( error );
}
fn( null, fieldMap.from( taxonomy, "taxonomy" ) );
});
},
getTaxonomies: function( fn ) {
this.authenticatedCall( "wp.getTaxonomies", function( error, taxonomies ) {
if ( error ) {
return fn( error );
}
fn( null, taxonomies.map(function( taxonomy ) {
return fieldMap.from( taxonomy, "taxonomy" );
}));
});
},
getTerm: function( taxonomy, id, fn ) {
this.authenticatedCall( "wp.getTerm", taxonomy, id, function( error, term ) {
if ( error ) {
return fn( error );
}
fn( null, fieldMap.from( term, "term" ) );
});
},
getTerms: function( taxonomy, filter, fn ) {
if ( typeof filter === "function" ) {
fn = filter;
filter = {};
}
if ( filter.hideEmpty ) {
filter.hide_empty = filter.hideEmpty;
delete filter.hideEmpty;
}
if ( filter.orderby ) {
filter.orderby = fieldMap.array( [ filter.orderby ], "term" )[ 0 ];
}
this.authenticatedCall( "wp.getTerms", taxonomy, filter, function( error, terms ) {
if ( error ) {
return fn( error );
}
fn( null, terms.map(function( term ) {
return fieldMap.from( term, "term" );
}));
});
},
newTerm: function( data, fn ) {
this.authenticatedCall( "wp.newTerm", fieldMap.to( data, "term" ), fn );
},
editTerm: function( id, data, fn ) {
this.authenticatedCall( "wp.editTerm", id, fieldMap.to( data, "term" ), fn );
},
deleteTerm: function( taxonomy, id, fn ) {
this.authenticatedCall( "wp.deleteTerm", taxonomy, id, fn );
}
});
extend( Client.prototype, {
getMediaItem: function( id, fn ) {
this.authenticatedCall( "wp.getMediaItem", id, function( error, media ) {
if ( error ) {
return fn( error );
}
fn( null, fieldMap.from( media, "media" ) );
});
},
getMediaLibrary: function( filter, fn ) {
if ( typeof filter === "function" ) {
fn = filter;
filter = {};
}
this.authenticatedCall( "wp.getMediaLibrary", filter, function( error, media ) {
if ( error ) {
return fn( error );
}
fn( null, media.map(function( item ) {
return fieldMap.from( item, "media" );
}));
});
},
uploadFile: function( data, fn ) {
this.authenticatedCall( "wp.uploadFile", fieldMap.to( data, "file" ), fn );
}
});
module.exports = {
Client: Client,
createClient: function( settings ) {
return new Client( settings );
},
fieldMap: fieldMap
};