When calling the REST/JSON API to save a node, I am able to publish nodes just fine, but I am not able to unpublish them. The JSON request being sent is below, with the RDF mapping removed:

{
  "node":{
    "vid":"96",
    "uid":"1",
    "title":"SOME RANDOM TITLE",
    "log":"",
    "status":"1",
    "comment":"1",
    "promote":"0",
    "sticky":"1",
    "nid":"96",
    "type":"page",
    "language":"",
    "created":"1330924791",
    "changed":"1333324372",
    "tnid":"0",
    "translate":"0",
    "revision_timestamp":"1333324372",
    "revision_uid":"1",
    "body":[

    ],
    "cid":"0",
    "last_comment_timestamp":"1330924791",
    "last_comment_name":null,
    "last_comment_uid":"1",
    "comment_count":"0",
    "name":"admin",
    "picture":"0",
    "data":"b:0;"
  }
}

Replace the "promote":"0" with "promote":"1" works just fine.

I took a look into node_resource.inc, but couldn't see a clear reason this might be failing. Though I wonder if

As a side-note, this is part of a Backbone module demo module, using Services and Backbone to make a better node administration screen. I'm guessing that the "0" value is being passed over as a null somewhere, but am not sure where to look.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

marcingy’s picture

Category: bug » support
ethanw’s picture

Thanks for the response. I consulted this similar post, http://groups.drupal.org/node/199748, which recommends using false instead of "0" for the "off" state of promote. I tried this with "promote", "sticky" and "publish", but am not able to set any of those to their false states via a JSON call. I am able to promote, make stick and publish via the REST call and a value of "1", but trying to un-publish, un-promote or un-sticky does not happen when using either "0" or false values.

I've attached the updated JSON request and the corresponding $form_values value passed to the drupal_submit_form invocation in node_resource.inc's update function.

ygerasimov’s picture

This is quite weird. Json got decoded in services here http://drupalcode.org/project/services.git/blob/refs/heads/7.x-3.x:/serv...

But I have tried to json_decode your attached node and got FALSE in values of status, promote and sticky.

You can also enable debug mode and see what arguments passed to node resource. Please check there whether promote property is really FALSE and not empty string. Empty string does not affect anything so default value will be applied.

setvik’s picture

Did some debugging and narrowed down the cause. Not sure of the right solution though.

The problem results from submitting a string value of "0" for fields that are checkbox fields in their Drupal FAPI incarnation.

string values of "0" submitted for checkbox fields like status, promote, and sticky (as well as any field api checkbox fields) pass through services -> _node_resource_update() -> drupal_form_submit() ... and finally to Drupal core's form_type_checkbox_value() which correctly interprets a string value of "0" as checked (see http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.13.2)...

To ensure that status, promote, sticky, and all other checkbox fields are saved as unchecked (i.e. FALSE), a form value of either NULL or FALSE must be submitted via drupal_form_submit().

Backbone's JS node object represents Drupal checkbox fields (status, promote, sticky, etc) as either "1" or "0" and submits them that way upon save unless they are explicitly set to another value. "0" values are interpreted by Drupal as checked and so all checkbox fields get set to TRUE when a node is saved by Backbone through services.

To ensure all checkbox fields aren't set to checked by backbone/services, there are two options currently:

Unset those properties to ensure they aren't altered from their current values. e.g. If you just want to change the title on a simple node, you have to first unset checkbox fields and then save.

myNode.unset('status');
myNode.unset('promote');
myNode.unset('sticky');
myNode.save('title', 'new title');

To forcibly set a checkbox field to unchecked, you have to explicitly set the property to null when saving. e.g. if I wanted to promote a node and change it's title, i'd do:

myNode.unset('status');
myNode.unset('sticky');
myNode.save({title: 'new title', promote: null});

This isn't ideal, but not sure what the appropriate solution is...

I haven't tested yet, but if services's node loading functionality returns a JSON object with unchecked checkbox field values represented as "0"... it probably makes sense to fix the problem there... so that service's node loading and saving functions are consistent in how they handle checkbox field values.

Otherwise, it probably needs to be addressed in Backbone. I gotta take a break, but that's what I've gotten so far...

I'll try to take a look at what Services node.load is returning via JSON in a couple of days (unless someone beats me to it :-)).

ethanw’s picture

Thanks @setvik, this is awesome.

I feel like the most elegant solutions here would involve overriding `Model.save` to insert some prep-processing code before delegating to `Backbone.model.save()`.

Of course, the question then is how to be identify which properties should convert `0`/`1`, `null`/`true`. I can't think of an especially slick solution to this, but the simplest seems like it would be for the model to keep a property that is an array of boolean properties values, something like model._booleanProperties, and iterating over those to force the values to what the FAPI needs before saving. Something like:

save: function(attributes, options) {
  _.each(attributes, function(val, key) {
    if (key in this.booleanProperties) {
      attributes.key = myCastingFunction(val);
    } 
  });
  Backbone.save(attributes, options); // or a better way of calling Super
}

How does this approach seem to others?

hansstam’s picture

Is there any solution for this yet?
I am having the same issues.

ethanw’s picture

Not yet, hoping to have a patch posted by next week.

Ethan

ygerasimov’s picture

Status: Active » Needs review
FileSize
1.12 KB

You are able to unpublish the node already. If the node is already created you should send following json in the request body in order to unpublish it:
node[status]

You can find attached patch that adds test that unpublishes the node. If you run the test you will see that node gets unpublished (and you will also see exact request body).

There is inconsistency that if you retrieve the node you will get '0' value but when you would like to set it -- you should send NULL value. But that is related to how services save the node -- via drupal_form_execute().

Please advise if you have any further questions about this case.

Status: Needs review » Needs work

The last submitted patch, services-unpublish-1511662-8.patch, failed testing.

ygerasimov’s picture

Status: Needs work » Active

Is this issue closed or anyone still has issues with unpublishing nodes?

Patch from #8 is not going to be committed to the repo. It is just example for you to see that it unpublishing works.

ethanw’s picture

We've worked around this issue in Backbone by making sure to set the JSON array to "null" for all values that we know are boolean. It would be nice to handle other "falsey" values such as `false`, `0` or `"0"` as false values for boolean values, but not 100% required to make things work.

marcingy’s picture

Status: Active » Closed (fixed)
kylebrowning’s picture

Category: support » bug
Status: Closed (fixed) » Needs review
FileSize
2.57 KB

I hate to be the bearer of bad news but it seems I cannot get a test to prove unpublishing a node works.

I attempted FALSE, 0, "0" and NULL all receive fails :/

Proof.

Status: Needs review » Needs work

The last submitted patch, 0001-Issue-1511662-Unable-to-unpublish-a-node.patch, failed testing.

stone_d’s picture

I want to use Drupal with an external workflow-System. So publish- and unpublish-options are essential since the nodes are published by passing a workflow, or unpublished if they need to be corrected.

Any solution yet how to pass these values via JSON-POST request?

Thanks in advance
F.

stone_d’s picture

ok i found solution in this module (at least for publishing/unpublishing): https://drupal.org/project/services_node_resource_extension
PLUS - see my explanation here: https://drupal.org/node/1955868#comment-7918765

erald’s picture

Version: 7.x-3.x-dev » 7.x-3.5
Priority: Normal » Critical

Am not sure if this belongs here.
I am able to unpublished and unstick a node via a json update by putting NULL in the field value.
However I have a boolean field (field_banner_visible) which I like to set and unset but that does not work. Tried NULL, FALSE, 0 put nothing seems to be accepted.
I use:

	  $node_data = array(
	  	'status' => 1,
	  	'sticky' => NULL,
	  	'field_counter_out' => array(
			'und' => array(
				0 => array(
					'value' => 109,
				)
			)
		),
		'field_banner_visible' => array(
			'und' => array(
				0 => array(
					'value' => '0',
				)
			)
		),
		'field_description' => array(
			'und' => array(
				0 => array(
					'value' => 'testing',
				)
			)
		),
	  );
$node_data = json_encode($node_data);

Have no idea on how to do this.

erald’s picture

The strange thing is that I can set the boolean value when using 1. but there is no way I can figure out how to unset it..

so this works

		'field_banner_visible' => array(
			'und' => array(
				0 => array(
					'value' => '1',
				)
			)
		),
stone_d’s picture

Have you already tried with "true" and "false" instead of 0 and 1?

erald’s picture

Yes I tried NULL, FALSE and 0 and '0'
Using 1 to set the boolean works perfectly.

stone_d’s picture

hmmm ... sound to me like the same issue many had with "publishing" and "unpublishing" via JSON. Actually i have no idea - sorry :/

erald’s picture

Well that works without a problem I can publish and unpublish without any problems just by using NULL to unpublish. Same for sticky that works the same way.
Just when I have a boolean field it does not work.

idflood’s picture

Having the same issue with custom field. I tried with FALSE, 0, NULL and unsetting the json variable but still no luck.

I'm not sure if this will help, but for reference it looks like the form_type_checkbox_value function in includes/form.inc is responsible for retrieving the value of a checkbox.

idflood’s picture

The drupal_form_submit doc says:

To submit an unchecked checkbox or other control that browsers submit by not having a $_POST entry, include the key, but set the value to NULL.

So I continued to try to alter the variable, this time in a hook_service_services_request_preprocess_alter function. In this function, if I want to set my checkbox to TRUE I need this code:

$args[1]["myfield"] = array(
    'und' => array(array("value" => 1)) // with ['und'][0]['value']
);

But if I want to uncheck the checkbox here is the code:

$args[1]["myfield"] = array(
    'und' => NULL // No [0]['value'] here...
);
erald’s picture

That works perfectly Changed the code to

		'field_banner_visible' => array(
			'und' => NULL
		),

Thanks some things are easy when you know.

idflood’s picture

Status: Needs work » Needs review
FileSize
2.05 KB

Here is a proof of concept patch to handle 0 values on custom field. The same method could certainly be used on the special promote, publish, ... fields.

I'm marking this as needs review, but it eventually needs more work and some tests.

Status: Needs review » Needs work

The last submitted patch, unset_boolean-1511662-26.patch, failed testing.

kylebrowning’s picture

Title: Unable to unpromote with node.save » list_boolean values are difficult to submit
Category: Bug report » Feature request
Issue summary: View changes
Priority: Critical » Normal

So, The reason my tests fail is because, #2002758: REST Server cannot parse form-data when the method is PUT

The original issue thats here, can be solved with using json and the following object.

{"node":{"title":"test3", "type":"article", "status":null}}

However for multipart forms it seems the above mentioned issue needs to be resolved first.

That being said, I'm able to achieve the same result with list boolean values, so this isn't a bug, its not a feature request for automatically handling field values.

yanyong’s picture

Here is another path base on idflood's work, this patch fix a issue which prevent boolean fields set back to 1 after applying unset_boolean-1511662-26.patch.

kylebrowning’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 29: unset_boolean_fileds-1511662-29.patch, failed testing.

kylebrowning’s picture

Status: Needs work » Closed (won't fix)
jerrac’s picture

I just ran into being unable to publish/unpublish via json. So I'm sad this issue is marked as "won't fix". That said, there https://www.drupal.org/node/1511662#comment-7918777 does work. To summarize so you don't have to click through to the other issue:

Use https://www.drupal.org/project/services_node_resource_extension and make an extra call to the appropriate url. /node//(un)publish.json. Make sure you use POST. GET will not work. You also need to enable the appropriate targeted actions for your service.