مربّعات الحوار والأشرطة الجانبية في مستندات Google Workspace

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

مربّعات حوار التنبيهات

التنبيه هو مربّع حوار تم إنشاؤه مسبقًا ويفتح داخل محرّر "مستندات Google" أو "جداول بيانات Google" أو "العروض التقديمية من Google" أو "نماذج Google". ويعرض رسالة وزر حسنًا، كما يمكن إضافة عنوان وأزرار بديلة. وهي تشبه استدعاء window.alert في JavaScript من جهة العميل ضمن متصفّح الويب.

تعمل التنبيهات على تعليق البرنامج النصي من جهة الخادم أثناء فتح مربّع الحوار. يتم استئناف تنفيذ النص البرمجي بعد أن يغلق المستخدم مربّع الحوار، ولكن لا يتم الاحتفاظ بعمليات ربط JDBC أثناء التعليق.

كما هو موضّح في المثال التالي، تستخدم "مستندات Google" و"نماذج Google" و"العروض التقديمية من Google" و"جداول بيانات Google" الطريقة Ui.alert، والتي تتوفّر بثلاثة أشكال مختلفة. لإلغاء الزر التلقائي حسنًا، مرِّر قيمة من التعداد Ui.ButtonSet كمعلَمة buttons. لتقييم الزر الذي نقر عليه المستخدم، قارِن القيمة المعروضة لـ alert بقائمة التعداد Ui.Button.

function onOpen() {
  SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
    .createMenu("Custom Menu")
    .addItem("Show alert", "showAlert")
    .addToUi();
}

function showAlert() {
  const ui = SpreadsheetApp.getUi(); // Same variations.

  const result = ui.alert(
    "Please confirm",
    "Are you sure you want to continue?",
    ui.ButtonSet.YES_NO,
  );

  // Process the user's response.
  if (result === ui.Button.YES) {
    // User clicked "Yes".
    ui.alert("Confirmation received.");
  } else {
    // User clicked "No" or X in the title bar.
    ui.alert("Permission denied.");
  }
}

مربّعات حوار الطلبات

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

تعمل الطلبات على تعليق البرنامج النصي من جهة الخادم أثناء فتح مربّع الحوار. يتم استئناف تنفيذ النص البرمجي بعد أن يغلق المستخدم مربّع الحوار، ولكن لا يتم الاحتفاظ بعمليات ربط JDBC أثناء التعليق.

كما هو موضّح في المثال التالي، تستخدم "مستندات Google" و"نماذج Google" و"العروض التقديمية من Google" و"جداول بيانات Google" الطريقة Ui.prompt، والتي تتوفّر بثلاثة أشكال مختلفة. لإلغاء الزر التلقائي حسنًا، مرِّر قيمة من التعداد Ui.ButtonSet كمعلَمة buttons. لتقييم رد المستخدم، سجِّل قيمة الإرجاع لـ prompt، ثم استدعِ PromptResponse.getResponseText لاسترداد إدخال المستخدم، وقارِن قيمة الإرجاع لـ PromptResponse.getSelectedButton بالتعداد Ui.Button.

function onOpen() {
  SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
    .createMenu("Custom Menu")
    .addItem("Show prompt", "showPrompt")
    .addToUi();
}

function showPrompt() {
  const ui = SpreadsheetApp.getUi(); // Same variations.

  const result = ui.prompt(
    "Let's get to know each other!",
    "Please enter your name:",
    ui.ButtonSet.OK_CANCEL,
  );

  // Process the user's response.
  const button = result.getSelectedButton();
  const text = result.getResponseText();
  if (button === ui.Button.OK) {
    // User clicked "OK".
    ui.alert("Your name is " + text + ".");
  } else if (button === ui.Button.CANCEL) {
    // User clicked "Cancel".
    ui.alert("I didn't get your name.");
  } else if (button === ui.Button.CLOSE) {
    // User clicked X in the title bar.
    ui.alert("You closed the dialog.");
  }
}

الإشعارات المؤقتة في جداول البيانات

الإشعار المنبثق هو نافذة حوار صغيرة تظهر في أسفل يسار محرّر "جداول بيانات Google" وتعرض رسالة بدون إيقاف البرنامج النصي مؤقتًا. وهي طريقة جيدة لعرض رسائل الحالة أو التحديثات التي لا تتطلّب تفاعلاً من المستخدم.

كما هو موضّح في المثال التالي، تستخدم "جداول بيانات Google" الطريقة Spreadsheet.toast. لا تتوفّر الإشعارات المنبثقة إلا في "جداول بيانات Google".

function showToast() {
  SpreadsheetApp.getActiveSpreadsheet().toast("Task completed successfully.");
}

مربّعات الحوار المخصّصة

يمكن لمربّع حوار مخصّص عرض واجهة مستخدم لخدمة HTML داخل أداة تعديل "مستندات Google" أو "جداول بيانات Google" أو "العروض التقديمية من Google" أو "نماذج Google".

لا تعلّق مربّعات الحوار المخصّصة النص البرمجي من جهة الخادم أثناء فتح مربّع الحوار. وبما أنّها غير متزامنة، تنتهي دالة جهة الخادم التي تفتح مربّع الحوار على الفور. لنقل البيانات من مربّع الحوار المخصّص إلى الخادم، استخدِم واجهة برمجة التطبيقات google.script في الرمز البرمجي من جهة العميل.

يمكن إغلاق مربّع الحوار تلقائيًا من خلال استدعاء google.script.host.close في جهة العميل من واجهة خدمة HTML. لا يمكن إغلاق مربّع الحوار من خلال واجهات أخرى، بل من خلال المستخدم أو مربّع الحوار نفسه فقط.

كما هو موضّح في المثال التالي، تستخدم "مستندات Google" و"نماذج Google" و"العروض التقديمية من Google" و"جداول بيانات Google" الطريقة Ui.showModalDialog لفتح مربّع الحوار.

Code.gs

function onOpen() {
  SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
      .createMenu('Custom Menu')
      .addItem('Show dialog', 'showDialog')
      .addToUi();
}

function showDialog() {
  const html = HtmlService.createHtmlOutputFromFile('Page')
      .setWidth(400)
      .setHeight(300);
  SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
      .showModalDialog(html, 'My custom dialog');
}

Page.html

Hello, world! <input type="button" value="Close" onclick="google.script.host.close()" />

الأشرطة الجانبية المخصّصة

يمكن أن يعرض الشريط الجانبي واجهة مستخدم لخدمة HTML داخل محرّر &quot;مستندات Google&quot; و&quot;نماذج Google&quot; و&quot;العروض التقديمية من Google&quot; و&quot;جداول بيانات Google&quot;.

لا تعلّق الأشرطة الجانبية النص البرمجي من جهة الخادم أثناء فتح مربّع الحوار. يمكن للمكوّن من جهة العميل إجراء طلبات غير متزامنة إلى النص البرمجي من جهة الخادم باستخدام واجهة برمجة التطبيقات google.script لواجهات خدمة HTML.

يمكن إغلاق الشريط الجانبي تلقائيًا من خلال استدعاء google.script.host.close في جهة العميل من واجهة خدمة HTML. لا يمكن إغلاق الشريط الجانبي من خلال واجهات أخرى، بل من خلال المستخدم أو الشريط الجانبي نفسه.

كما هو موضّح في المثال التالي، تستخدم &quot;مستندات Google&quot; و&quot;نماذج Google&quot; و&quot;العروض التقديمية من Google&quot; و&quot;جداول بيانات Google&quot; الطريقة Ui.showSidebar لفتح الشريط الجانبي.

Code.gs

function onOpen() {
  SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
      .createMenu('Custom Menu')
      .addItem('Show sidebar', 'showSidebar')
      .addToUi();
}

function showSidebar() {
  const html = HtmlService.createHtmlOutputFromFile('Page')
      .setTitle('My custom sidebar');
  SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
      .showSidebar(html);
}

Page.html

Hello, world! <input type="button" value="Close" onclick="google.script.host.close()" />

مربّعات حوار فتح الملفات

Google Picker هي واجهة برمجة تطبيقات JavaScript تتيح للمستخدمين اختيار ملفات Google Drive أو تحميلها. استخدِم مكتبة Google Picker في خدمة HTML لإنشاء مربّع حوار مخصّص يتيح للمستخدمين اختيار ملفات حالية أو تحميل ملفات جديدة، ثم إعادة الاختيار إلى البرنامج النصي.

المتطلبات

يتطلّب استخدام Google Picker مع برمجة تطبيقات Google استيفاء عدة شروط، وهي:

  • إعداد البيئة لخدمة Google Picker

  • يجب أن يستخدم مشروع النص البرمجي مشروع Google Cloud عاديًا.

    مرِّر رقم مشروع على السحابة الإلكترونية نفسه إلى PickerBuilder.setAppId في حال استخدام النطاق drive.file.

  • يجب أن يحدّد بيان مشروع برمجة التطبيقات نطاقات التفويض التي تتطلّبها واجهة برمجة تطبيقات Google Picker، وذلك كي تعرض الدالة ScriptApp.getOAuthToken الرمز المميز الصحيح للدالة PickerBuilder.setOauthtoken.

  • حصر مفتاح واجهة برمجة التطبيقات الذي تم ضبطه في PickerBuilder.setDeveloperKey على "برمجة تطبيقات Google" ضمن قيود التطبيقات، اتّبِع الخطوات التالية:

    1. اختَر المواقع الإلكترونية التي تحيل المستخدمين إلى موقعك (عناوين HTTP).
    2. ضمن القيود على المواقع الإلكترونية، انقر على إضافة عنصر.
    3. انقر على المُحيل وأدخِل *.google.com.
    4. أضِف منتجًا آخر وأدخِل *.googleusercontent.com كالمُحيل.
    5. انقر على تم.
  • اتّصِل على PickerBuilder.setOrigin.

مثال

يعرض المثال التالي Google Picker في برمجة تطبيقات.

code.gs

picker/code.gs
/**
 * Creates a custom menu in Google Sheets when the spreadsheet opens.
 */
function onOpen() {
  SpreadsheetApp.getUi()
    .createMenu("Picker")
    .addItem("Start", "showPicker")
    .addToUi();
}

/**
 * Displays an HTML-service dialog in Google Sheets that contains client-side
 * JavaScript code for the Google Picker API.
 */
function showPicker() {
  const html = HtmlService.createHtmlOutputFromFile("dialog.html")
    .setWidth(800)
    .setHeight(600)
    .setSandboxMode(HtmlService.SandboxMode.IFRAME);
  SpreadsheetApp.getUi().showModalDialog(html, "Select a file");
}
// Ensure the Drive API is enabled.
if (!Drive) {
  throw new Error("Please enable the Drive advanced service.");
}

/**
 * Checks that the file can be accessed.
 * @param {string} fileId The ID of the file.
 * @return {Object} The file resource.
 */
function getFile(fileId) {
  return Drive.Files.get(fileId, { fields: "*" });
}

/**
 * Gets the user's OAuth 2.0 access token so that it can be passed to Picker.
 * This technique keeps Picker from needing to show its own authorization
 * dialog, but is only possible if the OAuth scope that Picker needs is
 * available in Apps Script. In this case, the function includes an unused call
 * to a DriveApp method to ensure that Apps Script requests access to all files
 * in the user's Drive.
 *
 * @return {string} The user's OAuth 2.0 access token.
 */
function getOAuthToken() {
  return ScriptApp.getOAuthToken();
}

dialog.html

picker/dialog.html
<!DOCTYPE html>
<html>
  <head>
    <link
      rel="stylesheet"
      href="https://ssl.gstatic.com/docs/script/css/add-ons.css"
    />
    <style>
      #result {
        display: flex;
        flex-direction: column;
        gap: 0.25em;
      }

      pre {
        font-size: x-small;
        max-height: 25vh;
        overflow-y: scroll;
        background: #eeeeee;
        padding: 1em;
        border: 1px solid #cccccc;
      }
    </style>
    <script>
      // TODO: Replace the value for DEVELOPER_KEY with the API key obtained
      // from the Google Developers Console.
      const DEVELOPER_KEY = "AIza...";
      // TODO: Replace the value for CLOUD_PROJECT_NUMBER with the project
      // number obtained from the Google Developers Console.
      const CLOUD_PROJECT_NUMBER = "1234567890";

      let pickerApiLoaded = false;
      let oauthToken;

      /**
       * Loads the Google Picker API.
       */
      function onApiLoad() {
        gapi.load("picker", {
          callback: function () {
            pickerApiLoaded = true;
          },
        });
      }

      /**
       * Gets the user's OAuth 2.0 access token from the server-side script so that
       * it can be passed to Picker. This technique keeps Picker from needing to
       * show its own authorization dialog, but is only possible if the OAuth scope
       * that Picker needs is available in Apps Script. Otherwise, your Picker code
       * will need to declare its own OAuth scopes.
       */
      function getOAuthToken() {
        google.script.run
          .withSuccessHandler((token) => {
            oauthToken = token;
            createPicker(token);
          })
          .withFailureHandler(showError)
          .getOAuthToken();
      }

      /**
       * Creates a Picker that can access the user's spreadsheets. This function
       * uses advanced options to hide the Picker's left navigation panel and
       * default title bar.
       *
       * @param {string} token An OAuth 2.0 access token that lets Picker access the
       *     file type specified in the addView call.
       */
      function createPicker(token) {
        document.getElementById("result").innerHTML = "";

        if (pickerApiLoaded && token) {
          const picker = new google.picker.PickerBuilder()
            // Instruct Picker to display only spreadsheets in Drive. For other
            // views, see https://developers.google.com/picker/reference/picker.viewid
            .addView(
              new google.picker.DocsView(
                google.picker.ViewId.SPREADSHEETS
              ).setOwnedByMe(true)
            )
            // Hide the navigation panel so that Picker fills more of the dialog.
            .enableFeature(google.picker.Feature.NAV_HIDDEN)
            // Hide the title bar since an Apps Script dialog already has a title.
            .hideTitleBar()
            .setOAuthToken(token)
            .setDeveloperKey(DEVELOPER_KEY)
            .setAppId(CLOUD_PROJECT_NUMBER)
            .setCallback(pickerCallback)
            .setOrigin(google.script.host.origin)
            .build();
          picker.setVisible(true);
        } else {
          showError("Unable to load the file picker.");
        }
      }

      /**
       * @typedef {Object} PickerResponse
       * @property {string} action
       * @property {PickerDocument[]} docs
       */

      /**
       * @typedef {Object} PickerDocument
       * @property {string} id
       * @property {string} name
       * @property {string} mimeType
       * @property {string} url
       * @property {string} lastEditedUtc
       */

      /**
       * A callback function that extracts the chosen document's metadata from the
       * response object. For details on the response object, see
       * https://developers.google.com/picker/reference/picker.responseobject
       *
       * @param {PickerResponse} data The response object.
       */
      function pickerCallback(data) {
        const action = data[google.picker.Response.ACTION];
        if (action == google.picker.Action.PICKED) {
          handlePicked(data);
        } else if (action == google.picker.Action.CANCEL) {
          document.getElementById("result").innerHTML = "Picker canceled.";
        }
      }

      /**
       * Handles `"PICKED"` responsed from the Google Picker.
       *
       * @param {PickerResponse} data The response object.
       */
      function handlePicked(data) {
        const doc = data[google.picker.Response.DOCUMENTS][0];
        const id = doc[google.picker.Document.ID];

        google.script.run
          .withSuccessHandler((driveFilesGetResponse) => {
            // Render the response from Picker and the Drive.Files.Get API.
            const resultElement = document.getElementById("result");
            resultElement.innerHTML = "";

            for (const response of [
              {
                title: "Picker response",
                content: JSON.stringify(data, null, 2),
              },
              {
                title: "Drive.Files.Get response",
                content: JSON.stringify(driveFilesGetResponse, null, 2),
              },
            ]) {
              const titleElement = document.createElement("h3");
              titleElement.appendChild(document.createTextNode(response.title));
              resultElement.appendChild(titleElement);

              const contentElement = document.createElement("pre");
              contentElement.appendChild(
                document.createTextNode(response.content)
              );
              resultElement.appendChild(contentElement);
            }
          })
          .withFailureHandler(showError)
          .getFile(data[google.picker.Response.DOCUMENTS][0].id);
      }

      /**
       * Displays an error message within the #result element.
       *
       * @param {string} message The error message to display.
       */
      function showError(message) {
        document.getElementById("result").innerHTML = "Error: " + message;
      }
    </script>
  </head>

  <body>
    <div>
      <button onclick="getOAuthToken()">Select a file</button>
      <div id="result"></div>
    </div>
    <script src="https://apis.google.com/js/api.js?onload=onApiLoad"></script>
  </body>
</html>

appsscript.json

picker/appsscript.json
{
  "timeZone": "America/Los_Angeles",
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8",
  "oauthScopes": [
    "https://www.googleapis.com/auth/script.container.ui",
    "https://www.googleapis.com/auth/drive.file"
  ],
  "dependencies": {
    "enabledAdvancedServices": [
      {
        "userSymbol": "Drive",
        "version": "v3",
        "serviceId": "drive"
      }
    ]
  }
}