Печать из Google Apps Script / Хабр

«scripterror: для выполнения этого действия требуется авторизация». при запуске n из библиотеки

Искренне Ваш,

Я нашел несколько вопросов относительно этой ошибки, "ScriptError: Authorisation is required to perform that action."но не могу найти ни одного, касающегося
моей проблемы.

Ссылаясь на этот ответ, эти ответы и это сообщение об ошибке, я создал “функцию оболочки” в библиотечном сценарии. Я пытаюсь вызвать файл function.gs из файлов в библиотеке google, где оба файла находятся на этой странице.

Вот что я сделал:

Html в библиотеке:

.gs в библиотеке

.gs в скрипте, используя :

В консоли появляется надпись HERE, за которой следует сообщение INFRENT: ScriptHere, для выполнения этого значения требуется авторизация.

П РИМЕЧАНИЯ. HTML-код предназначен не для настройки веб-приложения, а для немодального диалогового окна в таблицах.

Искренне надеюсь, что кто-нибудь сможет мне помочь. Я благодарен.

Gcp web element

Я попробовал oAuth 2.0, но это оказалось непросто.

. Я не стал тратить много времени на изучение этой темы, потому что у меня уже были готовые ответы. В двух словах, результат плохой. Для отображения в браузере Google Apps Script переписывает код JavaScript. Следовательно, GCP Web Element просто не функционирует. Вот пример кода для создания устройства.


В итоге я остановился пока на oAuth 1.0, как на наиболее тиражируемом варианте (хоть и работоспособен метод до 20 апреля, тем не менее, как

первое

Это лучше, потому что проще объяснить клиенту сложность oAuth 2.0.

Oauth 2.0

O Auth 1.0 в настоящее время находится на последних месяцах своего существования; поддержка протокола будет запрещена после 20 апреля 2022 года. При использовании сервисов Google возникает проблема с client_id и redirect, когда нам нужно воспроизвести решение после перехода на авторизацию oAuth 2.0.

  1. Отправляет пользователя на страницу с запросом на аутентификацию.
  2. Получили ответ с кодом на перенаправленный URL.
  3. Получили маркер с кодом для доступа к услугам.


Проблема возникает именно с редиректом, поскольку каждый скрипт имеет в облаке уникальный идентификатор, и URL редиректа должен соответствовать этому идентификатору. Поэтому, в тиражируемом решении, есть такие варианты:

Все эти методы являются либо внутренней разработкой, которая стоит на втором месте, либо на третьем, что не очень удобно для них самих. На самом деле, ничто в архитектуре oAuth не указывает на то, что ситуация может измениться. Первый вариант, или второй, если клиент согласен предоставить доступ к своей учетной записи или создать нейтральную новую, – вот что я бы посоветовал для повторяемого решения.

Учитывая, что я не писал второй вариант, а только обдумывал его, приведу пример кода первого варианта. Второй вариант идентичен первому в том, что client_id генерируется клиентом или разработчиками.

Аутентификация через oAuth 2.0

Шаг 1. создаем client_id

  1. Откройте Google Developer Console и создайте новый проект.
  2. Перейдите в APIs&auth ->Credentials, нажмите на Create a new client ID.
  3. Тип – веб-приложение; Разрешенные источники JavaScript – script.google.com/; Разрешенные URI перенаправления – найти URL редактора сценария для нашего сценария, не включая /edit, а затем добавить /usercallback в конец.

Обычно в результате должно получиться что-то вроде этого:

Печать из Google Apps Script / Хабр

Шаг 2. код для авторизации

Предоставьте пользователю кнопку, которая направит его на URL для авторизации oAuth 2.0 в данном случае. Функция, которую мы указали, получит перенаправление.

function test() { var html = HtmlService.createTemplateFromFile("Auth").evaluate().setSandboxMode(HtmlService.SandboxMode.NATIVE).setTitle("Test"); SpreadsheetApp.getUi().showModalDialog(html, "Test");
}
function getAuthURL() { var options= { client_id : "110560935370-jdoq9cc7tvna2r94va4j9o3310m6ghth.apps.googleusercontent.com", // заменить на свой scope : "https://www.googleapis.com/auth/cloudprint", redirect_uri : "https://script.google.com/macros/d/MDYeOxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/usercallback", // заменить на свой state : ScriptApp.newStateToken().withMethod("getAuthResponse").createToken() }; var url = "https://accounts.google.com/o/oauth2/auth?response_type=code&access_type=offline"; for(var i in options) url = "&" i "=" encodeURIComponent(options[i]); return url;
}

Auth.html:

<a href='<?!= getAuthURL(); ?>' target='_blank'>
<button>Authorize!</button>
</a>

Ключевая функция здесь

ScriptApp.newStateToken()

В описании метода рассказывается, как создать параметр для метода.

usercallback

Влекущий вызов указанной функции ()

getAuthResponse

). Вы можете посмотреть, что происходит при использовании YouTube.

test()

В диалоговом окне имеется кнопка, ведущая на страницу авторизации.

Шаг 3. получение oauth token и вызов google cloud print

После обратного звонка мы прибываем в интернат.

getAuthResponse()

. Давайте создадим этот метод и используем полученный токен для вызова метода Google Cloud Print:

function getAuthResponse(q) { var options = { method: "post", muteHttpExceptions: true, payload: { code: q.parameter.code, client_id : "110560935370-jdoq9cc7tvna2r94va4j9o3310m6ghth.apps.googleusercontent.com", // заменить на свой client_secret : "xxxxxxxxxxxxxxxxxxxxxxxx", // заменить на свой redirect_uri: "https://script.google.com/macros/d/MDYeOxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/usercallback", // заменить на свой grant_type: "authorization_code" } } var response = JSON.parse(UrlFetchApp.fetch("https://accounts.google.com/o/oauth2/token", options)); var auth_string = response.token_type " " response.access_token; options.method = "get"; options.payload = null; options.headers = {Authorization: auth_string}; response = UrlFetchApp.fetch("https://www.google.com/cloudprint/search",options); return ContentService.createTextOutput(response.getContentText());
}

Если все пойдет по плану, результат нажатия кнопки

Authorize

Появится список подключенных принтеров, откроется окно авторизации.

Я не знаю точно почему, но другой подход лучше работает для меня с точки зрения исполнения.

Грязный хак

Поддерживается токен авторизации Google Apps Script. Вы можете вызвать его для активации.

ScriptApp.getOAuthToken()

. Однако этот маркер не предоставляет никаких привилегий доступа к Google Cloud Print.

Однако существует способ расширить этот список прав. Для этого вызовите окно запроса авторизации и, если необходимо, вызовите сброс текущего токена.

ScriptApp.invalidateAuth()

) и скопируйте адрес этого окна (не закрывайте окно до получения подтверждения! ):

Печать из Google Apps Script / Хабр

В скопированном URL один из параметров будет выглядеть, как «scope=https:// https://» (набор прав, необходимых скрипту). Достаточно добавить в конце данного параметра

 https://www.googleapis.com/auth/cloudprint

В новой вкладке браузера введите измененный URL-адрес, а затем утвердите запрос. После этого сценарию будут предоставлены права доступа к Google Cloud Print, и эти права будут действовать до их возобновления (например, путем вызова).

invalidateAuth

У вас есть возможность удалить жетон, если вы хотите сбросить его.

Контент и печать


Если бы API Google Apps Script работал бы так, как указано в документации, жизнь, несомненно была бы намного проще. Google Spreadsheet (точнее, приложение для работы с таблицей SpreadsheetApp) поддерживает конвертацию «на лету» в pdf:

function test() { var pdf = SpreadsheetApp.getActiveSpreadsheet().getAs("application/pdf");
}

Планировалось экспортировать выбранный диапазон в PDF из новой электронной таблицы. К сожалению, ошибка Google Apps Script исчезает. Как вы можете ее избежать?

  1. Оказывается, Google Cloud Print знает, как распечатать электронную таблицу Google. Вы можете переместить выделение в новую электронную таблицу и дать команду печати.
  2. Более элегантный способ: в меню Google Spreadsheet есть опция “Загрузить как…” с возможностью выбора формата PDF. И этот вариант, в отличие от преобразования через Google Apps Script, работает.

Браузер выбирает второй вариант, щелкая по уникальной ссылке. Введите следующий код для создания PDF из переданного диапазона электронной таблицы:

Однако в коде есть две проблемы, которые не позволяют ему работать. Во-вторых, при попытке передачи файла происходит сбой oAuth 1.0 (привет, ошибки сценариев Google Apps). Во-вторых, URL-адрес для загрузки файла не может быть доступен, а контекст аутентификации сценария не совпадает с контекстом пользователя, вызвавшего сценарий.

Как это работает? – После завершения печати откройте электронную таблицу для “внешнего мира” на время печати. Но в этом случае вам не нужно предоставлять Google Cloud Print URL-адрес загрузки:

Попытка 2. Работает!

function test() { var searchAnswer = invokeCloudPrint_("search"); var url = cloudPrint_("A1:D12",true); var payload = { printerid: printer, xsrf: searchAnswer.xsrf_token, title: rangess.getSheet().getName(), ticket: "{"version": "1.0","print": {}}", contentType: "url", content: url }; var drivefile = DriveApp.getFileById(SpreadsheetApp.getActiveSpreadsheet().getId()); var oldaccess = drivefile.getSharingAccess(); var oldpermission = drivefile.getSharingPermission(); drivefile.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW); var printstatus = invokeCloudPrint_("submit",payload); drivefile.setSharing(oldaccess, oldpermission); Browser.msgBox(printstatus.message);
}

Часть 1. challenge

Просматривая ленту новостей на YouTube, я обнаружил нечто очень похожее.

Наткнулся в приложениях или Google Apps Script. Клиент попросил меня создать сценарий, который бы мгновенно отправлял ему определенную часть электронной таблицы Google.

— Мы сами знаем, что она не имеет решения, — сказал Хунта, немедленно ощетиниваясь. — Мы хотим знать, как её решать.
Аркадий и Борис Стругацкие. Понедельник начинается в субботу


Статья рассчитана на читателей, уже знакомых с Google Apps Script и сопутствующими технологиями.

Часть 2. мучения

Ответ был прост: пользуйтесь услугами “Ростелекома”.

Распечатанный документ можно использовать как PDF. Перед отправкой запроса на печать служба может сначала потребовать аутентификацию. Я настроил службу, распечатал принтеры и выполнил запросы API. Печать из REST-клиента безупречна и функциональна. Пора писать статью.

Шаг 2. код для авторизации


Здесь все просто — показываем пользователю кнопку, которая перебросит его на URL для авторизации по oAuth 2.0. Редирект пойдет назад в указанную нами функцию:

Шаг 3. получение oauth token и вызов google cloud print

После ответного звонка мы прибываем в город.

getAuthResponse()

. Давайте напишем этот метод и используем полученный токен для вызова одного из методов Google Cloud Print.

Часть 3. итоги


В итоге, после путешествия по лабиринту багов и проблем, печать заработала. Привожу полный код с oAuth 1.0 (как самодостаточное решение):

Печать из Google Apps Script

var contextauth=false;
function cloudPrint_(strrange,portrait,size) { var searchAnswer = invokeCloudPrint_("search"); var ss = SpreadsheetApp.getActiveSpreadsheet(); var rangess = ss.getRange(strrange); var gid = rangess.getSheet().getSheetId(); var r1=rangess.getRow()-1; var c1=rangess.getColumn()-1; var r2=r1 rangess.getHeight(); var c2=c1 rangess.getWidth(); portrait = typeof portrait !== 'undefined' ? portrait : true; size = typeof size !== 'undefined' ? size : 0; var docurl="https://docs.google.com/spreadsheets/d/" SpreadsheetApp.getActiveSpreadsheet().getId() "/export?format=pdf&size=0&fzr=false&portrait=" portrait "&fitw=true&gid=" gid "&r1=" r1 "&c1=" c1 "&r2=" r2 "&c2=" c2 "&ir=false&ic=false&gridlines=false&printtitle=false&sheetnames=false&pagenum=UNDEFINED&attachment=true"; var prop = PropertiesService.getUserProperties(); var printer = prop.getProperty("printer"); if (printer == null) { selectPrinterDlg(strrange,portrait,size); return; } ss.toast("Printing..."); var drivefile = DriveApp.getFileById(SpreadsheetApp.getActiveSpreadsheet().getId()); var oldaccess = drivefile.getSharingAccess(); var oldpermission = drivefile.getSharingPermission(); drivefile.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW); var payload={ printerid: printer, xsrf: searchAnswer.xsrf_token, title: rangess.getSheet().getName(), ticket: "{"version": "1.0","print": {}}", contentType: "url", content: docurl }; var printstatus = invokeCloudPrint_("submit",payload); drivefile.setSharing(oldaccess, oldpermission); Browser.msgBox(printstatus.message);
}
function selectPrinterDlg(strrange,portrait,size) { var searchAnswer = invokeCloudPrint_("search"); var ui = UiApp.createApplication(); var panel = ui.createVerticalPanel(); var lb = ui.createListBox(false).setId('lb').setName('lb'); strrange = typeof strrange !== 'undefined' ? strrange : ""; portrait = typeof portrait !== 'undefined' ? portrait : ""; size = typeof size !== 'undefined' ? size : ""; var hidden1 = ui.createTextBox().setVisible(false).setValue(strrange).setId("range").setName("range"); var hidden2 = ui.createTextBox().setVisible(false).setValue(portrait.toString()).setId("portrait").setName("portrait"); var hidden3 = ui.createTextBox().setVisible(false).setValue(size.toString()).setId("printsize").setName("printsize"); for (var i in searchAnswer.printers) { var connPrinter = searchAnswer.printers[i]; lb.addItem(connPrinter.displayName, connPrinter.id); } var button = ui.createButton("Save"); var handler = ui.createServerHandler("SavePrinter_").addCallbackElement(panel); button.addClickHandler(ui.createClientHandler().forEventSource().setEnabled(false).setText("Saving...")); button.addClickHandler(handler); panel.add(lb).setCellHorizontalAlignment(button, UiApp.HorizontalAlignment.CENTER); panel.add(hidden1); panel.add(hidden2); panel.add(button); ui.add(panel); SpreadsheetApp.getUi().showModalDialog(ui, "Select printer"); return;
}
function clear() { PropertiesService.getUserProperties().deleteProperty("printer"); ScriptApp.invalidateAuth();
}
function SavePrinter_(e) { var ui = UiApp.getActiveApplication(); PropertiesService.getUserProperties().setProperty("printer", e.parameter.lb); ui.close(); if (e.parameter.range != "") cloudPrint_(e.parameter.range,e.parameter.portrait == "true",parseInt(e.parameter.printsize)); return ui;
}
function invokeCloudPrint_(method,payload) { var baseurl = "https://www.google.com/cloudprint/"; var options = { method: "post", muteHttpExceptions: true, oAuthServiceName: "print", oAuthUseToken: "always" }; if (payload != undefined) options.payload = payload; authorize_(); var response = UrlFetchApp.fetch(baseurl method,options); if (response.getResponseCode() == 403) { Browser.msgBox("Please authorize me to print!"); } return JSON.parse(response.getContentText());
}
function validate() { var searchAnswer = invokeCloudPrint_("search");
}
function authorize_() { if (contextauth) return; var oauthConfig = UrlFetchApp.addOAuthService("print"); oauthConfig.setConsumerKey("anonymous"); oauthConfig.setConsumerSecret("anonymous"); oauthConfig.setRequestTokenUrl("https://www.google.com/accounts/OAuthGetRequestToken?scope=https://www.googleapis.com/auth/cloudprint"); oauthConfig.setAuthorizationUrl("https://accounts.google.com/OAuthAuthorizeToken"); oauthConfig.setAccessTokenUrl("https://www.google.com/accounts/OAuthGetAccessToken"); contextauth = true;
}
function onOpen() { SpreadsheetApp.getUi().createMenu("Printing").addItem("Select printer...", "selectPrinterDlg").addToUi();
}
function Print() { cloudPrint_("A1:D12",true);
}

В дополнение к разобранным фрагментам кода добавлен диалог (и пункт меню) для выбора принтеров. Рекомендации по установке:

  1. Вперед: Настройте Google Cloud Print, протестируйте печать
  2. Создайте новую таблицу Google, введите что-нибудь в диапазоне A1:D12
  3. Откройте Script Editor, создайте новый пустой проект
  4. Скопируйте код, сохраните, вызовите validate – для утверждения всех необходимых разрешений
  5. Вызовите функцию Print. При первом вызове открывается диалоговое окно выбора принтера на вкладке Таблица
Похожее:  Ошибка при входе в приложение банка «ВТБ»
1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (Пока оценок нет)
Загрузка...

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *