如何摆脱“表单”附加菜单中的“安装”按钮?

时间:2019-06-25 10:15:05

标签: google-apps-script gsuite google-apps-script-addon

我遵循了本教程https://developers.google.com/gsuite/add-ons/editors/forms/quickstart/forms-notifications

您可以在代码中看到,它仅添加了2个菜单项(“配置通知”和“关于”)

function onOpen(e) {
  FormApp.getUi()
      .createAddonMenu()
      .addItem('Configure notifications', 'showSidebar')
      .addItem('About', 'showAbout')
      .addToUi();
}

几周前,当我第一次开始使用Google Apps Script时,我确定在测试插件时,“安装”按钮没有出现(只有3个项目:“配置通知”,“关于”和“帮助”)


enter image description here

但是现在,每当我编写一个新的插件并尝试对其进行测试时,就会出现“安装”按钮,当我单击它时,什么也没有发生。我不确定这是Apps Script还是其他任何错误

对于用户而言,这不是一个好的UX,我想将其删除。

非常感谢。

已更新:我在此处添加了代码,它与本教程中的代码基本相同

code.gs

/**
 * @OnlyCurrentDoc
 *
 * The above comment directs Apps Script to limit the scope of file
 * access for this add-on. It specifies that this add-on will only
 * attempt to read or modify the files in which the add-on is used,
 * and not all of the user's files. The authorization request message
 * presented to users will reflect this limited scope.
 */

/**
 * A global constant String holding the title of the add-on. This is
 * used to identify the add-on in the notification emails.
 */
var ADDON_TITLE = 'Form Notifications';

/**
 * A global constant 'notice' text to include with each email
 * notification.
 */
var NOTICE = 'Form Notifications was created as an sample add-on, and is meant for' +
'demonstration purposes only. It should not be used for complex or important' +
'workflows. The number of notifications this add-on produces are limited by the' +
'owner\'s available email quota; it will not send email notifications if the' +
'owner\'s daily email quota has been exceeded. Collaborators using this add-on on' +
'the same form will be able to adjust the notification settings, but will not be' +
'able to disable the notification triggers set by other collaborators.';

/**
 * Adds a custom menu to the active form to show the add-on sidebar.
 *
 * @param {object} e The event parameter for a simple onOpen trigger. To
 *     determine which authorization mode (ScriptApp.AuthMode) the trigger is
 *     running in, inspect e.authMode.
 */
function onOpen(e) {
  FormApp.getUi()
      .createAddonMenu()
      .addItem('Configure notifications', 'showSidebar')
      .addItem('About', 'showAbout')
      .addToUi();
}

/**
 * Runs when the add-on is installed.
 *
 * @param {object} e The event parameter for a simple onInstall trigger. To
 *     determine which authorization mode (ScriptApp.AuthMode) the trigger is
 *     running in, inspect e.authMode. (In practice, onInstall triggers always
 *     run in AuthMode.FULL, but onOpen triggers may be AuthMode.LIMITED or
 *     AuthMode.NONE).
 */
function onInstall(e) {
  onOpen(e);
}

/**
 * Opens a sidebar in the form containing the add-on's user interface for
 * configuring the notifications this add-on will produce.
 */
function showSidebar() {
  var ui = HtmlService.createHtmlOutputFromFile('Sidebar')
      .setTitle('Form Notifications');
  FormApp.getUi().showSidebar(ui);
}

/**
 * Opens a purely-informational dialog in the form explaining details about
 * this add-on.
 */
function showAbout() {
  var ui = HtmlService.createHtmlOutputFromFile('About')
      .setWidth(420)
      .setHeight(270);
  FormApp.getUi().showModalDialog(ui, 'About Form Notifications');
}

/**
 * Save sidebar settings to this form's Properties, and update the onFormSubmit
 * trigger as needed.
 *
 * @param {Object} settings An Object containing key-value
 *      pairs to store.
 */
function saveSettings(settings) {
  PropertiesService.getDocumentProperties().setProperties(settings);
  adjustFormSubmitTrigger();
}

/**
 * Queries the User Properties and adds additional data required to populate
 * the sidebar UI elements.
 *
 * @return {Object} A collection of Property values and
 *     related data used to fill the configuration sidebar.
 */
function getSettings() {
  var settings = PropertiesService.getDocumentProperties().getProperties();

  // Use a default email if the creator email hasn't been provided yet.
  if (!settings.creatorEmail) {
    settings.creatorEmail = Session.getEffectiveUser().getEmail();
  }

  // Get text field items in the form and compile a list
  //   of their titles and IDs.
  var form = FormApp.getActiveForm();
  var textItems = form.getItems(FormApp.ItemType.TEXT);
  settings.textItems = [];
  for (var i = 0; i < textItems.length; i++) {
    settings.textItems.push({
      title: textItems[i].getTitle(),
      id: textItems[i].getId()
    });
  }
  return settings;
}

/**
 * Adjust the onFormSubmit trigger based on user's requests.
 */
function adjustFormSubmitTrigger() {
  var form = FormApp.getActiveForm();
  var triggers = ScriptApp.getUserTriggers(form);
  var settings = PropertiesService.getDocumentProperties();
  var triggerNeeded =
      settings.getProperty('creatorNotify') == 'true' ||
      settings.getProperty('respondentNotify') == 'true';

  // Create a new trigger if required; delete existing trigger
  //   if it is not needed.
  var existingTrigger = null;
  for (var i = 0; i < triggers.length; i++) {
    if (triggers[i].getEventType() == ScriptApp.EventType.ON_FORM_SUBMIT) {
      existingTrigger = triggers[i];
      break;
    }
  }
  if (triggerNeeded && !existingTrigger) {
    var trigger = ScriptApp.newTrigger('respondToFormSubmit')
        .forForm(form)
        .onFormSubmit()
        .create();
  } else if (!triggerNeeded && existingTrigger) {
    ScriptApp.deleteTrigger(existingTrigger);
  }
}

/**
 * Responds to a form submission event if an onFormSubmit trigger has been
 * enabled.
 *
 * @param {Object} e The event parameter created by a form
 *      submission; see
 *      https://developers.google.com/apps-script/understanding_events
 */
function respondToFormSubmit(e) {
  var settings = PropertiesService.getDocumentProperties();
  var authInfo = ScriptApp.getAuthorizationInfo(ScriptApp.AuthMode.FULL);

  // Check if the actions of the trigger require authorizations that have not
  // been supplied yet -- if so, warn the active user via email (if possible).
  // This check is required when using triggers with add-ons to maintain
  // functional triggers.
  if (authInfo.getAuthorizationStatus() ==
      ScriptApp.AuthorizationStatus.REQUIRED) {
    // Re-authorization is required. In this case, the user needs to be alerted
    // that they need to reauthorize; the normal trigger action is not
    // conducted, since authorization needs to be provided first. Send at
    // most one 'Authorization Required' email a day, to avoid spamming users
    // of the add-on.
    sendReauthorizationRequest();
  } else {
    // All required authorizations have been granted, so continue to respond to
    // the trigger event.

    // Check if the form creator needs to be notified; if so, construct and
    // send the notification.
    if (settings.getProperty('creatorNotify') == 'true') {
      sendCreatorNotification();
    }

    // Check if the form respondent needs to be notified; if so, construct and
    // send the notification. Be sure to respect the remaining email quota.
    if (settings.getProperty('respondentNotify') == 'true' &&
        MailApp.getRemainingDailyQuota() > 0) {
      sendRespondentNotification(e.response);
    }
  }
}


/**
 * Called when the user needs to reauthorize. Sends the user of the
 * add-on an email explaining the need to reauthorize and provides
 * a link for the user to do so. Capped to send at most one email
 * a day to prevent spamming the users of the add-on.
 */
function sendReauthorizationRequest() {
  var settings = PropertiesService.getDocumentProperties();
  var authInfo = ScriptApp.getAuthorizationInfo(ScriptApp.AuthMode.FULL);
  var lastAuthEmailDate = settings.getProperty('lastAuthEmailDate');
  var today = new Date().toDateString();
  if (lastAuthEmailDate != today) {
    if (MailApp.getRemainingDailyQuota() > 0) {
      var template =
          HtmlService.createTemplateFromFile('authorizationEmail');
      template.url = authInfo.getAuthorizationUrl();
      template.notice = NOTICE;
      var message = template.evaluate();
      MailApp.sendEmail(Session.getEffectiveUser().getEmail(),
          'Authorization Required',
          message.getContent(), {
            name: ADDON_TITLE,
            htmlBody: message.getContent()
          });
    }
    settings.setProperty('lastAuthEmailDate', today);
  }
}

/**
 * Sends out creator notification email(s) if the current number
 * of form responses is an even multiple of the response step
 * setting.
 */
function sendCreatorNotification() {
  var form = FormApp.getActiveForm();
  var settings = PropertiesService.getDocumentProperties();
  var responseStep = settings.getProperty('responseStep');
  responseStep = responseStep ? parseInt(responseStep) : 10;

  // If the total number of form responses is an even multiple of the
  // response step setting, send a notification email(s) to the form
  // creator(s). For example, if the response step is 10, notifications
  // will be sent when there are 10, 20, 30, etc. total form responses
  // received.
  if (form.getResponses().length % responseStep == 0) {
    var addresses = settings.getProperty('creatorEmail').split(',');
    if (MailApp.getRemainingDailyQuota() > addresses.length) {
      var template =
          HtmlService.createTemplateFromFile('creatorNotification');
      template.sheet =
          DriveApp.getFileById(form.getDestinationId()).getUrl();
      template.summary = form.getSummaryUrl();
      template.responses = form.getResponses().length;
      template.title = form.getTitle();
      template.responseStep = responseStep;
      template.formUrl = form.getEditUrl();
      template.notice = NOTICE;
      var message = template.evaluate();
      MailApp.sendEmail(settings.getProperty('creatorEmail'),
          form.getTitle() + ': Form submissions detected',
          message.getContent(), {
            name: ADDON_TITLE,
            htmlBody: message.getContent()
          });
    }
  }
}

/**
 * Sends out respondent notification emails.
 *
 * @param {FormResponse} response FormResponse object of the event
 *      that triggered this notification
 */
function sendRespondentNotification(response) {
  var form = FormApp.getActiveForm();
  var settings = PropertiesService.getDocumentProperties();
  var emailId = settings.getProperty('respondentEmailItemId');
  var emailItem = form.getItemById(parseInt(emailId));
  var respondentEmail = response.getResponseForItem(emailItem)
      .getResponse();
  if (respondentEmail) {
    var template =
        HtmlService.createTemplateFromFile('respondentNotification');
    template.paragraphs = settings.getProperty('responseText').split('\n');
    template.notice = NOTICE;
    var message = template.evaluate();
    MailApp.sendEmail(respondentEmail,
        settings.getProperty('responseSubject'),
        message.getContent(), {
          name: form.getTitle(),
            htmlBody: message.getContent()
        });
  }
}

Sidebar.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
    <!-- The CSS package above applies Google styling to buttons and other elements. -->
    <style>
    .branding-below {
      bottom: 54px;
      top: 0;
    }
    .branding-text {
      left: 7px;
      position: relative;
      top: 3px;
    }
    .logo {
      vertical-align: middle;
    }
    .width-100 {
      width: 100%;
      box-sizing: border-box;
      -webkit-box-sizing: border-box;
      -moz-box-sizing: border-box;
    }
    label {
      font-weight: bold;
    }
    #creator-options,
    #respondent-options {
      background-color: #eee;
      border-color: #eee;
      border-width: 5px;
      border-style: solid;
      display: none;
    }
    #creator-email,
    #respondent-email,
    #button-bar,
    #submit-subject {
      margin-bottom: 10px;
    }

    #response-step {
      display: inline;
    }
    </style>
  </head>
  <body>
    <div class="sidebar branding-below">
      <form>
        <div class="block">
          <input type="checkbox" id="creator-notify">
          <label for="creator-notify">Notify me</label>
        </div>
        <div class="block form-group" id="creator-options">
          <label for="creator-email">
            My email addresses (comma-separated)
          </label>
          <input type="text" class="width-100" id="creator-email">
          <label for="response-step">Send notifications after every</label>
          <input type="number" id="response-step" value="10"
              min="1" max="99999"> responses (default 10)
        </div>

        <div class="block">
          <input type="checkbox" id="respondent-notify">
          <label for="respondent-notify">Notify respondents</label>
        </div>
        <div class="block form-group" id="respondent-options">
          <label for="respondent-email">
            Which question asks for their email?
          </label>
          <select class="width-100" id="respondent-email"></select>
          <label for="submit-subject">
            Notification email subject:
          </label>
          <input type="text" class="width-100" id="submit-subject">
          <label for="submit-notice">Notification email body:</label>
          <textarea rows="8" cols="40" id="submit-notice"
              class="width-100"></textarea>
        </div>

        <div class="block" id="button-bar">
          <button class="action" id="save-settings">Save</button>
        </div>
      </form>
    </div>

    <div class="sidebar bottom">
      <img alt="Add-on logo" class="logo" width="25"
          src="https://g-suite-documentation-images.firebaseapp.com/images/newFormNotificationsicon.png">
      <span class="gray branding-text">Form Notifications by Google</span>
    </div>

    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js">
    </script>
    <script>
      /**
       * On document load, assign required handlers to each element,
       * and attempt to load any saved settings.
       */
      $(function() {
        $('#save-settings').click(saveSettingsToServer);
        $('#creator-notify').click(toggleCreatorNotify);
        $('#respondent-notify').click(toggleRespondentNotify);
        $('#response-step').change(validateNumber);
        google.script.run
           .withSuccessHandler(loadSettings)
           .withFailureHandler(showStatus)
           .withUserObject($('#button-bar').get())
           .getSettings();
      });

      /**
       * Callback function that populates the notification options using
       * previously saved values.
       *
       * @param {Object} settings The saved settings from the client.
       */
      function loadSettings(settings) {
        $('#creator-email').val(settings.creatorEmail);
        $('#response-step').val(!settings.responseStep ?
           10 : settings.responseStep);
        $('#submit-subject').val(!settings.responseSubject ?
           'Thank you for filling out our form!' :
           settings.responseSubject);
        $('#submit-notice').val(!settings.responseText ?
           'Thank you for responding to our form!' :
           settings.responseText);

        if (settings.creatorNotify === 'true') {
          $('#creator-notify').prop('checked', true);
          $('#creator-options').show();
        }

        if (settings.respondentNotify === 'true') {
          $('#respondent-notify').prop('checked', true);
          $('#respondent-options').show();
        }

        // Fill the respondent email select box with the
        // titles given to the form's text Items. Also include
        // the form Item IDs as values so that they can be
        // easily recovered during the Save operation.
        for (var i = 0; i < settings.textItems.length; i++) {
          var option = $('<option>').attr('value', settings.textItems[i]['id'])
              .text(settings.textItems[i]['title']);
          $('#respondent-email').append(option);
        }
        $('#respondent-email').val(settings.respondentEmailItemId);
      }

      /**
       * Toggles the visibility of the form creator notification options.
       */
      function toggleCreatorNotify() {
        $('#status').remove();
        if ($('#creator-notify').is(':checked')) {
          $('#creator-options').show();
        } else {
          $('#creator-options').hide();
        }
      }

      /**
       * Toggles the visibility of the form sumbitter notification options.
       */
      function toggleRespondentNotify() {
        $('#status').remove();
        if($('#respondent-notify').is(':checked')) {
          $('#respondent-options').show();
        } else {
          $('#respondent-options').hide();
        }
      }

      /**
       * Ensures that the entered step is a number between 1
       * and 99999, inclusive.
       */
      function validateNumber() {
        var value = $('#response-step').val();
        if (!value) {
          $('#response-step').val(10);
        } else if (value < 1) {
          $('#response-step').val(1);
        } else if (value > 99999) {
          $('#response-step').val(99999);
        }
      }

      /**
       * Collects the options specified in the add-on sidebar and sends them to
       * be saved as Properties on the server.
       */
      function saveSettingsToServer() {
        this.disabled = true;
        $('#status').remove();
        var creatorNotify = $('#creator-notify').is(':checked');
        var respondentNotify = $('#respondent-notify').is(':checked');
        var settings = {
          'creatorNotify': creatorNotify,
          'respondentNotify': respondentNotify
        };

        // Only save creator options if notify is turned on
        if (creatorNotify) {
          settings.responseStep = $('#response-step').val();
          settings.creatorEmail = $('#creator-email').val().trim();

          // Abort save if entered email is blank
          if (!settings.creatorEmail) {
            showStatus('Enter an owner email', $('#button-bar'));
            this.disabled = false;
            return;
          }
        }

        // Only save respondent options if notify is turned on
        if (respondentNotify) {
          settings.respondentEmailItemId = $('#respondent-email').val();
          settings.responseSubject = $('#submit-subject').val();
          settings.responseText = $('#submit-notice').val();
        }

        // Save the settings on the server
        google.script.run
            .withSuccessHandler(
              function(msg, element) {
                showStatus('Saved settings', $('#button-bar'));
                element.disabled = false;
              })
            .withFailureHandler(
              function(msg, element) {
                showStatus(msg, $('#button-bar'));
                element.disabled = false;
              })
            .withUserObject(this)
            .saveSettings(settings);
      }

      /**
       * Inserts a div that contains an status message after a given element.
       *
       * @param {String} msg The status message to display.
       * @param {Object} element The element after which to display the Status.
       */
      function showStatus(msg, element) {
         var div = $('<div>')
             .attr('id', 'status')
             .attr('class','error')
             .text(msg);
        $(element).after(div);
      }
    </script>
  </body>
</html>

About.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
    <!-- The CSS package above applies Google styling to buttons and other elements. -->
  </head>
  <body>
    <div>
      <p>
      <i>Form Notifications</i> was created as an sample add-on, and is meant
      for demonstration purposes only. It should not be used for complex or
      important workflows.
      </p>
      <p>
      The number of notifications this add-on produces are limited by the owner's
      available email quota; it will not send email notifications if the owner's
      daily email quota has been exceeded. Collaborators using this add-on on the
      same form will be able to adjust the notification settings, but will not be
      able to disable the notification triggers set by other collaborators.
      </p>
    </div>
  </body>
</html>

0 个答案:

没有答案