التعامل مع التعليقات والاقتراحات

تتيح "مستندات Google" للمتعاونين الكتابة معًا من خلال إضافة تعليقات وتقديم اقتراحات تعمل كتعديلات مؤجّلة في انتظار الموافقة عليها.

يمكنك استخدام واجهة برمجة التطبيقات لعرض التغييرات المقترَحة مضمّنة في نص المستند. في "الإصدار التجريبي للمطوّرين"، يمكنك أيضًا قراءة سلاسل التعليقات والاقتراحات أو إنشاؤها أو الردّ عليها أو تعديلها أو حذفها آليًا.

عند استخدام طريقة documents.get لجلب محتوى المستند، قد يتضمّن المحتوى اقتراحات لم يتم حلّها. للتحكّم في طريقة تمثيل documents.get للاقتراحات، استخدِم المَعلمة الاختيارية SuggestionsViewMode. تتوفّر شروط الفلتر التالية مع هذه المَعلمة:

  • الحصول على المحتوى باستخدام SUGGESTIONS_INLINE، ما يؤدي إلى ظهور النص الذي في انتظار الحذف أو الإدراج في المستند
  • الحصول على المحتوى كمعاينة مع قبول جميع الاقتراحات
  • الحصول على المحتوى كمعاينة بدون اقتراحات مع رفض جميع الاقتراحات

إذا لم تقدِّم SuggestionsViewMode، تستخدِم Google Docs API إعدادًا تلقائيًا مناسبًا لأذونات المستخدم الحالي.

الاقتراحات والفهارس

أحد الأسباب التي تجعل SuggestionsViewMode مهمة هو أنّ الفهارس في الردّ قد تختلف استنادًا إلى ما إذا كانت هناك اقتراحات، كما هو موضّح أدناه.

المحتوى الذي يتضمّن اقتراحات المحتوى الذي لا يتضمّن اقتراحات
{
 "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"
        }
       }
      }
     ]
    }
   }
  }
 ]
},

في الردّ أعلاه، تعرض الفقرة التي تحتوي على السطر "نص بعد الاقتراح" الفرق عند استخدام SuggestionsViewMode. عند ضبط القيمة على SUGGESTIONS_INLINE، يبدأ startIndex في ParagraphElement من 51 ويتوقف endIndex عند 81. بدون اقتراحات، يتراوح startIndex وendIndex بين 32 و62.

الحصول على المحتوى بدون اقتراحات

تعرض عيّنة التعليمات البرمجية الجزئية التالية كيفية الحصول على مستند كمعاينة مع رفض جميع الاقتراحات (إن وُجدت) من خلال ضبط المَعلمة SuggestionsViewMode على PREVIEW_WITHOUT_SUGGESTIONS.

جافا

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()
)

إنّ حذف المَعلمة SuggestionsViewMode يعادل تقديم DEFAULT_FOR_CURRENT_ACCESS كقيمة للمَعلمة.

اقتراحات الأنماط

يمكن أن تتضمّن المستندات أيضًا اقتراحات أنماط. وهي تغييرات مقترَحة على التنسيق والعرض، بدلاً من تغييرات المحتوى.

على عكس عمليات إدراج النص أو حذفه، لا تؤدي هذه التغييرات إلى إزاحة الـ فهارس، على الرغم من أنّها قد تقسم الـ TextRun إلى أجزاء أصغر، ولكنّها تضيف فقط تعليقات توضيحية حول تغيير النمط المقترَح.

أحد هذه التعليقات التوضيحية هو SuggestedTextStyle، الذي يتألف من جزأين:

  • textStyle: يصف كيفية تنسيق النص بعد التغيير المقترَح، ولكن لا يوضّح ما تم تغييره.

  • textStyleSuggestionState: يوضّح كيف يغيّر الاقتراح حقول textStyle.

يمكنك الاطّلاع على ذلك في مقتطف علامة تبويب المستند التالي، الذي يتضمّن تغييرًا مقترَحًا في النمط:

[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] }

في العيّنة أعلاه، تتألف الفقرة من 3 عمليات `TextRun`، تبدأ في الأسطر 6 و14 و50. اطّلِع على عملية `TextRun` الوسطى:

  • السطر 16: هناك عنصر suggestedTextStyleChanges.
  • السطر 18: يحدِّد textStyle تنسيقات مختلفة.
  • السطر 36: يوضّح لك textStyleSuggestionState أنّ الجزء الغامق فقط من هذا التحديد هو الاقتراح.
  • السطر 42: إنّ تنسيق النص المائل لعملية `TextRun` هذه هو جزء من المستند الحالي (ولم يتأثر بالاقتراح).

إنّ ميزات النمط التي تم ضبطها على true في textStyleSuggestionState فقط هي جزء من الاقتراح.

إنشاء التعليقات وإدارتها

يمكنك إضافة التعليقات والردود وتعديل التعليقات وحذف التعليقات أو الردود آليًا باستخدام الـ documents.batchUpdate طريقة.

عند إجراء عمليات تعديل مجمّعة تتضمّن تعليقات أو اقتراحات، عليك مراقبة حالات الإخفاق الجزئي المحتمَلة. لمزيد من المعلومات، اطّلِع على مقالة حالة تعديل التعليقات والاقتراحات.

إدراج تعليق

لإدراج سلسلة تعليقات، استخدِم عنصر InsertCommentRequest. يجب تقديم محتوى نص التعليق وموقع ارتساء (مثل نطاق) يتم إرفاق التعليق به.

يضيف مثال JSON التالي سلسلة تعليقات غير مخصّصة إلى النطاق المحدّد:

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

يمكنك تخصيص تعليق لمستخدم معيّن من خلال تقديم عنوان بريده الإلكتروني في حقل assigneeEmailAddress:

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

إضافة ردّ أو اتّخاذ إجراء

للردّ على سلسلة تعليقات أو اقتراحات أو لحلّ سلسلة أو إعادة فتحها، استخدِم AddCommentReplyRequest.

يتم تمثيل الردّ من خلال عنصر Post. يحتوي عنصر Post على content الردّ ويمكنه اختياريًا تحديد commentAction (لـ RESOLVE أو REOPEN السلسلة).

يردّ المثال التالي على سلسلة تعليقات حالية:

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

يحلّ المثال التالي سلسلة تعليقات، ولا يتطلّب ذلك محتوى:

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

تعديل مشاركة

لتعديل محتوى النص لمشاركة كتبتها، استخدِم UpdateCommentPostRequest. يجب تحديد رقم تعريف السلسلة (commentId أو suggestionId) وpostId للمشاركة التي تريد تعديلها وcontent النص العادي الجديد.

يُرجى العِلم أنّه لا يمكنك تعديل المشاركة الرئيسية لسلسلة اقتراحات (لأنّها يتم إنشاؤها من خلال التعديلات في وضع الاقتراح).

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

حذف التعليقات والردود

  • حذف سلسلة تعليقات: لإزالة سلسلة تعليقات بالكامل، استخدِم DeleteCommentRequest. لا يمكنك حذف سلسلة تعليقات إلا إذا كنت مؤلف المشاركة الرئيسية للسلسلة.
  • حذف ردّ: لحذف مشاركة ردّ معيّنة، استخدِم DeleteCommentReplyRequest. لا يمكنك حذف سوى الردود التي كتبتها. لا يمكنك حذف مشاركات الردود التي تحتوي على إجراءات أو مستلِمين.

يحذف المثال التالي سلسلة تعليقات:

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

كتابة الاقتراحات وإدارة سلاسل الاقتراحات

يمكنك كتابة التعديلات كأنّها اقتراحات بدلاً من التعديلات المباشرة، وقبول سلاسل الاقتراحات أو رفضها أو حذفها آليًا.

عند إجراء عمليات تعديل مجمّعة تتضمّن اقتراحات، عليك مراقبة حالات الإخفاق الجزئي المحتمَلة. لمزيد من المعلومات، اطّلِع على مقالة حالة تعديل التعليقات والاقتراحات.

إنشاء اقتراحات باستخدام وضع الاقتراح

لتطبيق التعديلات كأنّها اقتراحات، اضبط الحقل writeMode في العنصر WriteControl على SUGGEST في طلب التعديل المجمّع. تتم معالجة جميع التعديلات في الطلب كأنّها اقتراحات.

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

الطلبات غير المتوافقة في وضع الاقتراح

عند استخدام WriteMode.SUGGEST، لا تتوافق أنواع الطلبات التالية وستعرض خطأ:

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

بالإضافة إلى ذلك، لا يمكنك اقتراح تغييرات على تنسيق المستند أو إعدادات الرؤوس أو التذييلات. في UpdateDocumentStyle، لا تتوافق الاقتراحات مع أنواع الأنماط التالية:

  • documentFormat
  • useEvenPageHeaderFooter
  • useFirstPageHeaderFooter

قبول سلاسل الاقتراحات أو رفضها أو حذفها

يمكنك إدارة سلاسل الاقتراحات باستخدام الطلبات التالية:

  • قبول الاقتراح: استخدِم AcceptSuggestionRequest لقبول الاقتراح. يتطلّب ذلك إذنًا بتعديل المستند.
  • رفض الاقتراح: استخدِم RejectSuggestionRequest لرفض الاقتراح. يتطلّب ذلك إذنًا بتعديل المستند أو أن تكون مؤلف الاقتراح.
  • حذف الاقتراح: استخدِم DeleteSuggestionRequest لحذف الاقتراح. يتطلّب ذلك أن تكون مؤلف الاقتراح.

يقبل المثال التالي سلسلة اقتراحات:

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

حالة تعديل التعليقات والاقتراحات

قد تواجه الطلبات التي تتطلّب حفظ سلاسل التعليقات أو الاقتراحات (مثل إدراج التعليقات أو إضافة الردود أو تقديم الاقتراحات) حالات إخفاق جزئي. في هذه الحالات، قد يتم بنجاح إرسال تغييرات نموذج المستند (مثل عمليات إدراج النص أو حذفه) إلى نموذج "مستندات Google"، ولكن قد يتعذّر حفظ التعليقات أو الاقتراحات المرتبطة.

يمكنك التحقّق مما إذا تم تطبيق تعديلات التعليقات أو الاقتراحات بنجاح من خلال الاطّلاع على حقل commentUpdateState في BatchUpdateDocumentResponse.

يتم عرض الحالات التالية في CommentUpdateState:

  • NO_UPDATES_REQUESTED: لم يتم طلب أي تعديلات على التعليقات أو الاقتراحات في العملية المجمّعة.
  • ALL_SAVED: تم تطبيق جميع التعديلات المطلوبة على التعليقات أو الاقتراحات بنجاح.
  • ALL_FAILED_UNKNOWN_REASON: تعذّر حفظ جميع التعديلات المطلوبة على التعليقات أو الاقتراحات، على الرغم من أنّه قد تم إرسال تغييرات نموذج "مستندات Google".