Trabalhar com comentários e sugestões

O Documentos Google permite que os colaboradores trabalhem juntos escrevendo comentários e fazendo sugestões que atuam como edições adiadas aguardando aprovação.

Você pode usar a API para visualizar as mudanças sugeridas no texto do documento. Na prévia para desenvolvedores, também é possível ler, criar, responder, atualizar ou excluir conversas de comentários e sugestões de forma programática.

Quando você usa o documents.get método para buscar o conteúdo do documento, ele pode incluir sugestões não resolvidas. Para controlar como documents.get representa as sugestões, use o parâmetro opcional SuggestionsViewMode. As seguintes condições de filtro estão disponíveis com esse parâmetro:

  • Receba conteúdo com SUGGESTIONS_INLINE, para que o texto pendente de exclusão ou inserção apareça no documento.
  • Receba conteúdo como uma prévia com todas as sugestões aceitas.
  • Receba conteúdo como uma prévia, sem sugestões, com todas as sugestões rejeitadas.

Se você não fornecer SuggestionsViewMode, a API Google Docs usará uma configuração padrão adequada aos privilégios do usuário atual.

Para controlar se os comentários são incluídos ao buscar um documento, use o parâmetro commentsViewMode opcional. Se você definir commentsViewMode como COMMENTS_VIEW_MODE_INCLUDED, também precisará definir includeTabsContent como true. Além disso, se você usar uma máscara de campo que faça referência ao campo tabs (ou qualquer subcampo), a API tratará implicitamente a solicitação como se você tivesse definido includeTabsContent como true.

Sugestões e índices

Um dos motivos pelos quais o SuggestionsViewMode é importante é que os índices na resposta podem variar dependendo da presença de sugestões, conforme mostrado no exemplo a seguir.

Conteúdo com sugestões Conteúdo sem sugestões
{
 "tabs": [
  {
   "documentTab": {
    "body": {
     "content": [
      {
       "startIndex": 1,
       "endIndex": 31,
       "paragraph": {
        "elements": [
         {
          "startIndex": 1,
          "endIndex": 31,
          "textRun": {
           "content": "Text preceding the suggestion\n",
           "textStyle": {}
          }
         }
        ],
        "paragraphStyle": {
         "namedStyleType": "NORMAL_TEXT",
         "direction": "LEFT_TO_RIGHT"
        }
       }
      },
      {
       "startIndex": 31,
       "endIndex": 51,
       "paragraph": {
        "elements": [
         {
          "startIndex": 31,
          "endIndex": 50,
          "textRun": {
           "content": "Suggested insertion",
           "suggestedInsertionIds": [
            "suggest.vcti8ewm4mww"
           ],
           "textStyle": {}
          }
         },
         {
          "startIndex": 50,
          "endIndex": 51,
          "textRun": {
           "content": "\n",
           "textStyle": {}
          }
         }
        ],
        "paragraphStyle": {
         "namedStyleType": "NORMAL_TEXT",
         "direction": "LEFT_TO_RIGHT"
        }
       }
      },
      {
       "startIndex": 51,
       "endIndex": 81,
       "paragraph": {
        "elements": [
         {
          "startIndex": 51,
          "endIndex": 81,
          "textRun": {
           "content": "Text following the suggestion\n",
           "textStyle": {}
          }
         }
        ],
        "paragraphStyle": {
         "namedStyleType": "NORMAL_TEXT",
         "direction": "LEFT_TO_RIGHT"
        }
       }
      }
     ]
    }
   }
  }
 ]
},

{
 "tabs": [
  {
   "documentTab": {
    "body": {
     "content": [
      {
       "startIndex": 1,
       "endIndex": 31,
       "paragraph": {
        "elements": [
         {
          "startIndex": 1,
          "endIndex": 31,
          "textRun": {
           "content": "Text preceding the suggestion\n",
           "textStyle": {}
          }
         }
        ],
        "paragraphStyle": {
         "namedStyleType": "NORMAL_TEXT",
         "direction": "LEFT_TO_RIGHT"
        }
       }
      },
      {
       "startIndex": 31,
       "endIndex": 32,
       "paragraph": {
        "elements": [
         {
          "startIndex": 31,
          "endIndex": 32,
          "textRun": {
           "content": "\n",
           "textStyle": {}
          }
         }
        ],
        "paragraphStyle": {
         "namedStyleType": "NORMAL_TEXT",
         "direction": "LEFT_TO_RIGHT"
        }
       }
      },
      {
       "startIndex": 32,
       "endIndex": 62,
       "paragraph": {
        "elements": [
         {
          "startIndex": 32,
          "endIndex": 62,
          "textRun": {
           "content": "Text following the suggestion\n",
           "textStyle": {}
          }
         }
        ],
        "paragraphStyle": {
         "namedStyleType": "NORMAL_TEXT",
         "direction": "LEFT_TO_RIGHT"
        }
       }
      }
     ]
    }
   }
  }
 ]
},

Na resposta anterior, o parágrafo que contém a linha "Texto após a sugestão" mostra a diferença ao usar SuggestionsViewMode. Com o valor definido como SUGGESTIONS_INLINE, o startIndex do ParagraphElement começa em 51 e o endIndex termina em 81. Sem sugestões, o intervalo startIndex e endIndex varia de 32 a 62.

Receber conteúdo sem sugestões

O exemplo de código parcial a seguir mostra como receber um documento como uma prévia com todas as sugestões rejeitadas (se houver) definindo o parâmetro SuggestionsViewMode como PREVIEW_WITHOUT_SUGGESTIONS.

Java

final string SUGGEST_MODE = "PREVIEW_WITHOUT_SUGGESTIONS";
Document doc =
    service
        .documents()
        .get(DOCUMENT_ID)
        .setIncludeTabsContent(true)
        .setSuggestionsViewMode(SUGGEST_MODE)
        .execute();

Python

SUGGEST_MODE = "PREVIEW_WITHOUT_SUGGESTIONS"
result = (
  service.documents()
  .get(
      documentId=DOCUMENT_ID,
      includeTabsContent=True,
      suggestionsViewMode=SUGGEST_MODE,
  )
  .execute()
)

Omitir o parâmetro SuggestionsViewMode é equivalente a fornecer DEFAULT_FOR_CURRENT_ACCESS como o valor de parâmetro.

Sugestões de estilo

Os documentos também podem ter sugestões de estilo. Essas são mudanças sugeridas na formatação e apresentação, em vez de mudanças no conteúdo.

Ao contrário das inserções ou exclusões de texto, elas não compensam os índices, embora possam dividir um TextRun em partes menores, mas apenas adicionam anotações sobre a mudança de estilo sugerida.

Uma dessas anotações é um SuggestedTextStyle, que consiste em duas partes:

  • O textStyle, que descreve como o texto é estilizado após a mudança sugerida, mas não diz o que mudou.

  • O textStyleSuggestionState, que indica como a sugestão altera os campos do textStyle.

É possível conferir isso no extrato da guia no documento a seguir, que inclui uma mudança de estilo sugerida:

[01] "paragraph": {
[02]    "elements": [
[03]        {
[04]            "endIndex": 106,
[05]            "startIndex": 82,
[06]            "textRun": {
[07]                "content": "Some text that does not ",
[08]                "textStyle": {}
[09]            }
[10]        },
[11]        {
[12]            "endIndex": 115,
[13]            "startIndex": 106,
[14]            "textRun": {
[15]                "content": "initially",
[16]                "suggestedTextStyleChanges": {
[17]                    "suggest.xymysbs9zldp": {
[18]                        "textStyle": {
[19]                            "backgroundColor": {},
[20]                            "baselineOffset": "NONE",
[21]                            "bold": true,
[22]                            "fontSize": {
[23]                                "magnitude": 11,
[24]                                "unit": "PT"
[25]                            },
[26]                            "foregroundColor": {
[27]                                "color": {
[28]                                    "rgbColor": {}
[29]                                }
[30]                            },
[31]                            "italic": false,
[32]                            "smallCaps": false,
[33]                            "strikethrough": false,
[34]                            "underline": false
[35]                        },
[36]                        "textStyleSuggestionState": {
[37]                            "boldSuggested": true,
[38]                            "weightedFontFamilySuggested": true
[39]                        }
[40]                    }
[41]                },
[42]                "textStyle": {
[43]                    "italic": true
[44]                }
[45]            }
[46]        },
[47]        {
[48]            "endIndex": 143,
[49]            "startIndex": 115,
[50]            "textRun": {
[51]                "content": " contain any boldface text.\n",
[52]                "textStyle": {}
[53]            }
[54]        }
[55]    ],
[56]    "paragraphStyle": {
[57]        "direction": "LEFT_TO_RIGHT",
[58]        "namedStyleType": "NORMAL_TEXT"
[59]    }
[60] }

No exemplo anterior, o parágrafo consiste em três execuções de texto, começando nas linhas 6, 14 e 50. Examine a execução de texto do meio:

  • Linha 16: há um objeto suggestedTextStyleChanges.
  • Linha 18: o textStyle especifica várias formatações.
  • Linha 36: o textStyleSuggestionState informa que apenas a parte em negrito dessa especificação foi a sugestão.
  • Linha 42: o estilo em itálico dessa execução de texto faz parte do documento atual (e não é afetado pela sugestão).

Somente os recursos de estilo definidos como true no textStyleSuggestionState fazem parte da sugestão.

Criar e gerenciar comentários

É possível adicionar comentários e respostas, editar comentários e excluir comentários ou respostas de forma programática usando o documents.batchUpdate método.

Ao realizar atualizações em lote envolvendo comentários ou sugestões, monitore possíveis falhas parciais. Para mais informações, consulte Status da atualização de comentários e sugestões.

Inserir um comentário

Para inserir uma conversa de comentários, use o InsertCommentRequest objeto. Você precisa fornecer o conteúdo do texto do comentário e um local de ancoragem (como um intervalo) em que o comentário está anexado.

O exemplo JSON a seguir adiciona uma conversa de comentários não atribuída ao intervalo especificado:

{
  "requests": [
    {
      "insertComment": {
        "content": "This is a comment added via the API.",
        "range": {
          "startIndex": 10,
          "endIndex": 25
        }
      }
    }
  ]
}

É possível atribuir um comentário a um usuário específico fornecendo o e-mail dele no campo assigneeEmailAddress:

{
  "requests": [
    {
      "insertComment": {
        "content": "Please review this paragraph.",
        "assigneeEmailAddress": "user@example.com",
        "range": {
          "startIndex": 10,
          "endIndex": 25
        }
      }
    }
  ]
}

Adicionar uma resposta ou realizar uma ação

Para responder a uma conversa de comentários ou sugestões ou para resolver ou reabrir uma conversa, use AddCommentReplyRequest.

Uma resposta é representada por um Post objeto. O objeto Post contém o content da resposta e pode especificar opcionalmente um commentAction (para RESOLVE ou REOPEN a conversa).

Também é possível reatribuir uma conversa de comentários especificando um novo assigneeEmail no objeto Post.

O exemplo a seguir responde a uma conversa de comentários existente:

{
  "requests": [
    {
      "addCommentReply": {
        "commentId": "comment_thread_id",
        "post": {
          "content": "Replying to the comment thread."
        }
      }
    }
  ]
}

O exemplo a seguir resolve uma conversa de comentários, que não exige conteúdo:

{
  "requests": [
    {
      "addCommentReply": {
        "commentId": "comment_thread_id",
        "post": {
          "commentAction": "RESOLVE"
        }
      }
    }
  ]
}

O exemplo JSON a seguir mostra como reatribuir uma conversa de comentários:

{
  "requests": [
    {
      "addCommentReply": {
        "commentId": "comment_thread_id",
        "post": {
          "content": "Replying to the comment thread.",
          "assigneeEmail": "user@example.com"
        }
      }
    }
  ]
}

Editar uma postagem

Para editar o conteúdo de texto de uma postagem criada, use UpdateCommentPostRequest. Você precisa especificar o ID da conversa (commentId ou suggestionId), o postId da postagem que você quer editar e o novo content de texto simples.

Não é possível editar a postagem principal de uma conversa de sugestões, porque elas são geradas por edições no modo de sugestão.

{
  "requests": [
    {
      "updateCommentPost": {
        "commentId": "comment_thread_id",
        "postId": "post_id",
        "content": "This is the updated comment text."
      }
    }
  ]
}

Excluir comentários e respostas

  • Excluir uma conversa de comentários: para remover uma conversa de comentários inteira, use DeleteCommentRequest. Só é possível excluir uma conversa de comentários se você for o autor da postagem principal da conversa.
  • Excluir uma resposta: para excluir uma postagem de resposta específica, use DeleteCommentReplyRequest. Só é possível excluir as respostas que você criou. Não é possível excluir postagens de resposta que contenham ações ou destinatários.

O exemplo a seguir exclui uma conversa de comentários:

{
  "requests": [
    {
      "deleteComment": {
        "commentId": "comment_thread_id"
      }
    }
  ]
}

Escrever sugestões e gerenciar conversas de sugestões

É possível escrever edições como sugestões em vez de edições diretas e aceitar, rejeitar ou excluir conversas de sugestões de forma programática.

Ao realizar atualizações em lote envolvendo sugestões, monitore possíveis falhas parciais. Para mais informações, consulte Status da atualização de comentários e sugestões.

Criar sugestões usando o modo de sugestão

Para aplicar edições como sugestões, defina o campo writeMode do objeto WriteControl como SUGGEST na solicitação de atualização em lote. Todas as atualizações na solicitação são processadas como sugestões.

{
  "requests": [
    {
      "insertText": {
        "text": "suggested insertion text",
        "location": {
          "index": 1
        }
      }
    }
  ],
  "writeControl": {
    "writeMode": "SUGGEST"
  }
}

Solicitações não compatíveis no modo de sugestão

Ao usar WriteMode.SUGGEST, os seguintes tipos de solicitação não são aceitos e retornam um erro:

  • AddDocumentTab
  • CreateNamedRange
  • DeleteFooter
  • DeleteHeader
  • DeleteNamedRange
  • DeleteTab
  • UpdateDocumentTabProperties
  • UpdateTableColumnProperties

Além disso, não é possível sugerir mudanças no formato do documento ou nas configurações de cabeçalho e rodapé. Em UpdateDocumentStyle, as sugestões não são aceitas para os seguintes tipos de estilo:

  • documentFormat
  • useEvenPageHeaderFooter
  • useFirstPageHeaderFooter

Aceitar, rejeitar ou excluir conversas de sugestões

É possível gerenciar conversas de sugestões usando as seguintes solicitações:

  • Aceitar sugestão: Use AcceptSuggestionRequest para aceitar a sugestão. Isso exige acesso de edição ao documento.
  • Rejeitar sugestão: Use RejectSuggestionRequest para rejeitar a sugestão. Isso exige acesso de edição ao documento ou ser o autor da sugestão.
  • Excluir sugestão:use DeleteSuggestionRequest para excluir a sugestão. Isso exige ser o autor da sugestão.

O exemplo a seguir aceita uma conversa de sugestões:

{
  "requests": [
    {
      "acceptSuggestion": {
        "suggestionId": "suggestion_thread_id"
      }
    }
  ]
}

Status da atualização de comentários e sugestões

As solicitações que exigem salvar conversas de comentários ou sugestões (como inserir comentários, adicionar respostas ou fazer sugestões) podem apresentar falhas parciais. Nesses casos, as mudanças no modelo de documento (como inserções ou exclusões de texto) podem ser confirmadas no modelo do Documentos, mas os comentários ou sugestões associados podem falhar ao salvar.

É possível verificar se as atualizações de comentários ou sugestões foram aplicadas com sucesso verificando o commentUpdateState no BatchUpdateDocumentResponse.

Os seguintes estados são retornados em CommentUpdateState:

  • NO_UPDATES_REQUESTED: nenhuma atualização de comentário ou sugestão foi solicitada na operação em lote.
  • ALL_SAVED: todas as atualizações de comentários ou sugestões solicitadas foram aplicadas com sucesso.
  • ALL_FAILED_UNKNOWN_REASON: todas as atualizações de comentários ou sugestões solicitadas não foram salvas, mesmo que as mudanças no modelo do Documentos tenham sido confirmadas.