4

I'm trying to create a copy of a document using the Google Docs Api, and then edit it that copy by inserting text into it. So, I've looked at the documentation and seemingly implemented it exactly the way it says.

<?php

            //That is, if this is just a regular login 

            //Personal Files
            require("loginManager.php");
            require("globals.php");
            require("googleDrive.php");

            //Moodle files
            require("../config.php");
            require("../my/lib.php");
            require("../files/renderer.php");
            require("../course/lib.php");

            //Google Docs
            $CREDENTIALS_PATH = "../../" . "vendor/autoload.php";
            require ($CREDENTIALS_PATH);

            //Example code
            $copyTitle = 'Copy Title';
            $documentId = "1vYyeGLbadFi0sl9g2LEJSZCB4YiGOpCb";
            $copy = new Google_Service_Drive_DriveFile(array(
                'name' => $copyTitle
            ));


            //Initialize necessary client variables
            $desiredPath = "../../credentials.json";
            $authCode = (isset($_GET["code"]) ? $_GET["code"]:"");
            GoogleDrive::setAuthCode($authCode);
            $client = GoogleDrive::getClient($desiredPath, $FULLSCRIPT);
            $docServices = new Google_Service_Docs($client);
            $driveServices = new Google_Service_Drive($client);

            $files = $driveServices->files;
            $documents = $docServices->documents;
            $driveResponse = $files->copy($documentId, $copy);
            $documentCopyId = $driveResponse->id;

            //Create desiredRequests
            $desiredRequests = array(new Google_Service_Docs_Request(array(
            'insertText' => array(
            'text' => 'Hello world!',
            'location' => array(
            'index' => 25)))));



            $batchUpdateRequests = new Google_Service_Docs_BatchUpdateDocumentRequest(array(
                'requests' => $desiredRequests));
            $docChangeResponse = $documents->batchUpdate($documentCopyId, $batchUpdateRequests);


            echo $OUTPUT->header();
            echo $OUTPUT->custom_block_region('content');
            echo $OUTPUT->footer();

            //Check if there's any get actions that need to be serviced
            $getVariable = filter_input(INPUT_GET, "action");
            if($getVariable == "openFileManager") {core_files_renderer::render_form_filemanager();}
            else if($getVariable == "createCourse") 
            {
                /*Important note: there are two types of ids when it comes to courses
                 * 
                 * Programmer's Notes:
                 * 
                 * -$instance is the instance of a record of from the enrol table
                 * -the enrol table stores instances of courses...so does mdl_course
                 * -idnumber and id, the latter is the actual primary key, the other is
                 * I guess is for school admins or course creators to be able to number
                 * the courses according to their own system. idnumber can be null.
                 *  
                 */
                $enrollmentPlugin = enrol_get_plugin("manual");
                if($enrollmentPlugin)
                {
                    //Create data for course_request
                    $data = new stdClass();
                    $data->requester = $USER->id;
                    $data->id = 1;
                    $course_request_object = new course_request($data);
                    unset($data);

                    //create data for new course
                    $data = new stdClass();
                    $data->fullname = 'Math';
                    $data->shortname = 'Math 7';
                    $data->summary = 'Awesome!';
                    $data->summaryformat = FORMAT_PLAIN;
                    $data->format = 'topics';
                    $data->newsitems = 0;
                    $data->numsections = 5;
                    //$data->category = $course_request_object->get_category();
                    $data->category = 1;

                    $course = create_course($data);

                    //Instance is the record from the enrol table
                    $instanceid = $enrollmentPlugin->add_instance($course);
                    $instance = $DB->get_record('enrol', array('courseid'=>$course->id, 'enrol'=>'manual'), '*', MUST_EXIST);
                    $enrollmentPlugin->enrol_user($instance, $USER->id);
                }
            }
            else if($getVariable == "appendDocument")
            {
                $courseID = filter_input(INPUT_GET, "courseID");
                $fs = get_file_storage();
                $data = array(
                    'contextid' => $courseID,  // ID of context
                    'component' => 'course_myarea',     // usually = table name
                    'filearea' => 'myarea',      // usually = table name
                    'itemid' => 0,               // usually = ID of row in table
                    'filepath' => '/',           // any path beginning and ending in /
                    'filename' => 'myfile.txt'
                );
                $content = "hellp worldl";
                $fs->create_file_from_string($data, $content);

            }
            else if($getvariable == null)
            {
                //if there are no get paramaters then it's a first time entry
                //get all of user's courses, files, etc
                $courses = enrol_get_all_users_courses($USER->id);
                global $DB;

                foreach($courses as $currentCourse)
                {
                    $desiredID = $currentCourse->id;
                    $desiredFiles = $DB->get_record('files', array('contextid'=> $desiredID));
                    $contentHash = $desiredFiles->contenthash;
                    $dir1 = substr($contentHash, 0, 2);   $dir2 = substr($contentHash, 2, 2);
                    $desiredPath = $CFG->dirrot."../../../../moodledata/filedir/"
                            .$dir1."/".$dir2."/".$contentHash;
                    $myFile = file_get_contents($desiredPath);
                    $type = mime_content_type($desiredPath);

                    $contentTypes = array("pdf" => "application/pdf",
                        "txt" => "text/plain");
                    //header("Content-Type: application/pdf");
                    //readfile($desiredPath, false, $foo);
                    $myFile = file_get_contents("goedel.pdf");
                    $foo = 3;
                }
            }

        ?>

Here's where GoogleDrive::getClient is defined in case it helps

class GoogleDrive
{
    private static $AUTH_CODE;

    public static function setAuthCode($desiredCode)
    {
        self::$AUTH_CODE = $desiredCode;
    }
    public static function getClient($credentialsPath, $callbackScript)
    {        
        $client = new Google_Client();
        $client->setApplicationName('MyApp');
        $client->setScopes(Google_Service_Drive::DRIVE);
        $client->setAuthConfig($credentialsPath);
        $client->setAccessType('online');
        $client->setPrompt('select_account consent');


        $desiredVariable = self::$AUTH_CODE;
        if($desiredVariable != null)
        {
            $accessToken = $client->fetchAccessTokenWithAuthCode($desiredVariable);
            $client->setAccessToken($accessToken);

            return $client;
        }

        // Load previously authorized token from a file, if it exists.
        // The file token.json stores the user's access and refresh tokens, and is
        // created automatically when the authorization flow completes for the first
        // time.
        $tokenPath = 'token.json';
        if (file_exists($tokenPath)) {
            $accessToken = json_decode(file_get_contents($tokenPath), true);
            $client->setAccessToken($accessToken);
        }

        // If there is no previous token or it's expired.
        if ($client->isAccessTokenExpired()) {
            // Refresh the token if possible, else fetch a new one.
            if ($client->getRefreshToken()) {
                $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
            } else {
                // Request authorization from the user.
                $authUrl = $client->createAuthUrl();
                $client->setRedirectUri($callbackScript);
                redirect($authUrl);

                // Exchange authorization code for an access token.
                $accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
                $client->setAccessToken($accessToken);

                // Check to see if there was an error.
                if (array_key_exists('error', $accessToken)) {
                    throw new Exception(join(', ', $accessToken));
                }
            }
            // Save the token to a file.
            if (!file_exists(dirname($tokenPath))) {
                mkdir(dirname($tokenPath), 0700, true);
            }
            file_put_contents($tokenPath, json_encode($client->getAccessToken()));
        }
        return $client;

    }

}


However, when I run the previous code I get this error.

"{
  "error": {
    "code": 400,
    "message": "This operation is not supported for this document",
    "errors": [
      {
        "message": "This operation is not supported for this document",
        "domain": "global",
        "reason": "failedPrecondition"
      }
    ],
    "status": "FAILED_PRECONDITION"
  }
}
"

Any help or direction will be greatly appreciated.

Edit 1: I've changed the script to reflect Tanaike's solution

1 Answer 1

3

I believe your situation and goal as follows.

  • You have already been able to use Google Docs API.
  • The script of a copy of a document worked.
  • You want to remove the error in your question.

For this, how about this modification?

From:

$desiredRequests = new Google_Service_Docs_Request(array(
    'insertText' => array(
    'text' => 'Hello world!',
    'location' => array(
    'index' => 25))));
  • In your script, when $batchUpdateRequests is retrieved, it becomes as follows.

    {"requests":{"createNamedRangeType":{},"createNamedRangeDataType":{},"createParagraphBulletsType":{},"createParagraphBulletsDataType":{},"deleteContentRangeType":{},"deleteContentRangeDataType":{},"deleteNamedRangeType":{},"deleteNamedRangeDataType":{},"deleteParagraphBulletsType":{},"deleteParagraphBulletsDataType":{},"deletePositionedObjectType":{},"deletePositionedObjectDataType":{},"deleteTableColumnType":{},"deleteTableColumnDataType":{},"deleteTableRowType":{},"deleteTableRowDataType":{},"insertInlineImageType":{},"insertInlineImageDataType":{},"insertTableRowType":{},"insertTableRowDataType":{},"insertTextType":{},"insertTextDataType":{},"replaceAllTextType":{},"replaceAllTextDataType":{},"updateParagraphStyleType":{},"updateParagraphStyleDataType":{},"updateTextStyleType":{},"updateTextStyleDataType":{},"internal_gapi_mappings":{},"modelData":{},"processed":{},"insertText":{}}}
    
    • I think that this is the reason of your issue.

To:

$desiredRequests = array(new Google_Service_Docs_Request(array(
    'insertText' => array(
    'text' => 'Hello world!',
    'location' => array(
    'index' => 25)))));
  • In this modified script, when $batchUpdateRequests is retrieved, it becomes as follows.

    {"requests":[{"insertText":{"text":"Hello world!","location":{"index":25,"segmentId":null}}}]}
    
    • In this request body, I could confirm that it worked.

Note:

  • If an error like Invalid requests[0].insertText: Index 25 must be less than the end index of the referenced segment occurs, please modify 'index' => 25 to 'index' => 1.

Reference:

Sign up to request clarification or add additional context in comments.

1 Comment

Comments are not for extended discussion; this conversation has been moved to chat.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.