Как использовать Google Sheets и Google Apps Script для создания собственного CMS блога

1656667341 kak ispolzovat google sheets i google apps script dlya sozdaniya

Дэниел Айресон

Lj9vVuTcnkiJZoykxiRoiAmKLWwDKxKvrcjI
Использование графики с SAP Scenes Pack

Недавно я встретил Google Apps Scripts, платформу, которая позволяет пользователям расширять G Suite онлайн-продуктов Google с помощью языка сценариев, производного от JavaScript. Это аналог VBA, встроенный в большинство продуктов Microsoft Office.

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

В этой статье я расскажу простой, но новый пример построения системы управления содержимым (CMS) для онлайн-блога с помощью Google Таблиц, Google Forms и Google Apps Script.

Блог будет разработан в качестве одностраничного приложения с разбивкой на страницы и возможностью фильтрации по категориям публикаций. Публикации блога будут храниться в электронной таблице Таблиц Google. Новые публикации будут добавлены через Google Forms, поскольку это обеспечивает удобный интерфейс. Сценарий Google Apps будет использоваться для создания API, чтобы сделать содержимое электронных таблиц доступным в удобном для использования формате.

07oxc4724ms4QIehmCYTvmw9-w3Y24UiSLU8
https://danielireson.github.io/google-sheets-blog-cms

Отказ от ответственности

Я не использую это в производстве, и не знаю, будет ли это масштабироваться. Подумайте об этом как доказательство концепции, чтобы показать, что возможно. Вам следует провести собственное исследование, если вы хотите использовать его в производственной среде. Я подозреваю, что трафик уменьшится, если вы приблизитесь к верхней границе квот обслуживания. Для бесплатных аккаунтов Google существует жесткий лимит в 20 000 вопросов URL-адресов в день, а также могут быть другие ограничения.

Хранение данных

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

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

Структура электронной таблицы

Каждая строка будет представлять новую публикацию блога, а столбцы будут использоваться для записи отдельных полей публикации блога. В структуре плоского файла нет понятия первичного и внешнего ключей, как у реляционной модели. Информация, содержащаяся в столбцах, например категория и автор, будет дублироваться в публикациях блога, если они общие.

Начинаем

Создайте новую электронную таблицу Google Sheets и подключите ее к Google Forms, перейдя по адресу Инструменты > Создать form на панели меню. После выбора этого параметра вам будет предложен редактор для определения вопросов формы. Они сопоставляются со столбцами электронной таблицы.

Для своей демонстрации я добавил четыре вопроса для Название, Категория, автор, и Содержание.

Каждое поле имело тип текста, кроме Категориякоторый являлся типом радио с четырьмя гипотетическими категориями: общая, маркетинговая, финансовая, технологическая.

a9K2JlG8UCudNKRgAUmyhtKtc3ASZ2vTVju7
https://docs.google.com/forms/d/1QKthdGK9pznyojcZ4esrU1moky8_Wih4aqa7_uIQ0sw

Когда отправляется форма, к электронной таблице Google Таблиц добавляется строка. А Метка времени поле автоматически добавляется для каждой строки, которую мы будем использовать для расчета даты публикации.

Чтобы разрешить черновики публикаций, я также добавил логическое значение Опубликовано? поле как первый столбец. API должно возвращать только сообщения со значением правда. Это позволяет просматривать и редактировать публикации перед их публикацией.

z3uEkxyDVTu9Co94vNxUODDqCdSCijV78rHv
https://docs.google.com/spreadsheets/d/1xy6Hz8yagIW7zwdGGC0XICObIoZ_YYhRhnQ1T8GrQnE/edit?usp=sharing

Создание API

Сценарий Google Apps создан на основе стандарта JavaScript ECMAScript 5 (ES5). При создании API мы не можем использовать такие функции ES6, как переменные области видимости, функции со стрелками или параметры по умолчанию. Если вы не уверены, что доступно в ES5, я бы порекомендовал просмотреть таблицы совместимости документов MDN.

Несмотря на отсутствие ES6, Google Apps Scripts все еще можно использовать для создания достаточно сложных приложений на основе продуктов G Suite.

Начинаем

Вы можете получить доступ к онлайн-редактору Google Apps Script, перейдя по адресу Инструменты > Редактирование сценарияили на панели меню из электронной таблицы Google Таблиц. Откроется редактор сценариев с пустым названием файлакод ред.gs. Поскольку это простая программа, мы разместим нашу логику в одном сценарии, но вы также можете легко разбить свою программу на отдельные сценарии.

Возврат ответа

Мы можем использовать doGet и doPost функции обратного вызова для ответа на запросы HTTP. Это только обычные функции, которые запускает Google Apps Script, когда соответственно отправляется запрос GET или POST к API.

Чтобы создать ответ, мы воспользуемся ContentService. Можно передать объект JavaScript JSON.stringify а затем до createTextOutput на этой службе для создания ответа JSON. Если для типа mime установлено значение ContentService.MimeType.JSON это соответствующим образом установит тип содержимого application/json.

Сгенерировать ответ в формате JSON так же просто:

function doGet(e) {  var output = JSON.stringify({    status: 'success',    message: 'It worked',  });    return ContentService.createTextOutput(output)    .setMimeType(ContentService.MimeType.JSON);}

Разбор запросов

The doGet обратный вызов всегда вызывается событием, сгенерированным по запросу. С этого события можно получить доступ к параметрам строки запроса, которые мы будем использовать для поддержки различных параметров API. Простая аутентификация без состояния будет реализована через a key параметр. Это просто проверит, что key значение параметра соответствует жестко закодированному значению ключа. На несовпадающие запросы будет показан неавторизованный ответ.

А category параметр будет использоваться, чтобы пользователи могли запрашивать публикации из одной категории. Это избавляет их от необходимости отфильтровывать по категориям на интерфейсе. Пагинация также будет реализована через a page параметр.

Эти параметры следует добавить в URL при отправке запроса API.

GET https://apiurl?key=abcdef&category=general&page=1

Этот запрос создаст следующее:

{  "queryString": "key=abcdef&category=general&page=1",  "parameter": {},  "contextPath": "",  "parameters": {    "key": [      "abcdef"    ],    "category": [      "general"    ],    "page": [      "1"    ]  },  "contentLength": -1}

Давайте сначала подтвердим событие. Мы сделаем это, проверив это key было предоставлено и соответствует определенному ключу API.

var API_KEY = 'abcdef';
function doGet(e) {  if (!isAuthorized(e)) {    return buildErrorResponse('not authorized');  }      return buildSuccessResponse('authorized');}
function isAuthorized(e) {  return 'key' in e.parameters && e.parameters.key[0] === API_KEY;}
function buildSuccessResponse(message) {  var output = JSON.stringify({    status: 'success',    message: message  });    return ContentService.createTextOutput(output)   .setMimeType(ContentService.MimeType.JSON);}
function buildErrorResponse(message) {  var output = JSON.stringify({    status: 'error',    message: message  });    return ContentService.createTextOutput(output)   .setMimeType(ContentService.MimeType.JSON);}

Ключ API определяется как abcdef в верхней части файла. The isAuthorizedфункция возвращает логическое значение для проверки подлинности. Если это возвращает false a not authorized сообщение возвращается через buildErrorResponse помощник. Если isAuthorized возвращает true, функция может продолжать работу, пока не будет возвращен успешный ответ buildSuccessResponse.

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

Например, код статуса 401 неаутентифицирован означает, что учетные данные пользователя не совпадают, и им следует попробовать еще раз, используя другие учетные данные. При использовании ответа всегда код статуса 200 OK doGetдаже для обработанных неудачных ответов. Я обхожу это, добавляя a status значение для всех ответов API. Для этого обычного примера статус может быть хоть каким success или errorно легко понять, как этот шаблон можно расширить для других более подробных статусов, если нужно.

Давайте создадим две функции для анализа category и page параметры Если действительное число page не придается, должно быть значение по умолчанию 1. Так же, если категория не указана, значение по умолчанию должно быть установлено на nullв этом случае нужно вернуть сообщения из всех категорий.

function getPageParam(e) {  if ('page' in e.parameters) {    var page = parseInt(e.parameters['page'][0]);    if (!isNaN(page) && page > 0) {      return page;    }  }    return 1}
function getCategoryParam(e) {  if ('category' in e.parameters) {    return e.parameters['category'][0];  }    return null}

Чтение из таблицы

Google Apps Script делает доступными различные глобальные объекты, которые можно использовать для взаимодействия с продуктами G Suite. Мы будем использовать SpreadsheetService, чтобы скачать нашу электронную таблицу по идентификатору и прочитать сообщения блога. Самый простой способ найти идентификатор электронной таблицы, проверив его URL-адрес Таблиц Google.

https://docs.google.com/spreadsheets/d/{id}/edit

После загрузки электронной таблицы через openById метод на глобал SpreadsheetService, нам нужно получить активный диапазон данных из первого листа. Чтобы сначала вернуть новейшие сообщения, мы должны сортировать по Метка времени колонка, являющаяся второй колонкой.

var SPREADSHEET_ID = '12345';var spreadsheet = SpreadsheetApp.openById(SPREADSHEET_ID);var worksheet = spreadsheet.getSheets()[0];var rows = worksheet.getDataRange() .sort({column: 2, ascending: false}) .getValues();

The rows массив от getDataRange Содержит как заголовки столбцов как первый элемент массива, так и строки публикации блога как следующие элементы массива. Заголовки можно сопоставлять с публикациями блога, чтобы API мог возвращать полные объекты публикаций блога, а не только значение столбцов.

var headings = rows[0].map(String.toLowerCase);var posts = rows.slice(1);var postsWithHeadings = addHeadings(posts, headings);
function addHeadings(posts, headings) {  return posts.map(function(postAsArray) {    var postAsObj = {};        headings.forEach(function(heading, i) {      postAsObj[heading] = postAsArray[i];    });        return postAsObj;  });}

Фильтрация нерелевантных сообщений

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

Давайте создадим функцию для удаления черновиков публикаций с помощью фильтра массива:

var postsPublic = removeDrafts(postsWithHeadings);
function removeDrafts(posts, category) {  return posts.filter(function(post) {    return post['published'] === true;  });}

И еще одна функция filter в категории постов:

var category = getCategoryParam(e);var postsFiltered = filter(postsPublic, category);
function filter(posts, category) {  return posts.filter(function(post) {    if (category !== null) {      var c1 = post['category'].toLowerCase()      var c2 = category.toLowerCase()      return c1 === c2;    } else {      return true;    }  });}

Погибание ответов

Из соображений производительности мы должны ограничить максимальное количество публикаций, возвращаемых одним ответом API. Клиент должен иметь возможность запрашивать следующую страницу публикаций, увеличивая page параметр запроса.

Давайте реализуем это с помощью функции разбиения на страницы, которая возвращает объект, содержащий отфильтрованные сообщения блога posts и ссылки на страницы под pages. Если есть больше или предварительные результаты, pages содержит соответствующий номер страницы под next и previous соответственно.

var RESULTS_PER_PAGE = 5;var page = getPageParam(e)var paginated = paginate(postsFiltered, page);
function paginate(posts, page) {  var postsCopy = posts.slice();  var postsChunked = [];  var postsPaginated = {    posts: [],    pages: {      previous: null,      next: null    }  };    while (postsCopy.length > 0) {    postsChunked.push(postsCopy.splice(0, RESULTS_PER_PAGE));  }    if (page - 1 in postsChunked) {    postsPaginated.posts = postsChunked[page - 1];  } else {    postsPaginated.posts = [];  }
  if (page > 1 && page <= postsChunked.length) {    postsPaginated.pages.previous = page - 1;  }    if (page >= 1 && page < postsChunked.length) {    postsPaginated.pages.next = page + 1;  }    return postsPaginated;}

наш buildSuccessResponse помощник из предыдущего можно обновить для обработки posts и pages. После этого API должен быть готов к развертыванию.

function buildSuccessResponse(posts, pages) {  var output = JSON.stringify({    status: 'success',    data: posts,    pages: pages  });    return ContentService.createTextOutput(output)    .setMimeType(ContentService.MimeType.JSON);}

Развертывание API

После завершения сценария API можно сделать общедоступным, перейдя по адресу Опубликовать > Развернуть как webapp из панели меню редактора сценариев. Убедитесь, что программа выполняется как я и ялюбого, даже анонимаимеет доступ.

Развертывание вернет URL, который будет выглядеть так:

https://script.google.com/macros/s/{id}/exec

Добавьте ключ API к URL-адресу и введите его в браузер, чтобы проверить правильность работы API. Надеемся, вы увидите ответ JSON с тремя ключами верхнего уровня: status, posts, pages.

https://script.google.com/macros/s/{id}/exec?key=abcdef

Резюме

Если вы следили за этим, теперь вам предстоит функциональная CMS, построенная на Google Таблицах, Google Forms и Google Apps Script. Он не продвинут, но его легко начать и отвечает основным требованиям CMS. Подключение его к интерфейсу выходило за рамки этой статьи, но если вы хотите увидеть, как это делается, вам следует посмотреть демонстрацию, которую я собрал на GitHub.

В следующий раз, когда вы собираетесь дотянуться до современных технологий, я призываю вас подумать о том, существует ли более простое решение, которое можно создать с помощью существующего программного обеспечения. Это решение может быть неполнофункциональным, но оно часто поможет вам достичь 80% пути за 20% усилий, что во многих случаях будет достаточно. Я надеюсь, что эта публикация в блоге продемонстрировала это, и вы по дороге кое-что узнали о Google Apps Script.

Посмотреть демо

Просмотреть проект на GitHub

mja7cLraj-m84aPQRLYXru7c3tIBYr38l9XS

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

Ваш адрес email не будет опубликован.