: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
_.each( collection.models, function( model ) {
model.set( 'parent_post', postId );
* Add a helper function to handle post Meta.
* Get meta by key for a post.
* @param {string} key The meta key.
* @return {Object} The post meta value.
getMeta: function( key ) {
var metas = this.get( 'meta' );
* Get all meta key/values for a post.
* @return {Object} The post metas, as a key value pair object.
return this.get( 'meta' );
* Set a group of meta key/values for a post.
* @param {Object} meta The post meta to set, as key/value pairs.
setMetas: function( meta ) {
var metas = this.get( 'meta' );
this.set( 'meta', metas );
* Set a single meta value for a post, by key.
* @param {string} key The meta key.
* @param {Object} value The meta value.
setMeta: function( key, value ) {
var metas = this.get( 'meta' );
this.set( 'meta', metas );
* Add a helper function to handle post Revisions.
getRevisions: function() {
return buildCollectionGetter( this, 'PostRevisions' );
* Add a helper function to handle post Tags.
* Get the tags for a post.
* @return {Deferred.promise} promise Resolves to an array of tags.
var tagIds = this.get( 'tags' ),
tags = new wp.api.collections.Tags();
// Resolve with an empty array if no tags.
if ( _.isEmpty( tagIds ) ) {
return jQuery.Deferred().resolve( [] );
return tags.fetch( { data: { include: tagIds } } );
* Set the tags for a post.
* Accepts an array of tag slugs, or a Tags collection.
* @param {Array|Backbone.Collection} tags The tags to set on the post.
setTags: function( tags ) {
if ( _.isString( tags ) ) {
// If this is an array of slugs, build a collection.
if ( _.isArray( tags ) ) {
allTags = new wp.api.collections.Tags();
success: function( alltags ) {
// Find the passed tags and set them up.
_.each( tags, function( tag ) {
newTag = new wp.api.models.Tag( alltags.findWhere( { slug: tag } ) );
// Tie the new tag to the post.
newTag.set( 'parent_post', self.get( 'id' ) );
// Add the new tag to the collection.
tags = new wp.api.collections.Tags( newTags );
self.setTagsWithCollection( tags );
this.setTagsWithCollection( tags );
* Set the tags for a post.
* Accepts a Tags collection.
* @param {Array|Backbone.Collection} tags The tags to set on the post.
setTagsWithCollection: function( tags ) {
// Pluck out the category IDs.
this.set( 'tags', tags.pluck( 'id' ) );
* Add a helper function to handle post Categories.
* Get a the categories for a post.
* @return {Deferred.promise} promise Resolves to an array of categories.
getCategories: function() {
var categoryIds = this.get( 'categories' ),
categories = new wp.api.collections.Categories();
// Resolve with an empty array if no categories.
if ( _.isEmpty( categoryIds ) ) {
return jQuery.Deferred().resolve( [] );
return categories.fetch( { data: { include: categoryIds } } );
* Set the categories for a post.
* Accepts an array of category slugs, or a Categories collection.
* @param {Array|Backbone.Collection} categories The categories to set on the post.
setCategories: function( categories ) {
var allCategories, newCategory,
if ( _.isString( categories ) ) {
// If this is an array of slugs, build a collection.
if ( _.isArray( categories ) ) {
// Get all the categories.
allCategories = new wp.api.collections.Categories();
success: function( allcats ) {
// Find the passed categories and set them up.
_.each( categories, function( category ) {
newCategory = new wp.api.models.Category( allcats.findWhere( { slug: category } ) );
// Tie the new category to the post.
newCategory.set( 'parent_post', self.get( 'id' ) );
// Add the new category to the collection.
newCategories.push( newCategory );
categories = new wp.api.collections.Categories( newCategories );
self.setCategoriesWithCollection( categories );
this.setCategoriesWithCollection( categories );
* Set the categories for a post.
* Accepts Categories collection.
* @param {Array|Backbone.Collection} categories The categories to set on the post.
setCategoriesWithCollection: function( categories ) {
// Pluck out the category IDs.
this.set( 'categories', categories.pluck( 'id' ) );
* Add a helper function to retrieve the author user model.
getAuthorUser: function() {
return buildModelGetter( this, this.get( 'author' ), 'User', 'author', 'name' );
* Add a helper function to retrieve the featured media.
getFeaturedMedia: function() {
return buildModelGetter( this, this.get( 'featured_media' ), 'Media', 'wp:featuredmedia', 'source_url' );
// Exit if we don't have valid model defaults.
if ( _.isUndefined( model.prototype.args ) ) {
// Go thru the parsable date fields, if our model contains any of them it gets the TimeStampedMixin.
_.each( parseableDates, function( theDateKey ) {
if ( ! _.isUndefined( model.prototype.args[ theDateKey ] ) ) {
// Add the TimeStampedMixin for models that contain a date field.
model = model.extend( TimeStampedMixin );
// Add the AuthorMixin for models that contain an author.
if ( ! _.isUndefined( model.prototype.args.author ) ) {
model = model.extend( AuthorMixin );
// Add the FeaturedMediaMixin for models that contain a featured_media.
if ( ! _.isUndefined( model.prototype.args.featured_media ) ) {
model = model.extend( FeaturedMediaMixin );
// Add the CategoriesMixin for models that support categories collections.
if ( ! _.isUndefined( model.prototype.args.categories ) ) {
model = model.extend( CategoriesMixin );
// Add the MetaMixin for models that support meta.
if ( ! _.isUndefined( model.prototype.args.meta ) ) {
model = model.extend( MetaMixin );
// Add the TagsMixin for models that support tags collections.
if ( ! _.isUndefined( model.prototype.args.tags ) ) {
model = model.extend( TagsMixin );
// Add the RevisionsMixin for models that support revisions collections.
if ( ! _.isUndefined( loadingObjects.collections[ modelClassName + 'Revisions' ] ) ) {
model = model.extend( RevisionsMixin );
/* global wpApiSettings:false */
// Suppress warning about parse function's unused "options" argument:
/* jshint unused:false */
var wpApiSettings = window.wpApiSettings || {},
trashableTypes = [ 'Comment', 'Media', 'Comment', 'Post', 'Page', 'Status', 'Taxonomy', 'Type' ];
* Backbone base model for all models.
wp.api.WPApiBaseModel = Backbone.Model.extend(
/** @lends WPApiBaseModel.prototype */
* Types that don't support trashing require passing ?force=true to delete.
if ( -1 === _.indexOf( trashableTypes, this.name ) ) {
this.requireForceForDelete = true;
* Set nonce header before every Backbone sync.
* @param {string} method.
* @param {Backbone.Model} model.
* @param {{beforeSend}, *} options.
sync: function( method, model, options ) {
// Remove date_gmt if null.
if ( _.isNull( model.get( 'date_gmt' ) ) ) {
model.unset( 'date_gmt' );
if ( _.isEmpty( model.get( 'slug' ) ) ) {
if ( _.isFunction( model.nonce ) && ! _.isEmpty( model.nonce() ) ) {
beforeSend = options.beforeSend;
// @todo Enable option for jsonp endpoints.
// options.dataType = 'jsonp';
// Include the nonce with requests.
options.beforeSend = function( xhr ) {
xhr.setRequestHeader( 'X-WP-Nonce', model.nonce() );
return beforeSend.apply( this, arguments );
// Update the nonce when a new nonce is returned with the response.
options.complete = function( xhr ) {
var returnedNonce = xhr.getResponseHeader( 'X-WP-Nonce' );
if ( returnedNonce && _.isFunction( model.nonce ) && model.nonce() !== returnedNonce ) {
model.endpointModel.set( 'nonce', returnedNonce );
// Add '?force=true' to use delete method when required.
if ( this.requireForceForDelete && 'delete' === method ) {
model.url = model.url() + '?force=true';
return Backbone.sync( method, model, options );
* Save is only allowed when the PUT OR POST methods are available for the endpoint.
save: function( attrs, options ) {
// Do we have the put method, then execute the save.
if ( _.includes( this.methods, 'PUT' ) || _.includes( this.methods, 'POST' ) ) {
// Proxy the call to the original save function.
return Backbone.Model.prototype.save.call( this, attrs, options );
// Otherwise bail, disallowing action.
* Delete is only allowed when the DELETE method is available for the endpoint.
destroy: function( options ) {
// Do we have the DELETE method, then execute the destroy.
if ( _.includes( this.methods, 'DELETE' ) ) {
// Proxy the call to the original save function.
return Backbone.Model.prototype.destroy.call( this, options );
// Otherwise bail, disallowing action.
* API Schema model. Contains meta information about the API.
wp.api.models.Schema = wp.api.WPApiBaseModel.extend(
/** @lends Schema.prototype */
initialize: function( attributes, options ) {
wp.api.WPApiBaseModel.prototype.initialize.call( model, attributes, options );
model.apiRoot = options.apiRoot || wpApiSettings.root;
model.versionString = options.versionString || wpApiSettings.versionString;
return this.apiRoot + this.versionString;
var wpApiSettings = window.wpApiSettings || {};
* Contains basic collection functionality such as pagination.
wp.api.WPApiBaseCollection = Backbone.Collection.extend(
/** @lends BaseCollection.prototype */
initialize: function( models, options ) {
if ( _.isUndefined( options ) ) {
this.parent = options.parent;
* Extend Backbone.Collection.sync to add nince and pagination support.
* Set nonce header before every Backbone sync.
* @param {string} method.
* @param {Backbone.Model} model.
* @param {{success}, *} options.
sync: function( method, model, options ) {