Collaborate ist ein REDAXO Addon, welches primär entwickelt wurde, um kollisionsfreie, parallele Zusammenarbeit im Backend zu ermöglichen. Der Kern des AddOns ist ein unabhängig von der REDAXO-Instanz (als Website) arbeitender Dienst, der permanent läuft und einen Websocket Server zur Verfügung stellt. Plugins liefern dann die eigentlichen Features und können via Callbacks und Event-Handler sowohl server-seitig (PHP) als auch client-seitig (JS) auf Aktionen von Backend- und Front-Nutzern reagieren.
Die folgende Installationsanweisung fokussiert sich auf Anpassungen am Webserver und setzt in diesem Kontext das Vorhandensein eines gültigen Let's Encrypt-Zertifikats voraus. Die unten stehenden Code-Schnipsel und Konfigurationsempfehlungen sind nicht direkt für die Nutzung mit anderen SSL-Zertifikaten anwendbar.
Voraussetzungen zusammengefasst
- Server-Zugriff inkl. Webserver-Konfigurationsdateien
- Server-Restart möglich
- idealerweise root-Zugriff oder Managed-Server > dann den Betreiber die Anpassungen durchführen lassen
- Installation von
mod_proxy
(bei Apache)
Für die Nutzung des AddOns sind Anpassungen an der Server-Konfiguration notwendig. Bei geteilten Hosting-Paketen (Shared Hosting) wird man in der Regel vom Betreiber keine Anpassungen an seiner Server-Konfigurartion erwarten können, da diese dann immer mehrere Kunden auf diesem Server betreffen und das wiederum zu weiteren offenen Flnken führt, die Administratoren aus Sicherheitsgründen gern vermeiden.
Sollte die Website, für die man Collaborate unter REDAXO betreiben möchte auf einem Managed Server liegen, müsste der Support des Server-Betreibers normalerweise die notwendigen Anpassungen erledigen können. Dort sollte dann kommuniziert werden, dass es um Websockets via SSL (wss-Protokoll) für Domain X geht und alles über Port P laufen soll.
Den Port findet man übrigens in der package.yml
im AddOn-Basisverzeichnis. Dort steht unter websocket-server-port
der Port,
unter dem das Collaborate-Websocket dann laufen wird. Dieser ist per default auf 6789 gesetzt und wird deshalb in nachfolgenden Konfigurations-Schnipseln auch so eingesetzt.
Möchte man diesen ändern, müsste diese Änderung in der Datei erfolgen und anschließend das AddOn re-installiert werden. Falls zu diesem Zeitpunkt schon die Server-Application lief,
müsste diese (einzeln oder via Service) ebenfalls neu gestartet werden, um die Port-Anpassung zu berücksichtigen.
Absolut notwendig für den Betrieb des AddOns ist das Vorhandensein von mod_proxy
.
Die nachfolgende Basis-Konfiguration faktisch ein Server-internes Routing der Websocket-Verbindungen auf reguläre Verbindungen, die dann über Portmapping dem Collaborate-Application-Prozess zur Verfügung gestellt werden. Der Rückweg von der Application zum Client erfolgt ebenfalls hierüber.
Mehr Infos dazu hier: https://letsencrypt.org/de/docs/challenge-types/
<IfModule mod_proxy.c>
ProxyPass /wss ws://0.0.0.0:6789/
ProxyPassReverse /wss ws://0.0.0.0:6789/
</IfModule>
TODO
Bei der Installation des AddOns wird im Pfad [...]/redaxo/src/addons/collaborate/conf/collaborate.service
eine .service-Datei erstellt und bereits
mit den richtigen Pfaden für den aktuellen Server vorausgefüllt. Dort stehen auch oben in den Kommentaren noch einmal alle wichtigen Kommandos für die
nachfolgenden Schritte; ebenfalls vorausgefüllt mit den korrekten Pfaden und direkt via Copy-and-paste einsetzbar.
Man sollte beachten, dass folgende Kommandos als root-User ausgeführt werden sollten!
2.1 Sym-Link erstellen
Mit nachfolgendem Kommando wird ein Sym-Link der Collaborate Service-Konfiguration in der Liste der System-Dienste ergänzt.
> sudo ln -s [...]redaxo/src/addons/collaborate/conf/collaborate.service /etc/systemd/system/collaborate_websocket_[NAME].service
2.2 Dienst-Befehle
- Websocket-Dienst (und damit die Server-Application) starten:
> sudo systemctl start collaborate_websocket_[NAME].service
- Websocket-Dienst stoppen:
> sudo systemctl stop collaborate_websocket_[NAME].service
- Websocket-Dienst Status-Abfrage:
> sudo systemctl status collaborate_websocket_[NAME].service
Das Kern-AddOn fokussiert sich auf Zusammenarbeit im Backend und setzt dafür Sicherheitsroutinen ein, die dafür sorgen, dass nur Verbindungen von Backend-Nutzern zugelassen werden. Dennoch ist es möglich, Plugins so zu entwickeln, dass Up- und/oder Down-Stream Websocket-Verbindungen aus dem Frontend zulassen (mehr dazu in Plugin-Entwicklung).
Diese AddOns benötigen in der Regel eigene JavaScript-Dateien (und ggf. auch eigene CSS-Dateien). Um diese im Frontend zu laden, sind folgende 2 Anpassungen notwendig:
- In den Templates, in denen Collaborate im Frontend eingebunden werden soll, muss der Platzhalter
REX_COLLABORATE_FRONTEND[]
im<head>
-Bereich eingebunden werden. Dieser sorgt für das Laden und Instanziieren der zentralen Collaborate-Klasse und lädt des Weiteren die Frontend-Ressourcen aktivierter Plugins. - Plugins benötigen im Ordner
/assets/js/
eine Datei mit dem Namensschemacollaborate.plugin.[PLGUINNAME].frontend.js
. Zur automatischen Einbindung einer CSS-Datei muss diese in/assets/css/
mit dem Namensschemacollaborate.plugin.[PLGUINNAME].frontend.css
angelegt werden.
...
- Multi-Tab- bzw. Fenster-Erkennung via Tab IDs
- 1 Tab/Fenster = 1 registrierte Client-Verbindung
- vor dem Broadcasting werden mehrere Verbindungen desselben Nutzers zusammengeführt
- Header-Bar-Toolbox im REDAXO-Backend mit Indikator, ob der Websocket-Server läuft bzw. der Client dorthin eine Verbindung aufbauen kann
(benötigt Berechtigung
collaborate[]
)- Auto-Reconnect nach 60sek, falls der Server offline ist
- wenn offline wird ein Button zum manuellen Reconnect sichtbar
- Anzeige der Summe aller anderen aktiven Backend-Nutzer (eigene Verbindung wird dabei nicht mitgezählt!)
- Einzelrechte des Haupt-AddOns erweitern die Toolbox um mehr Funktionen:
collaborate[users]
- Toolbox ist klickbar und zeigt die Namen aller anderen aktuell eingeloggten Nutzer und seit wann diese online sindcollaborate[user_locations]
- zeigt zusätzlich an, welche Nutzer in welchen Sektionen (1-n) unterwegs sind und auch wie viele Tabs/Fenster sie geöffnet haben
- trotz Fokus auf Backend-Tools Einbindung im Frontend möglich und via Plugins steuerbar
- Websockets via SSL (wss-Protokoll)
Der Collaborate-Server wird unter der Prämisse entwickelt, dass Änderungen auf CMS-Ebene möglichst keinen Neustart des Websocket-Dienstes erfordern.
Der Websocket-Server führt deshalb in regelmäßigen Abständen Prüfungen der Nutzerdatenbank (rex_user
) durch und speichert das aktuelle Abbild
in einer eigenen Datenstruktur. Die Wiederholung dieses Checkups zur Laufzeit ("Loop" genannt) sorgt dafür, dass der Server mit leichter zeitlicher Verzögerung
neu hinzugekommene, gelöschte, deaktivierte oder hinsichtlich ihrer Rollen und Rechte veränderte Nutzer registriert und im weiteren Programmablauf der Application
darauf reagieren kann.
Für eine grundlegende Identifikation von aus dem Backend-Client eingehenden Anfragen werden die Parameter user
(Login-Name des Backend-Nutzers)
und ein Feld userhash
übermittelt, welches anhand bestimmter Daten des Accounts generiert wird. Auf der Server-Seite wird beim Start und bei der zyklischen
Überprüfung der User-Accounts derselbe Hash erzeugt und abgegleichen. Anfragen
Die Collaborate Application und ggf. auch eingebundene Plugins schreiben zur Überprüfung des korrekten Programmablaufs regelmäßig Ausgaben mittels echo
.
Die o.g. Standard-Implementierung als Dienst leitet diese Ausgaben in eine Log-Datei unter [...]/redaxo/data/addons/collaborate/collaborate.log
um. Dateien
im /data/
-Bereich sind vor Direktaufrufen im Browser geschützt. Ebenfalls sorgt die o.g. interne Umleitung der Websocket-Verbindungen dafür, dass
im Log stets 127.0.0.1
als IP-Adresse für eingehende Verbindungen erscheint, wodurch Restriktionen
Collaborate stellt im Kern hauptsächlich eine Server-Application (PHP) und zugehörige Client-Klassen (JS) bereit. Wegen des Fokus auf Zusammenarbeit von Backend-Nutzern ist die Verwaltung derer Verbindungen und Datenpakete bereits integriert. Das AddOn selbst liefert dabei nur minimale Features für die Nutzung im REDAXO Backend (siehe Feature-Liste).
Geplant und gewünscht ist die Entwicklung von Plugins, die sowohl Frontend- als auch Backend-Features liefern können und sich mittels Registrierung von Event-Handlern an die Prozesse der Application "anhängen" können. Verbundene Clients können vor der weiteren Verarbeitung im Hauptprozess manipuliert oder Verarbeitungsschritte abgebrochen oder einkürzt werden.
In der package.yml legt man für Up- und Downstream separat fest, ob diese das Backend, das Frontend oder beides betreffen. Bei der Installation des Plugins werden diese Flags in die Plugin-Config geschrieben und an anderer Stelle berücksichtigt. U.a. werden dann durch eine REX_VAR (siehe hier Ressourcen von Frontend-scoped Plugins und durch die boot-Routine des Haupt-AddOns die von Backend-scoped Plugins automatisch eingebunden. Wichtig dafür ist eine konsistente Namensgebung in den Plugin-Assets:
- Frontend-Ressourcen sind nach dem Schema
collaborate.plugin.PLUGINNAME.frontend.js/css
zu benennen - Backend-Ressourcen sind nach dem Schema
collaborate.plugin.PLUGINNAME.backend.js/css
zu benennen
Die Konfiguration der package.yml selbst ist dann wie folgt vorzunehmen:
default_config:
# defines scopes for websocket up- and down streams
# 2 = frontend & backend
# 1 = frontend
# 0 = backend
# -1 = no automatic embedding
upstream_scope: 1
downstream_scope: 0
Im lib
-Ordner des Plugin sollte die Klasse des Plugins liegen. Diese muss von der abstrakten Klasse CollaboratePlugin
erben, um beim Einlesen
durch die Application berücksichtigt zu werden. Diese Klasse kann mit vordefinierten Methoden dann auf bestimmte Events, die die Application bei
bestimmten Programmprozessen auslöst, reagieren.
Ein Beispiel für die Entwicklung eines Plugins namens "test"
:
class CollaboratePluginTest extends CollaboratePlugin {
/**
* do something with incoming messages (after successful backend user verification process!)
* @param $data
* @param ConnectionInterface $client
*/
public function onMessage(&$data, ConnectionInterface &$client) {
// Zwischenspeichern aller aktuell registrierten Backend-Verbindungen
// ACHTUNG: Ein und selbe Backend-User kann mehrere Tabs/Fenster geöffnet haben > jedes Tab/Fenster ist eine eigene Verbindung
$clients = $this->app->getClients();
// $data repräsentiert die JSON Daten, die der Nutzer mit der Verbindung $client
// mit der Event-auslösendes Nachricht versendet hat (aus dem Browser heraus)
if(count($clients) > 1 && $data->type == 'PAGE' && isset($data->page->path) && $data->page->path == "templates") {
foreach ($clients as $hash => $c) {
// aufrufender Benutzer wird ignoriert > muss nicht über seine eigene Aktion informiert werden
if (!is_null($client) && $c['user'] == $data->user) {
continue;
}
// Log-Eintrag schreiben
CollaborateApplication::echo(sprintf(
"test: Der Benutzer '%s' (resID %s) wird über den Besuch der Seite 'Templates' durch Benutzer (resID %s) informiert",
$c['user'],
$c['connection']->resourceId,
$client->resourceId
));
}
}
}
}
Hier wird auf onMessage
reagiert und lediglich ein Log-Eintrag generiert, wenn ein Benutzer $client die Backend-Seite 'Templates' aufruft.
Über das Manipulieren der $data-Variablen kann je nach Trigger-Punkt der weitere Programmablauf in der Main Application manipuliert
(ggf. gestoppt) werden. Da Collaborate mit 3 Plugins ausgeliefert wird, u.a. viewcounter
mit einem Handling für Frontend-Verbindungen, gibt
es bereits einige Code-Schnipsel, die auch als Vorlage für eigene Entwicklungen dienen sollen.
TODO
Collaborate ist unter der MIT Lizenz lizensiert.
siehe CHANGELOG.md
Friends Of REDAXO
Projekt-Lead
Collaborate basiert auf Ratchet von Chris Boden
- sinnloses Zustellen an verschiedene Connections desselben Clients (bei 3 offenen Tabs 2 unnötige Messages, weil für jedes Tab ein weiterer Messageblock generiert wird > Bug)
- Test, ob dynamisches Einbinden von aktualisierten Plugin-Files oder neu hinzu gekommenen Plugins wirklich funktioniert
- Beim zyklischen Plugin-Check deaktivierte/deinstallierte/verwaiste Plugins entfernen
- Plugin-Vorlage:
mixed
konsequent durch?object
ersetzen, vorher prüfen, wie stabil das ist downstream_scope
undupstream_scope
Flags in package.yml korrekt und fertig implementieren (auto-include im Backend via boot.php)yform
-Plugin:- Kollisionen serverseitig vermeiden (First come first served)
- Doku für AddOn generell verbessern + für Plugins überhaupt erst schreiben
- ggf. zentrales Object für FE-Verbindungen um bei mehreren FE-Plugins keine unnötige Redundanz zu erzeugen
- evtl. eigenes Log-File für FE Connections oder aber FE-Connections gar nicht loggen (erschwert allerdings Debugging sehr!)
structure
- Sperre pro clang (nicht generell über alle Sprachen)
structure
+yform
Plugins:- wenn in Detailansicht geblockt > nach Wiederfreigabe Seite neu laden, um auf aktuellem Stand zu sein
viewcounter
- Handling für verwaiste Frontend-Connections einbauen > created Timestamp ergänzen und globale Ablaufzeit festlegen
- evtl. Flag/Methode für das Resetten aller FE-Verbindungen einbauen (über Easter-Egg oder Console aufrufbar) > cleart FE Client Stack auf Serverseite und löscht alle Bubbles in structure View (Clientseite)