ماکروها را در صفحات گسترده دیگر کپی کنید

سطح کدنویسی : متوسط
مدت زمان : 30 دقیقه
نوع پروژه : افزونه Google Workspace

اهداف

  • درک کنید که راه حل چه کاری انجام می دهد.
  • آنچه را که سرویس های Apps Script در راه حل انجام می دهند، بدانید.
  • محیط را تنظیم کنید.
  • اسکریپت را تنظیم کنید
  • اسکریپت را اجرا کنید.

در مورد این راه حل

کپی دستی ماکروهای Google Sheets از یک صفحه گسترده به صفحه‌گسترده دیگر می‌تواند زمان‌بر و مستعد خطا باشد. این افزونه Google Workspace به طور خودکار یک پروژه اسکریپت را کپی می کند و آن را به صفحه گسترده مشخص شده توسط کاربر پیوست می کند. اگرچه این راه حل بر روی ماکروهای Sheets متمرکز است، می توانید از آن برای کپی و اشتراک گذاری هر اسکریپت محدود به کانتینر استفاده کنید.

اسکرین شات از افزونه Share Macro Google Workspace

چگونه کار می کند

اسکریپت پروژه Apps Script را که به صفحه‌گسترده اصلی متصل است کپی می‌کند و یک پروژه Apps Script تکراری متصل به صفحه‌گسترده مشخص‌شده توسط کاربر ایجاد می‌کند.

خدمات اسکریپت برنامه ها

این راه حل از خدمات زیر استفاده می کند:

  • سرویس واکشی URL – برای کپی کردن پروژه منبع و ایجاد یک کپی به API اسکریپت Apps متصل می شود.
  • سرویس اسکریپت – API اسکریپت Apps را مجاز می کند تا از درخواست مجوز دوم جلوگیری کند.
  • سرویس صفحه‌گسترده - صفحه‌گسترده مورد نظر را برای افزودن پروژه Apps Script کپی شده باز می‌کند.
  • سرویس کارت - رابط کاربری افزونه را ایجاد می کند.

پیش نیازها

برای استفاده از این نمونه به پیش نیازهای زیر نیاز دارید:

  • یک حساب Google (حساب‌های Google Workspace ممکن است به تأیید سرپرست نیاز داشته باشند).
  • یک مرورگر وب با دسترسی به اینترنت.

  • یک پروژه Google Cloud .

  • Google Apps Script API در تنظیمات داشبورد Apps Script روشن شده است.

محیط خود را تنظیم کنید

پروژه Cloud خود را در کنسول Google Cloud باز کنید

اگر قبلاً باز نشده است، پروژه Cloud را که می‌خواهید برای این نمونه استفاده کنید باز کنید:

  1. در کنسول Google Cloud، به صفحه انتخاب پروژه بروید.

    یک پروژه Cloud را انتخاب کنید

  2. پروژه Google Cloud را که می خواهید استفاده کنید انتخاب کنید. یا روی ایجاد پروژه کلیک کنید و دستورالعمل های روی صفحه را دنبال کنید. اگر یک پروژه Google Cloud ایجاد می‌کنید، ممکن است لازم باشد صورت‌حساب پروژه را روشن کنید .

Google Apps Script API را روشن کنید

این شروع سریع از Google Apps Script API استفاده می کند.

قبل از استفاده از Google API، باید آنها را در پروژه Google Cloud روشن کنید. می‌توانید یک یا چند API را در یک پروژه Google Cloud روشن کنید.

افزونه‌های Google Workspace به پیکربندی صفحه رضایت نیاز دارند. پیکربندی صفحه رضایت OAuth افزونه شما، آنچه را که Google برای کاربران نمایش می‌دهد مشخص می‌کند.

  1. در کنسول Google Cloud، به منو > APIs & Services > صفحه رضایت OAuth بروید.

    به صفحه رضایت OAuth بروید

  2. برای نوع User Internal را انتخاب کنید، سپس روی Create کلیک کنید.
  3. فرم ثبت نام برنامه را تکمیل کنید، سپس روی ذخیره و ادامه کلیک کنید.
  4. در حال حاضر، می‌توانید از افزودن دامنه‌ها صرفنظر کنید و روی ذخیره و ادامه کلیک کنید. در آینده، وقتی برنامه‌ای را برای استفاده خارج از سازمان Google Workspace خود ایجاد می‌کنید، باید نوع کاربر را به خارجی تغییر دهید و سپس محدوده‌های مجوز مورد نیاز برنامه خود را اضافه کنید.

  5. خلاصه ثبت برنامه خود را مرور کنید. برای ایجاد تغییرات، روی ویرایش کلیک کنید. اگر ثبت برنامه خوب به نظر می رسد، روی بازگشت به داشبورد کلیک کنید.

اسکریپت را تنظیم کنید

پروژه Apps Script را ایجاد کنید

  1. روی دکمه زیر کلیک کنید تا پروژه Share a macro Apps Script باز شود.
    پروژه را باز کنید
  2. روی نمای کلی کلیک کنید.
  3. در صفحه نمای کلی، روی Make a copy کلیک کنید نماد ایجاد یک کپی .

شماره پروژه Cloud را کپی کنید

  1. در کنسول Google Cloud، به منو > IAM & Admin > تنظیمات بروید.

    به تنظیمات IAM و Admin بروید

  2. در قسمت Project number مقدار را کپی کنید.

پروژه Cloud پروژه Apps Script را تنظیم کنید

  1. در پروژه Apps Script کپی شده خود، روی تنظیمات پروژه کلیک کنید نماد تنظیمات پروژه .
  2. در پروژه Google Cloud Platform (GCP) ، روی تغییر پروژه کلیک کنید.
  3. در شماره پروژه GCP ، شماره پروژه Google Cloud را جای‌گذاری کنید.
  4. روی تنظیم پروژه کلیک کنید.

یک برنامه آزمایشی نصب کنید

  1. در پروژه Apps Script کپی شده خود، روی Editor کلیک کنید.
  2. فایل UI.gs را باز کنید و روی Run کلیک کنید. وقتی از شما خواسته شد، اسکریپت را مجاز کنید.
  3. روی Deploy > Test Deployments کلیک کنید.
  4. روی Install > Done کلیک کنید.

اطلاعات اسکریپت ماکرو و صفحه گسترده را دریافت کنید

  1. صفحه گسترده Sheets را باز کنید که دارای ماکرو است و شما اجازه ویرایش آن را دارید. برای استفاده از صفحه‌گسترده نمونه، از صفحه‌گسترده نمونه ماکرو کپی کنید .
  2. روی برنامه‌های افزودنی > اسکریپت برنامه‌ها کلیک کنید.
  3. در پروژه Apps Script، روی تنظیمات پروژه کلیک کنید نماد تنظیمات پروژه .
  4. در زیر شناسه اسکریپت، روی Copy کلیک کنید.
  5. شناسه اسکریپت را برای استفاده در مرحله بعدی کنار بگذارید.
  6. صفحه گسترده جدیدی را در جایی که می خواهید ماکرو اضافه کنید، باز کنید یا ایجاد کنید. برای ویرایش صفحه گسترده باید مجوز داشته باشید.
  7. URL صفحه گسترده را کپی کنید و آن را برای استفاده در مرحله بعد کنار بگذارید.

اسکریپت را اجرا کنید

مطمئن شوید که Google Apps Script API در تنظیمات داشبورد شما روشن است. برای اجرای اسکریپت مراحل زیر را طی کنید.

ماکرو را کپی کنید

  1. در Sheets، در نوار کناری سمت راست، افزونه Share Macro را باز کنید نماد ایجاد یک کپی .
  2. در ماکرو منبع ، شناسه اسکریپت را جای‌گذاری کنید.
  3. در زیر صفحه‌گسترده هدف ، URL صفحه‌گسترده را جای‌گذاری کنید.
  4. روی Share macro کلیک کنید.
  5. روی مجوز دسترسی کلیک کنید و افزونه را تأیید کنید.
  6. مراحل 2-4 را تکرار کنید.

ماکرو کپی شده را باز کنید

  1. اگر قبلاً باز نشده است، صفحه گسترده ای را که ماکرو را در آن کپی کرده اید باز کنید.
  2. روی برنامه‌های افزودنی > اسکریپت برنامه‌ها کلیک کنید.
  3. اگر پروژه Apps Script کپی شده را نمی بینید، مطمئن شوید که Google Apps Script API در تنظیمات داشبورد روشن است و مراحل فهرست شده در قسمت Copy the macro را تکرار کنید.

کد را مرور کنید

برای بررسی کد Apps Script برای این راه حل، روی مشاهده کد منبع در زیر کلیک کنید:

مشاهده کد منبع

Code.gs

solutions/add-on/share-macro/Code.js
// To learn how to use this script, refer to the documentation:
// https://developers.devsite.corp.google.com/apps-script/add-ons/share-macro

/*
Copyright 2022 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

/**
 * Uses Apps Script API to copy source Apps Script project 
 * to destination Google Spreadsheet container.
 * 
 * @param {string} sourceScriptId - Script ID of the source project.
 * @param {string} targetSpreadsheetUrl - URL if the target spreadsheet.
 * @return {Card[]} - Card indicating successful copy.
 */
function shareMacro_(sourceScriptId, targetSpreadsheetUrl) {

  // Gets the source project content using the Apps Script API.
  const sourceProject = APPS_SCRIPT_API.get(sourceScriptId);
  const sourceFiles = APPS_SCRIPT_API.getContent(sourceScriptId);

  // Opens the target spreadsheet and gets its ID.
  const parentSSId = SpreadsheetApp.openByUrl(targetSpreadsheetUrl).getId();

  // Creates an Apps Script project that's bound to the target spreadsheet.
  const targetProjectObj = APPS_SCRIPT_API.create(sourceProject.title, parentSSId);

  // Updates the Apps Script project with the source project content.
  APPS_SCRIPT_API.updateContent(targetProjectObj.scriptId, sourceFiles);

}

/**
 * Function that encapsulates Apps Script API project manipulation. 
*/
const APPS_SCRIPT_API = {
  accessToken: ScriptApp.getOAuthToken(),

  /* APPS_SCRIPT_API.get
   * Gets Apps Script source project.
   * @param {string} scriptId - Script ID of the source project.
   * @return {Object} - JSON representation of source project.
   */
  get: function (scriptId) {
    const url = ('https://script.googleapis.com/v1/projects/' + scriptId);
    const options = {
      "method": 'get',
      "headers": {
        "Authorization": "Bearer " + this.accessToken
      },
      "muteHttpExceptions": true,
    };
    const res = UrlFetchApp.fetch(url, options);
    if (res.getResponseCode() == 200) {
      return JSON.parse(res);
    } else {
      console.log('An error occurred gettting the project details');
      console.log(res.getResponseCode());
      console.log(res.getContentText());
      console.log(res);
      return false;
    }
  },

  /* APPS_SCRIPT_API.create
   * Creates new Apps Script project in the target spreadsheet.
   * @param {string} title - Name of Apps Script project.
   * @param {string} parentId - Internal ID of target spreadsheet.
   * @return {Object} - JSON representation completed project creation.
   */
  create: function (title, parentId) {
    const url = 'https://script.googleapis.com/v1/projects';
    const options = {
      "headers": {
        "Authorization": "Bearer " + this.accessToken,
        "Content-Type": "application/json"
      },
      "muteHttpExceptions": true,
      "method": "POST",
      "payload": { "title": title }
    }
    if (parentId) {
      options.payload.parentId = parentId;
    }
    options.payload = JSON.stringify(options.payload);
    let res = UrlFetchApp.fetch(url, options);
    if (res.getResponseCode() == 200) {
      res = JSON.parse(res);
      return res;
    } else {
      console.log("An error occurred while creating the project");
      console.log(res.getResponseCode());
      console.log(res.getContentText());
      console.log(res);
      return false;
    }
  },
   /* APPS_SCRIPT_API.getContent
   * Gets the content of the source Apps Script project.
   * @param {string} scriptId - Script ID of the source project.
   * @return {Object} - JSON representation of Apps Script project content.
   */
   getContent: function (scriptId) {
    const url = "https://script.googleapis.com/v1/projects/" + scriptId + "/content";
    const options = {
      "method": 'get',
      "headers": {
        "Authorization": "Bearer " + this.accessToken
      },
      "muteHttpExceptions": true,
    };
    let res = UrlFetchApp.fetch(url, options);
    if (res.getResponseCode() == 200) {
      res = JSON.parse(res);
      return res['files'];
    } else {
      console.log('An error occurred obtaining the content from the source script');
      console.log(res.getResponseCode());
      console.log(res.getContentText());
      console.log(res);
      return false;
    }
  },

  /* APPS_SCRIPT_API.updateContent
   * Updates (copies) content from source to target Apps Script project.
   * @param {string} scriptId - Script ID of the source project.
   * @param {Object} files - JSON representation of Apps Script project content.
   * @return {boolean} - Result status of the function.
   */
  updateContent: function (scriptId, files) {
    const url = "https://script.googleapis.com/v1/projects/" + scriptId + "/content";
    const options = {
      "method": 'put',
      "headers": {
        "Authorization": "Bearer " + this.accessToken
      },
      "contentType": "application/json",
      "payload": JSON.stringify({ "files": files }),
      "muteHttpExceptions": true,
    };
    let res = UrlFetchApp.fetch(url, options);
    if (res.getResponseCode() == 200) {
      return true;
    } else {
      console.log(`An error occurred updating content of script ${scriptId}`);
      console.log(res.getResponseCode());
      console.log(res.getContentText());
      console.log(res);
      return false;
    }
  }
}

UI.gs

solutions/add-on/share-macro/UI.js
/**
 * Copyright 2022 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Change application logo here (and in manifest) as desired.
const ADDON_LOGO = 'https://www.gstatic.com/images/branding/product/2x/apps_script_48dp.png';

/**
 * Callback function for rendering the main card.
 * @return {CardService.Card} The card to show the user.
 */
function onHomepage(e) {
  return createSelectionCard(e);
}

/**
 * Builds the primary card interface used to collect user inputs.
 * 
 * @param {Object} e - Add-on event object.
 * @param {string} sourceScriptId - Script ID of the source project.
 * @param {string} targetSpreadsheetUrl - URL of the target spreadsheet.
 * @param {string[]} errors - Array of error messages. 
 * 
 * @return {CardService.Card} The card to show to the user for inputs.
 */
function createSelectionCard(e, sourceScriptId, targetSpreadsheetUrl, errors) {

  // Configures card header.
  let cardHeader = CardService.newCardHeader()
    .setTitle('Share macros with other spreadheets!')
    .setImageUrl(ADDON_LOGO)
    .setImageStyle(CardService.ImageStyle.SQUARE);

  // If form errors exist, configures section with error messages.
  let showErrors = false;

  if (errors && errors.length) {
    showErrors = true;
    let msg = errors.reduce((str, err) => `${str}• ${err}<br>`, '');
    msg = `<b>Form submission errors:</b><br><font color="#ba0000">${msg}</font>`;

    // Builds error message section.
    sectionErrors = CardService.newCardSection()
      .addWidget(CardService.newDecoratedText()
        .setText(msg)
        .setWrapText(true));
  }

  // Configures source project section.
  let sectionSource = CardService.newCardSection()
    .addWidget(CardService.newDecoratedText()
      .setText('<b>Source macro</b><br>The Apps Script project to copy'))

    .addWidget(CardService.newTextInput()
      .setFieldName('sourceScriptId')
      .setValue(sourceScriptId || '')
      .setTitle('Script ID of the source macro')
      .setHint('You must have at least edit permission for the source spreadsheet to access its script project'))

    .addWidget(CardService.newTextButton()
      .setText('Find the script ID')
      .setOpenLink(CardService.newOpenLink()
        .setUrl('https://developers.google.com/apps-script/api/samples/execute')
        .setOpenAs(CardService.OpenAs.FULL_SIZE)
        .setOnClose(CardService.OnClose.NOTHING)));

  // Configures target spreadsheet section.
  let sectionTarget = CardService.newCardSection()
    .addWidget(CardService.newDecoratedText()
      .setText('<b>Target spreadsheet</b>'))

    .addWidget(CardService.newTextInput()
      .setFieldName('targetSpreadsheetUrl')
      .setValue(targetSpreadsheetUrl || '')
      .setHint('You must have at least edit permission for the target spreadsheet')
      .setTitle('Target spreadsheet URL'));

  // Configures help section.
  let sectionHelp = CardService.newCardSection()
    .addWidget(CardService.newDecoratedText()
      .setText('<b><font color=#c80000>NOTE: </font></b>' +
        'The Apps Script API must be turned on.')
      .setWrapText(true))

    .addWidget(CardService.newTextButton()
      .setText('Turn on Apps Script API')
      .setOpenLink(CardService.newOpenLink()
        .setUrl('https://script.google.com/home/usersettings')
        .setOpenAs(CardService.OpenAs.FULL_SIZE)
        .setOnClose(CardService.OnClose.NOTHING)));

  // Configures card footer with action to copy the macro.
  var cardFooter = CardService.newFixedFooter()
    .setPrimaryButton(CardService.newTextButton()
      .setText('Share macro')
      .setOnClickAction(CardService.newAction()
        .setFunctionName('onClickFunction_')));

  // Begins building the card.
  let builder = CardService.newCardBuilder()
    .setHeader(cardHeader);

  // Adds error section if applicable.
  if (showErrors) {
    builder.addSection(sectionErrors)
  }

  // Adds final sections & footer.
  builder
    .addSection(sectionSource)
    .addSection(sectionTarget)
    .addSection(sectionHelp)
    .setFixedFooter(cardFooter);

  return builder.build();
}

/**
 * Action handler that validates user inputs and calls shareMacro_
 * function to copy Apps Script project to target spreadsheet.
 * 
 * @param {Object} e - Add-on event object.
 * 
 * @return {CardService.Card} Responds with either a success or error card.
 */
function onClickFunction_(e) {

  const sourceScriptId = e.formInput.sourceScriptId;
  const targetSpreadsheetUrl = e.formInput.targetSpreadsheetUrl;

  // Validates inputs for errors.
  const errors = [];

  // Pushes an error message if the Script ID parameter is missing.
  if (!sourceScriptId) {
    errors.push('Missing script ID');
  } else {

    // Gets the Apps Script project if the Script ID parameter is valid.
    const sourceProject = APPS_SCRIPT_API.get(sourceScriptId);
    if (!sourceProject) {
      // Pushes an error message if the Script ID parameter isn't valid.
      errors.push('Invalid script ID');
    }
  }

  // Pushes an error message if the spreadsheet URL is missing.
  if (!targetSpreadsheetUrl) {
    errors.push('Missing Spreadsheet URL');
  } else
    try {
      // Tests for valid spreadsheet URL to get the spreadsheet ID.
      const ssId = SpreadsheetApp.openByUrl(targetSpreadsheetUrl).getId();
    } catch (err) {
      // Pushes an error message if the spreadsheet URL parameter isn't valid.
      errors.push('Invalid spreadsheet URL');
    }

  if (errors && errors.length) {
    // Redisplays form if inputs are missing or invalid.
    return createSelectionCard(e, sourceScriptId, targetSpreadsheetUrl, errors);

  } else {
    // Calls shareMacro function to copy the project.
    shareMacro_(sourceScriptId, targetSpreadsheetUrl);

    // Creates a success card to display to users.
    return buildSuccessCard(e, targetSpreadsheetUrl);
  }
}

/**
 * Builds success card to inform user & let them open the spreadsheet.
 * 
 * @param {Object} e - Add-on event object.
 * @param {string} targetSpreadsheetUrl - URL of the target spreadsheet.
 * 
 * @return {CardService.Card} Returns success card.
 */function buildSuccessCard(e, targetSpreadsheetUrl) {

  // Configures card header.
  let cardHeader = CardService.newCardHeader()
    .setTitle('Share macros with other spreadsheets!')
    .setImageUrl(ADDON_LOGO)
    .setImageStyle(CardService.ImageStyle.SQUARE);

  // Configures card body section with success message and open button.
  let sectionBody1 = CardService.newCardSection()
    .addWidget(CardService.newTextParagraph()
      .setText('Sharing process is complete!'))
    .addWidget(CardService.newTextButton()
      .setText('Open spreadsheet')
      .setOpenLink(CardService.newOpenLink()
        .setUrl(targetSpreadsheetUrl)
        .setOpenAs(CardService.OpenAs.FULL_SIZE)
        .setOnClose(CardService.OnClose.RELOAD_ADD_ON)));
  let sectionBody2 = CardService.newCardSection()
    .addWidget(CardService.newTextParagraph()
      .setText('If you don\'t see the copied project in your target spreadsheet,' +
       ' make sure you turned on the Apps Script API in the Apps Script dashboard.'))
    .addWidget(CardService.newTextButton()
      .setText("Check API")
      .setOpenLink(CardService.newOpenLink()
        .setUrl('https://script.google.com/home/usersettings')
        .setOpenAs(CardService.OpenAs.FULL_SIZE)
        .setOnClose(CardService.OnClose.RELOAD_ADD_ON)));

  // Configures the card footer with action to start new process.
  let cardFooter = CardService.newFixedFooter()
    .setPrimaryButton(CardService.newTextButton()
      .setText('Share another')
      .setOnClickAction(CardService.newAction()
        .setFunctionName('onHomepage')));

  return builder = CardService.newCardBuilder()
    .setHeader(cardHeader)
    .addSection(sectionBody1)
    .addSection(sectionBody2)
    .setFixedFooter(cardFooter)
    .build();
 }

appsscript.json

solutions/add-on/share-macro/appsscript.json
{
  "timeZone": "America/Los_Angeles",
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8",
  "oauthScopes": [
    "https://www.googleapis.com/auth/spreadsheets",
    "https://www.googleapis.com/auth/script.external_request",
    "https://www.googleapis.com/auth/drive.readonly",
    "https://www.googleapis.com/auth/script.projects"
  ],
    "urlFetchWhitelist": [
    "https://script.googleapis.com/"
  ],
  "addOns": {
    "common": {
      "name": "Share Macro",
      "logoUrl": "https://www.gstatic.com/images/branding/product/2x/apps_script_48dp.png",
      "layoutProperties": {
        "primaryColor": "#188038",
        "secondaryColor": "#34a853"
      },
      "homepageTrigger": {
        "runFunction": "onHomepage"
      }
    },
    "sheets": {}
  }
}

مشارکت کنندگان

این نمونه توسط گوگل با کمک کارشناسان توسعه دهنده گوگل نگهداری می شود.

مراحل بعدی