Cannot set published flag to true with Google Drive API - php

I have made a simple PHP script which uploads a file to Google Drive. I then run the following function:
function PublishToWeb($service, $fileId, $revisionId) {
$patchedRevision = new Google_Revision();
$patchedRevision->setPublished(true);
$patchedRevision->setPublishAuto(true);
$patchedRevision->setPublishedOutsideDomain(true);
try {
return $service->revisions->patch($fileId, $revisionId, $patchedRevision);
} catch (Exception $e) {
print "An error occurred: " . $e->getMessage();
}
return NULL;
}
I get no error message but the word document is not published.
When I try to set the flags using the Google APIs explorer it returns no errors but also fails to set the published flag to true. Am I missing something obvious?
For clarity I'm trying to upload a file then instantly simulate pressing 'Publish to web'. I also tried using revisions.update
Update:
Okay, I've figured out that the document has to be uploaded and converted to a google doc format to be published. However when the document is saved as a google doc it has no headrevisionid set so I can't use revisions.update or revisions.patch
Anyone know how to publish a google doc file?

Okay I figured it out.
When uploading the file convert to a google doc
$createdFile = $service->files->insert($file, array(
'data' => $data,
'convert' => 'true',
));
Then update the published flag to true
$revisionId = 1; //The revisionId for a newly created google doc will = 1
function updateRevision($service, $fileId, $revisionId) {
$patchedRevision = new Google_Revision();
$patchedRevision->setPublished(true);
$patchedRevision->setPublishAuto(true);
$patchedRevision->setPublishedOutsideDomain(true);
try {
return $service->revisions->patch($fileId, $revisionId, $patchedRevision);
} catch (Exception $e) {
print "An error occurred: " . $e->getMessage();
}
return NULL;
}
Then build the publishedlink as this isn't done for you
$PublishURL = 'https://docs.google.com/document/d/'.$fileId.'/pub';

Related

How to setup server to serve authentication with Google Script bound to Google Cloud?

I have a script at Google Script which creates Google Drive directories and is to be activated by API calls, but I fail to make API call to it.
I have deployed the script, bound it to Google Cloud, obtained all credentials for the script and service account. Now I stuck at the point when API call to this script gives me the following error:
Caught exception: json key is missing the type field
$scriptId = env('GOOGLE_SCRIPT_ID');
$service = $this->getService();
$request = new \Google_Service_Script_ExecutionRequest();
$request->setFunction('main');
$request->setParameters("fdf");
try {
$response = $service->scripts->run($scriptId, $request);
if ($response->getError()) {
$error = $response->getError()['details'][0];
printf("Script error message: %s\n", $error['errorMessage']);
if (array_key_exists('scriptStackTraceElements', $error)) {
print "Script error stacktrace:\n";
foreach ($error['scriptStackTraceElements'] as $trace) {
printf("\t%s: %d\n", $trace['function'], $trace['lineNumber']);
}
}
}
} catch (\Exception $e) {
// The API encountered a problem before the script started executing.
echo 'Caught exception: ', $e->getMessage(), "\n";
}
What am I missing? Is there a comprehensive tutorial on this topic? I've read Google's manuals on the topic but they don't work much for me to solve the problem.

Get bad WSDL from SoapFault?

I'm connecting to a 3rd party service with SoapClient. Most of the time it works fine, but every once in awhile, maybe once out of every 100-150 calls, I get the error
Soap Failed: SOAP-ERROR: Parsing Schema: unexpected in complexType
My code is in a try/catch with a retry, and it will work on the next round through. But I'd like to examine the WSDL to find out why that fails, partly for my own curiosity, and in case I need to pass it along to the company I'm connecting to. Can I get that information from the SoapFault? Or would I have to call the URL to get the string? I'm afraid if I get the WSDL after the fact, it may already be fixed.
$pass = FALSE;
$this->soap = NULL;
$this->session = NULL;
do {
try {
Doc::i("Starting session");
$this->soap = new SoapClient($this->wsdl_url, ['trace' => 1]);
$pass = TRUE;
} catch (\SoapFault $e) {
Doc::e('Soap Failed: ' . $e->getMessage());
if(str_contains($e->getMessage(),'Parsing Schema') && !empty($e->detail)) {
Doc::e($e->detail); // Something new I'm trying to see if it helps
}
} catch (FatalErrorException $e) {
Doc::e("Soap failed really bad: " . $e->getMessage());
} catch (\Exception $e) {
Doc::e("Soap failed bad: " . $e->getMessage());
}
} while (!$pass);
You should be able to use $this->soap->__getLastResponse() since you are passing 'trace' => 1 option to SoapClient.
You might also consider logging $this->soap->__getLastRequest as well as the headers versions of both of these to ensure you're capturing as much information as possible at run-time.
Refer to the SoapClient method list for the possible options. Just remember the trick here is the trace option: without that, these will not return anything useful!

How implement error in Rest API?

I want to know which is the way to implement error in RestAPI, actually if a method in my classes generate an exception I return this ...
if(mysqli_connect_errno()) {
throw new Exception("Can't connect to db.");
}
... but this is a bad practice 'cause an API should be return a json.
So my idea is create a class called Errors and, in each class, when an error is fired I simply call the relative error number for display the json error.
Someone have another idea?
Maybe something like so :
<?php
try {
// Do your stuff
if(mysqli_connect_errno()) {
throw new Exception("Can't connect to db.");
}
} catch (Exception $e) {
echo json_encode(array("success" => false, "message" => $e->getMessage()));
return;
}
I think #Gwendal answer is good but it's no enough just to return a json response, you also have to return the proper http code:
<?php
try {
// Do your stuff
} catch (Exception $e) {
header($_SERVER['SERVER_PROTOCOL'] . ' 500 Internal Server Error', true, 500);
echo json_encode(array("success" => false, "message" => $e->getMessage()));
return;
}
I think you're in the right path. There are a couple of concerns that you're dealing with in here. First one is error handling, whilst the second one is error formatting.
Error handling can be done in several ways, and throwing exceptions is one of them. In order to find out when something bad happened, you'll need to wrap your exceptions within a try/catch block:
try {
//logic
if(mysqli_connect_errno()) {
throw new Exception("Can't connect to db.");
}
//more logic
} catch (Exception $e) {
//handle the error here
}
If you're following this route, I'd suggest you to be more specific in your exceptions, so you can better build your responses in your API. It's not the same having the DB down than to not being able to find a resource, for instance:
try {
//logic
if(mysqli_connect_errno()) {
throw new DBException("Can't connect to db.");
}
if(is_null($entity)) {
throw new ResourceNotFoundException("Entity could not be found");
}
//more logic
} catch (DBException $e) {
//handle DB error here
} catch (ResourceNotFoundException $e) {
//handle resource not found error here
}
Now for the formatting part, the normal response in REST APIs are JSON responses. One way to go about it, would be to create a specific class whose sole responsibility would be to transforms your response into a valid JSON:
...
} catch (DBException $e) {
return $this->JSONResponse->format("Sorry we could not complete your request", 500);
} catch (ResourceNotFoundException $e) {
return $this->JSONResponse->format("The resource you were looking for could not be found", 404);
}
As you can see, different errors have different status codes. The implementation of the class is quite trivial:
class JSONResponse {
public function format($message, $statusCode) {
return json_encode(['message' => $message, 'code' => $statusCode]);
}
}
This does not change the status code of the response though, which is essential to good REST API design. You'll need to set the appropriate status code by using this function.
You can find a more robust and flexible implementation of this class in the Symfony HTTPFoundation Component, which extends from the normal Response class.
My RESTful API always returns a JSON of this structure:
[
'resource' : [],
'code' : [
'id' : int,
'msg' : string
],
'meta' : [],
'log' : []
]
If I return data, the data is always in resource and code['id'] is always 0 (which represents 'OK'). When an error occours, I return an empty resource and some error code. Also I provide some extra information via meta and can log some actions via log which helps me a lot with debugging.
This might also help you with future issues, for example if you want to split an answer into pages so the client should request data via GET /path/to/resource/page/:page or want to notice the client that a certain request path is deprecated.

create copy of google doc spreadsheet using php api

I want to create copy of excel sheet of google doc using PHP API.I can see java code but no PHP code at all :(
Actually I already have a excel sheet on google drive I want to show it on website, I am already successed in it but problem is The same file is visible to everyone and if one user is changing file and at the same time same user can see that changes as it is synced to google drive.
So I thought solution is if I can create a copy of the sheet and shows different-2 copies to each user via PHP API.
===============
Edited Code
function copyFile($service, $originFileId, $copyTitle) {
$copiedFile = new Google_DriveFile();
$copiedFile->setTitle($copyTitle);
try {
$arr['convert'] = false;
$arr['visibility'] = 'default';
return $service->files->copy($originFileId, $copiedFile,$arr);
} catch (Exception $e) {
print "An error occurred: " . $e->getMessage();
}
return NULL;
}
function insertPermission($service, $fileId, $value, $type, $role) {
$newPermission = new Google_Permission();
$newPermission->setValue($value);
$newPermission->setType($type);
$newPermission->setRole($role);
try {
return $service->permissions->insert($fileId, $newPermission);
} catch (Exception $e) {
print "An error occurred: " . $e->getMessage();
}
return NULL;
}
function updateRevision($service, $fileId, $revisionId) {
try {
// First retrieve the revision from the API.
$revisions = $service->revisions;
$revision = $revisions->get($fileId, $revisionId);
echo '<pre>';print_r($revision);
$revision->setPinned(true);
return $revisions->update($fileId, $revisionId, $revision);
} catch (Exception $e) {
print "An error occurred: " . $e->getMessage();
}
return NULL;
}
function updateRevision1($service, $fileId, $revisionId) {
$patchedRevision = new Google_Revision();
$patchedRevision->setPublished(true);
$patchedRevision->setPublishAuto(true);
$patchedRevision->setPublishedOutsideDomain(true);
try {
return $service->revisions->patch($fileId, $revisionId, $patchedRevision);
} catch (Exception $e) {
print "An error occurred: " . $e->getMessage();
}
return NULL;
}
require_once 'google-api-php-client/src/Google_Client.php';
require_once 'google-api-php-client/src/contrib/Google_DriveService.php';
$client = new Google_Client();
// Get your credentials from the console
$client->setClientId('clientid');
$client->setClientSecret('secret');
$client->setScopes(array('https://www.googleapis.com/auth/drive'));
$client->setRedirectUri('redirecturi');
$service = new Google_DriveService($client);
if(!isset($_GET['code']) ) {
$authUrl = $client->createAuthUrl();
header('location: '.$authUrl );
die;
}
else{
$authCode = $_GET['code'];
// Exchange authorization code for access token
$accessToken = $client->authenticate($authCode);
$client->setAccessToken($accessToken);
$new_file = copyFile($service,'fileid','Baby Schema');
echo 'file=<pre>';print_r($new_file);
$permission = insertPermission($service,$new_file['id'],'me','anyone','writer');
echo 'permission=<pre>';print_r($permission);
$revision = updateRevision1($service, $new_file['id'], '1');
echo 'pub=<pre>';print_r($revision);
}
Code is working absolutely perfect but it is not doing what I want.
The above code is doing these tasks
1) I have one spreadsheet on bhuvneshgupta333#gmail.com
2) I am creating copy when bhuvnesh.gupta#witslog.com login to my abc.com website.
3) I am setting copy to be public on web.
4) Making copy to be published on web.
Now I am having issue.
1) In the spreadsheet there are 5 sheets out of them 2 are editable and rest 3 are protected so no one can see my formulas.
2) When copy is created by bhuvnesh.gupta#witslog.com then he becomes the owner of file while file is on bhuvneshgupta333#gmail.com while I want Owner to be bhuvneshgupta not bhuvnesh.gupta because I don't want to show formulas to any one not even bhuvnesh.gupta when file is opened on web.
3) I want to protect 3 sheets of spreadsheet to everyone.
I am using this way to call spreadhseet on web.
<iframe width="100%" height="600" src="https://docs.google.com/spreadsheets/d/fileid/edit?usp=sharing;&rm=minimal"></iframe>
Please help me guys.It's last point of my project.
Please help me guys.
Thanks in advance!
The google drive API reference does include PhP code for file copying.
/**
* Copy an existing file.
*
* #param Google_DriveService $service Drive API service instance.
* #param String $originFileId ID of the origin file to copy.
* #param String $copyTitle Title of the copy.
* #return DriveFile The copied file. NULL is returned if an API error occurred.
*/
function copyFile($service, $originFileId, $copyTitle) {
$copiedFile = new Google_DriveFile();
$copiedFile->setTitle($copyTitle);
try {
return $service->files->copy($originFileId, $copiedFile);
} catch (Exception $e) {
print "An error occurred: " . $e->getMessage();
}
return NULL;
}
It is here: https://developers.google.com/drive/v2/reference/files/copy#try-it

catching mailchimp php api errors

I am trying to create a subscribe method for my laravel app that uses the mailchimp api to subscribe a user to a given list. The method works fine when the email address is not already on the lsit. when it is already subscribed the mailchimp api throws the following error
Mailchimp_List_AlreadySubscribed blah#blah.co is already subscribed to
list Tc App Test List. Click here to update your profile.
with the following code being shown
public function castError($result) {
if($result['status'] !== 'error' || !$result['name']) throw new Mailchimp_Error('We received an unexpected error: ' . json_encode($result));
$class = (isset(self::$error_map[$result['name']])) ? self::$error_map[$result['name']] : 'Mailchimp_Error';
return new $class($result['error'], $result['code']);
}
I have attempted a try catch block to catch the error but it is still being returned to the browser, here is what I tried and were it says MailChimp_Error I tried with Exception as well.
public function subscribe($id, $email, $merge_vars)
{
try {
$this->mailchimp->lists->subscribe($id, $email, $merge_vars);
} catch (MailChimp_Error $e) {
$response = 'an error has occured';
}
return $response;
}
Ultimately I want to be able to run the method and then either return either a success message or a message describing the issue to the user. the 3 possible mailchimp method errors are Email_notexists, list_alreadysubscribed and list does not exist although tihs last one should not occur as I am providing the list in the source code.
edit 1; after being in touch with mailchimp api support they suggested this code but the error still gets returned to the browser in its entirety
try {
$results = $this->mailchimp->lists->subscribe($id, $email, $merge_vars);
} catch (Mailchimp_Error $e) {
if ($e->getMessage()) {
$error = 'Code:'.$e->getCode().': '.$e->getMessage();
}
}
echo $error;
You can do
try
{
$response = $this->mailchimp->lists->addListMember($list_id, [
"email_address" => $email,
"status" => "subscribed",
]);
}
catch (\EXCEPTION $e) {
return $e->getMessage();
}
The \EXCEPTION handles a sort of error for stripe
Subscribe is in a namespace Acme\Emails\Subscribe so catch(Mailchimp_Error $e) looks for Mailchimp_Error in this namespace.
Changing it to catch(\Mailchimp_Error $e) makes it look in the root namespace and then it works as intended

Categories