Verwenden von Grafiken aus SAP Scenes Pack

Verwendung von Google Sheets und Google Apps Script zum Erstellen eines eigenen Blog-CMS

Vor kurzem bin ich auf Google Apps Scripts gestoßen, eine Plattform, mit der Nutzer die Google G Suite mit Online-Produkten um eine Skriptsprache erweitern können, die von JavaScript abgeleitet ist. Dies ist analog zu VBA, das in den meisten Microsoft Office-Produkten integriert ist.

Google Apps Scripts ist unglaublich leistungsfähig und ermöglicht die Erstellung komplexer Systeme auf der Grundlage von Google-Diensten. Dies kann eine gute Wahl sein, wenn Sie schnell einen Prototyp für eine Idee erstellen oder eine Lösung entwerfen müssen, die von nicht-technischen Benutzern angepasst werden kann. Eine gute Möglichkeit, eine barrierefreie Lösung zu erstellen, besteht darin, auf Produkten aufzubauen, mit denen Benutzer bereits vertraut sind.

In diesem Artikel werde ich ein einfaches, aber neuartiges Beispiel für die Erstellung eines „Content Management Systems“ (CMS) für ein Online-Blog mit Google Sheets, Google Forms und Google Apps Script vorstellen.

Der Blog wird als einseitige Anwendung mit Seitenumbruch und der Möglichkeit zum Filtern nach Beitragskategorien konzipiert. Blog-Posts werden in einer Google Sheets-Tabelle gespeichert. Neue Beiträge werden über Google Forms hinzugefügt, da es eine benutzerfreundliche Oberfläche bietet. Mit Google Apps Script wird eine API erstellt, um den Inhalt der Tabelle in einem benutzerfreundlichen Format zur Verfügung zu stellen.

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

Haftungsausschluss

Ich verwende dies nicht in der Produktion und habe keine Ahnung, ob es skaliert. Betrachten Sie es als Proof of Concept, um zu zeigen, was möglich ist. Sie sollten selbst recherchieren, wenn Sie es in einer Produktionsumgebung verwenden möchten. Ich vermute, dass der Datenverkehr gedrosselt wird, wenn Sie sich den Obergrenzen der Dienstkontingente nähern. Es gibt ein festes Limit von 20.000 URL-Abrufen pro Tag für Skripte für kostenlose Google-Konten. Möglicherweise gibt es auch andere Limits.

Speichern der Daten

Google Sheets wird als Flat-File-Datenbank zum Speichern der Blog-Beiträge verwendet. In einer Flat-File-Datenbank werden Daten in Klartext in einer einzelnen Tabelle gespeichert. Im Gegensatz dazu erfasst eine relationale Datenbank Beziehungen über Tabellen hinweg und erzwingt die Struktur dieser Beziehungen, um Doppelarbeit zu minimieren und die Datenintegrität zu maximieren.

Obwohl die Struktur der Flatfiles etwas eingeschränkter ist, können Sie leicht damit beginnen und es eignet sich für den Anwendungsfall eines kleinen Blogs.

Tabellenstruktur

Jede Zeile stellt einen neuen Blog-Beitrag dar, und Spalten werden zum Erfassen einzelner Blog-Beitragsfelder verwendet. In einer Flat-File-Struktur gibt es kein Konzept für Primär- und Fremdschlüssel wie im relationalen Modell. Informationen, die in Spalten wie Kategorie und Autor erfasst werden, werden in Blog-Posts dupliziert, wenn sie gemeinsam sind.

Fertig machen

Erstellen Sie eine neue Google Sheets-Tabelle und verbinden Sie diese mit Google Forms, indem Sie in der Menüleiste auf Extras> Formular erstellen klicken. Nach Auswahl dieser Option wird ein Editor zum Definieren der Formularfragen angezeigt. Diese werden Tabellenkalkulationsspalten zugeordnet.

Für meine Demo habe ich vier Fragen für Titel, Kategorie, Autor und Inhalt hinzugefügt.

Jedes Feld hatte einen anderen Texttyp als Category, einen Radiotyp mit vier hypothetischen Kategorien: General, Marketing, Financial, Technology.

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

Bei der Übermittlung eines Formulars wird eine Zeile an die Google Sheets-Tabelle angehängt. Für jede Zeile wird automatisch ein Zeitstempelfeld hinzugefügt, anhand dessen wir das Post-Datum berechnen.

Um Entwürfe für Posts zuzulassen, habe ich außerdem einen booleschen Veröffentlichten hinzugefügt. Feld als erste Spalte. Die API sollte nur Posts mit dem Wert true zurückgeben. Auf diese Weise können Beiträge überprüft und bearbeitet werden, bevor sie veröffentlicht werden.

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

API erstellen

Google Apps Script basiert auf dem JavaScript-Standard ECMAScript 5 (ES5). Beim Erstellen der API können keine ES6-Funktionen wie Bereichsvariablen, Pfeilfunktionen oder Standardparameter verwendet werden. Wenn Sie sich nicht sicher sind, was in ES5 verfügbar ist, empfehlen wir Ihnen, die MDN-Kompatibilitätstabellen zu Rate zu ziehen.

Trotz des Fehlens von ES6 können mit Google Apps Scripts auch relativ komplexe Anwendungen auf der Basis von G Suite-Produkten erstellt werden.

Fertig machen

Sie können auf den Google Apps Script-Online-Editor zugreifen, indem Sie in der Menüleiste der Google Sheets-Tabelle auf Extras> Skript-Editor klicken. Ein Skripteditor wird mit einer leeren Datei namens Code.gs geöffnet. Da es sich um eine einfache Anwendung handelt, werden wir unsere Logik in dieses eine Skript integrieren. Sie können Ihre Anwendung jedoch auch problemlos in separate Skripts aufteilen.

Antwort zurücksenden

Wir können die Rückruffunktionen doGet und doPost verwenden, um auf HTTP-Anfragen zu antworten. Dies sind nur gewöhnliche Funktionen, die von Google Apps Script aufgerufen werden, wenn eine GET- oder POST-Anfrage an die API gestellt wird.

Um eine Antwort zu generieren, verwenden wir den ContentService. Ein JavaScript-Objekt kann an JSON.stringify und anschließend an createTextOutput für diesen Service übergeben werden, um eine JSON-Antwort zu erstellen. Wenn der MIME-Typ auf ContentService.MimeType.JSON festgelegt ist, wird der Inhaltstyp entsprechend auf application / json festgelegt.

Das Generieren einer JSON-Antwort ist so einfach wie folgt:

Funktion doGet (e) {
  var output = JSON.stringify ({
    Status: "Erfolg",
    Meldung: "Es hat funktioniert",
  });
  
  ContentService.createTextOutput (Ausgabe) zurückgeben
    .setMimeType (ContentService.MimeType.JSON);
}

Analysieren von Anforderungen

Der doGet-Rückruf wird immer mit einem aus der Anforderung generierten Ereignis aufgerufen. Von diesem Ereignis aus können wir auf die Abfragezeichenfolgenparameter zugreifen, mit denen wir verschiedene API-Optionen unterstützen. Die einfache zustandslose Authentifizierung wird über einen Schlüsselparameter implementiert. Dadurch wird einfach überprüft, ob der Schlüsselparameterwert mit einem fest codierten Schlüsselwert übereinstimmt. Anfragen, die nicht übereinstimmen, werden als nicht autorisierte Antwort angezeigt.

Ein Kategorieparameter wird verwendet, damit Benutzer Beiträge von einer einzelnen Kategorie anfordern können. Dies erspart ihnen das Filtern nach Kategorien im Front-End. Die Paginierung wird auch über einen Seitenparameter implementiert.

Diese Optionen sollten bei der API-Anforderung an die URL angehängt werden.

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

Diese Anforderung würde das folgende Ereignis generieren:

{
  "queryString": "key = abcdef & category = general & page = 1",
  "parameter": {},
  "contextPath": "",
  "parameters": {
    "Schlüssel": [
      "abcdef"
    ],
    "Kategorie": [
      "Allgemeines"
    ],
    "Seite": [
      "1"
    ]
  },
  "contentLength": -1
}

Lassen Sie uns zuerst das Ereignis authentifizieren. Dazu überprüfen wir, ob der Schlüssel bereitgestellt wurde und mit dem definierten API-Schlüssel übereinstimmt.

var API_KEY = 'abcdef';
Funktion doGet (e) {
  if (! isAuthorized (e)) {
    return buildErrorResponse ('nicht autorisiert');
  }
    
  return buildSuccessResponse ('authorized');
}
Funktion isAuthorized (e) {
  return 'key' in e.parameters && e.parameters.key [0] === API_KEY;
}
Funktion buildSuccessResponse (message) {
  var output = JSON.stringify ({
    Status: "Erfolg",
    Nachricht: Nachricht
  });
  
  ContentService.createTextOutput (Ausgabe) zurückgeben
   .setMimeType (ContentService.MimeType.JSON);
}
Funktion buildErrorResponse (message) {
  var output = JSON.stringify ({
    Status: 'Fehler',
    Nachricht: Nachricht
  });
  
  ContentService.createTextOutput (Ausgabe) zurückgeben
   .setMimeType (ContentService.MimeType.JSON);
}

Der API-Schlüssel ist oben in der Datei als abcdef definiert. Die Funktion isAuthorized gibt einen Booleschen Wert für die Authentifizierung zurück. Wenn dies false zurückgibt, wird eine nicht autorisierte Nachricht über den buildErrorResponse-Helper zurückgegeben. Wenn isAuthorized true zurückgibt, kann die Funktion fortgesetzt werden, bis eine erfolgreiche Antwort über buildSuccessResponse zurückgegeben wird.

Ein Nachteil beim Erstellen von Anwendungen in Google Apps Script ist, dass Sie keine Statuscodes für Antworten festlegen können. Diese können verwendet werden, um anzuzeigen, ob und warum die Antwort erfolgreich war.

Ein nicht authentifizierter 401-Statuscode bedeutet beispielsweise, dass die Benutzeranmeldeinformationen nicht übereinstimmen und dass sie es erneut mit anderen Anmeldeinformationen versuchen sollten. Antworten haben bei der Verwendung von doGet immer einen 200-OK-Statuscode, auch wenn sie nicht erfolgreich beantwortet wurden. Um dies zu umgehen, füge ich allen API-Antworten einen Statuswert hinzu. In diesem einfachen Beispiel kann der Status entweder "Erfolg" oder "Fehler" sein. Es ist jedoch leicht zu erkennen, wie dieses Muster bei Bedarf für andere, detailliertere Status erweitert werden kann.

Erstellen wir zwei Funktionen, um die Kategorie- und Seitenparameter zu analysieren. Wenn keine gültige numerische Seite angegeben wird, sollte der Standardwert 1 sein. Wenn keine Kategorie angegeben wird, sollte der Standardwert ebenfalls null sein. In diesem Fall sollten Beiträge aus allen Kategorien zurückgegeben werden.

Funktion getPageParam (e) {
  if ('Seite' in e.parameters) {
    var page = parseInt (e.parameters ['page'] [0]);
    if (! isNaN (page) && page> 0) {
      Rückkehr Seite;
    }
  }
  
  return 1
}
Funktion getCategoryParam (e) {
  if ('Kategorie' in e.parameters) {
    return e.parameters ['category'] [0];
  }
  
  null zurückgeben
}

Lesen aus der Tabelle

Google Apps Script stellt verschiedene globale Objekte zur Verfügung, mit denen Sie mit G Suite-Produkten interagieren können. Wir werden den SpreadsheetService verwenden, um unsere Tabelle nach ID zu laden und die Blog-Beiträge zu lesen. Der einfachste Weg, eine Tabellenkalkulations-ID nachzuschlagen, besteht darin, die Google Sheets-URL zu überprüfen.

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

Nach dem Laden der Tabelle über die openById-Methode im globalen SpreadsheetService müssen wir den aktiven Datenbereich aus dem ersten Arbeitsblatt abrufen. Um die neuesten Beiträge zuerst zurückzugeben, sollten wir nach der Timestamp-Spalte sortieren, bei der es sich um die zweite Spalte handelt.

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 ​​();

Das Zeilen-Array von getDataRange enthält sowohl die Spaltenüberschriften als erstes Array-Element als auch die Blog-Post-Zeilen als nachfolgende Array-Elemente. Überschriften können Blog-Posts zugeordnet werden, sodass die API anstelle der Spaltenwerte vollständige Blog-Post-Objekte zurückgeben kann.

var headings = rows [0] .map (String.toLowerCase);
var posts = rows.slice (1);
var postsWithHeadings = addHeadings (posts, headings);
Funktion addHeadings (Beiträge, Überschriften) {
  return posts.map (function (postAsArray) {
    var postAsObj = {};
    
    headings.forEach (Funktion (Überschrift, i) {
      postAsObj [Überschrift] = postAsArray [i];
    });
    
    return postAsObj;
  });
}

Filtern irrelevanter Beiträge

Blog-Posts sollten nur zurückgegeben werden, wenn ihre Kategorie mit der angeforderten übereinstimmt, und Posts aus allen Kategorien sollten zurückgegeben werden, wenn eine nicht angefordert wurde. Blog-Posts sollten auch weiterhin nur zurückgegeben werden, wenn sie den veröffentlichten Wert true haben.

Erstellen wir eine Funktion zum Entfernen von Entwurfspost durch einen Array-Filter:

var postsPublic = removeDrafts (postsWithHeadings);
Funktion removeDrafts (Beiträge, Kategorie) {
  return posts.filter (function (post) {
    return post ['published'] === true;
  });
}

Und noch eine Funktion zum Filtern nach Beitragskategorie:

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

Paginierende Antworten

Aus Performancegründen sollten wir die maximale Anzahl von Beiträgen begrenzen, die von einer einzelnen API-Antwort zurückgegeben werden. Der Client sollte in der Lage sein, die nächste Seite von Posts anzufordern, indem er den Parameter für die Seitenabfrage erhöht.

Implementieren wir dies durch eine Paginierungsfunktion, die ein Objekt zurückgibt, das die gefilterten Blog-Posts unter Posts und Paginierungslinks unter Seiten enthält. Wenn es mehr oder frühere Ergebnisse gibt, enthalten die Seiten die entsprechende Seitenzahl unter den nächsten bzw. vorherigen.

var RESULTS_PER_PAGE = 5;
var page = getPageParam (e)
var paginated = paginate (postsFiltered, page);
Funktion paginieren (Beiträge, Seite) {
  var postsCopy = posts.slice ();
  var postsChunked = [];
  var postsPaginated = {
    Beiträge: [],
    Seiten: {
      zurück: null,
      nächstes: null
    }
  };
  
  while (postsCopy.length> 0) {
    postsChunked.push (postsCopy.splice (0, RESULTS_PER_PAGE));
  }
  
  if (Seite - 1 in postsChunked) {
    postsPaginated.posts = postsChunked [Seite - 1];
  } else {
    postsPaginated.posts = [];
  }
  if (Seite> 1 && Seite <= postsChunked.length) {
    postsPaginated.pages.previous = page - 1;
  }
  
  if (page> = 1 && page 

Unser buildSuccessResponse-Helfer von früher kann aktualisiert werden, um Posts und Seiten zu verwalten. Die API sollte dann für die Bereitstellung bereit sein.

Funktion buildSuccessResponse (Beiträge, Seiten) {
  var output = JSON.stringify ({
    Status: "Erfolg",
    Daten: Beiträge,
    Seiten: Seiten
  });
  
  ContentService.createTextOutput (Ausgabe) zurückgeben
    .setMimeType (ContentService.MimeType.JSON);
}

API bereitstellen

Nach Abschluss des Skripts kann die API öffentlich verfügbar gemacht werden, indem Sie in der Menüleiste des Skripteditors auf Veröffentlichen> Als Webanwendung bereitstellen klicken. Stellen Sie sicher, dass die App wie ich ausgeführt wird und dass jeder, auch anonyme, Zugriff hat.

Durch die Bereitstellung wird eine URL zurückgegeben, die wie die folgende aussieht:

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

Hängen Sie den API-Schlüssel an die URL an und geben Sie ihn in Ihren Webbrowser ein, um zu überprüfen, ob die API ordnungsgemäß funktioniert. Hoffentlich sollten Sie eine JSON-Antwort mit drei Schlüsseln der obersten Ebene sehen: Status, Posts, Seiten.

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

Zusammenfassung

Wenn Sie mitgemacht haben, sollten Sie jetzt ein funktionsfähiges CMS haben, das auf Google Sheets, Google Forms und Google Apps Script basiert. Es ist nicht fortgeschritten, aber der Einstieg war einfach und bietet die Kernanforderungen eines CMS. Das Anschließen an ein Front-End lag außerhalb des Bereichs dieses Artikels. Wenn Sie jedoch sehen möchten, wie dies geschieht, sollten Sie sich die Demo ansehen, die ich auf GitHub zusammengestellt habe.

Wenn Sie das nächste Mal nach der Technologie des Tages greifen, fordere ich Sie auf, sich einige Momente Zeit zu nehmen, um zu überlegen, ob es eine einfachere Lösung gibt, die mit vorhandener Software erstellt werden kann. Diese Lösung ist möglicherweise nicht voll funktionsfähig, bringt Sie jedoch häufig zu 80% für 20% des Aufwands dorthin, was in vielen Fällen ausreichend ist. Ich hoffe, dieser Blogeintrag hat gezeigt, dass Sie ein oder zwei Dinge über Google Apps Script gelernt haben.

Demo ansehen

Sehen Sie sich das Projekt auf GitHub an