Folie: 1 ER-Modelling mit ZMS: Das ZMS SQL DB-Objekt Dr. Frank Hoffmann / Python User Group Berlin 18.06.2009
Folie: 2 ZMS - Zope-basiertes Open-Source-Redaktionssystem für das STM-Publishing
Folie: 3 Architektur: ZMS ist ein Zope-Produkt ZMS DTML Python ZOPE ZOPE ZOPE-Architektur * mehr dazu unter: http://www.zope.org
Folie: 4 Einfaches Content-Modell: Container und Content-Objekte Root Ordner Teaser Teaserelement Dokument Abbildung Tabelle Linksammlung Textabschnitt ZMSSQLDB Systemfolder
Folie: 5 Hintergrund SQL-Einbindung ins Redaktionssystem Vorhandene Datenbanken ins CMS einbinden Tabellenartige, nicht-hierarische Inhalte größeren Ausmaßes performant bearbeiten/publizieren Kein System/GUI-Wechsel Zielgruppe: Redakteure, CMS-Admins (keine Entwickler) Soll konfigurierbar sein weitgehend ohne Programmierung Redaktionelle Datenbearbeitung steht im Vordergrund
Folie: 6 Ziel: Zugriff wie im SQL-Browser plus Relationen!
Folie: 7 Installationsvoraussetzungen Python-SQL-Datenbank-Connector, z.b. Python-MySQL Zope-SQL-DB-Adapter, z.b. ZMySQLDA-Product Instanziieren eines DB-Adapter-Objektes
Folie: 8 Tabellen-Repräsentation automatisch per getentities() erzeugt Default ist optimiert für MySQL, Beispielvorlagen für Oracle oder PostGres getentities() liest Systemtabellen und erzeugt per DB-Connection das für ZMS normalisierte Tabellenbeschreibungs-Model Als Liste von Dictionaries: 1. type 2. id 3. label 4. columns: name, label, id, key nullable, type, sort, index, mandatory Hinweis: die in der Zope-Hierachie eingesetzte getenties() wird in der Art " 'getenties + DB-Connector-ID benamt (ohne Punkt und Komma!)
Folie: 9 Anlegen eines ZMSSQLDB-Objektes
Folie: 10 Konfiguration des Datenmodells
Folie: 11 Konfiguration des Datenmodells
Folie: 12 Automatische GUI-Elemente: Was man üblicherweise braucht Primitive Tabellentypen Primärschlüssel (PK) Table String Intersection Text / Richtext / html Hidden Datum/Zeit Int Tabellenspezifische Passwort GUI-Erweiterungen URL Interface Komplex (FK-Beziehungen) Filter Select (1:n) Multiselect (n:m) Multimultiselect (n:m:o) Blob (Binärdaten-Referenz) Details (Sub-Tabellen) Darstellungs-Varianten Lazy (Select-Popup für große Auswahlen) Editable (Selectlist-Items direkt erweitern)
Folie: 13 ER-Modell per XML Flexibel editierbar Ergänzung für GUI- Erweiterung per JS/jquery
Folie: 14 Beispiel Automatische GUI-Elemente 1. PK 2. Text 3. Blob 4. Multiselect 5. Select 6. Details
Folie: 15 Blob-Element: Typ image und file A. Konfigurations-GUI Blob-Element ist String (Dateiname) Zwei Typen type=image type=file Zwei Datenablage-Optionen path = lokalen Verzeichnispfad* remote = URL des entfernten ZMSSQLDB-Objektes Remote-Option: anstatt lokalem Pfad wird das Binary über das remote- ZMSSQLDB-Objekt gezogen (für Multisites bzw. Site-übergreifende DBs) B. Redaktions-GUI... <item type="dictionary"> <dictionary> <item key="auto"></item> <item key="blob" type="dictionary"> <dictionary> <item key="type"><![cdata[image]]></item> <item key="path"><![cdata[/home/var/cdbase]]></item> <!-- <item key="remote">http://www.test.de/e3681/e13473</item> --> </dictionary> </item> <item key="hide" type="int">0</item> <item key="id"><![cdata[cd_cover]]></item> <item key="index" type="int">2</item> <item key="label"><![cdata[cover]]></item> </dictionary> </item>... * WICHTIGER HINWEIS: Das lokale Verzeichnis muss in der ZMS-Config per Option localfsread freigegeben werden
Folie: 16 Besonderes GUI-Element: Multi-Multi-Select ( n:m:o ) ER-Modell Ausschnitt: Person hat diverse Arbeitpakete und dort jeweils eine definierte Rolle Konfigurations-GUI Automatisches Redaktions-GUI
Folie: 17 Besonderes GUI-Element: mysqlset... <item> <dictionary> <item key="id">cdmedium</item> <item key="label">medium</item> <item key="pk" type="int">0</item> <item key="auto" type="int">0</item> <item key="hide" type="int">1</item> <item key="multiselect"> <dictionary> <item key="mysqlset" type="int">1</item> </dictionary> </item> </dictionary> </item>...
Folie: 18 Code-Beispiel GUI-Element: Interface 1. Vorschau-Bild auf Größe begrenzen 2. Vollansicht im neuen Fenster per Mausklick... <item key="interface"><![cdata[ <script type="text/javascript"> $(document).ready(function(){ $("#tr_cd_cover table.neutralcolorblack img").attr({title:"klick zum Vergrößern..."} ); $("#tr_cd_cover table.neutralcolorblack img").click( function() { window.open( $("#tr_cd_cover table.neutralcolorblack img").attr('src') );return false; } ); }) </script> <style type="text/css"> tr#tr_cd_cover table.neutralcolorblack { margin-right:30px; margin-left:25px; } tr#tr_cd_cover table.neutralcolorblack img { width:120px; } </style>]]> </item>...
Folie: 19 Beispiel GUI-Element: Interface (Interaktive Datenvalidierung)
Folie: 20 Beispiel-Code Interaktive Daten-Validierung ------------------------------------------------------ [1] SQLMODEL: jquery Ajax-Script ersetzt DOM-Element ------------------------------------------------------... <item key="interface"><![cdata[<script type="text/javascript"> $(document).ready(function(){ $('#new_email').bind('change', function(){ if ( $('#new_email').attr('value')!= $('input[name=old_email]').attr('value') ) { $('#info_email').load('sql_validate_email_ajax', { 'test': $('#new_email').attr('value') }); } else { $('#info_email').empty(); } }); });... ------------------------------------------------------- [2] DTML: sql_validate_email_ajax liefert feedback -------------------------------------------------------- <dtml-let t="request.get('test','')"> <dtml-if t> <dtml-in "_['objmap']['datasource'].query('select * FROM persons WHERE email=\'%s\' '%(t))['records']" mapping> <span style="color:red;font-weight:bold;textdecoration: blink">error: Email already exists.</span> <dtml-else> <span style="color:green;font-weight:bold;">email check OK.</span> </dtml-in> </dtml-if> </dtml-let>
Folie: 21 Filter-Element: erweitert SQL-Klausel der Übersichtstabelle Typischer Einsatz: Multisites, die auf ein einziges SQL-Repository zugreifen Beispiel: Beispiel: <item <item key="filter"><![cdata[ key="filter"><![cdata[ <dtml-if <dtml-if "getparentnode().getobjproperty('ensemblefilter', "getparentnode().getobjproperty('ensemblefilter', REQUEST)">eventEnsemble REQUEST)">eventEnsemble IN IN ('<dtml-var ('<dtml-var "getparentnode().getobjproperty('ensemblefilter', "getparentnode().getobjproperty('ensemblefilter', REQUEST)">') REQUEST)">') </dtml-if> </dtml-if> ]]></item> ]]></item>
Folie: 22 Web-Maske
Folie: 23 Methoden-Struktur für die Masken-Präsentation Anm: Felder lassen sich ausschließen per REQUEST-Variable ZMS_EXCLUDE_IDS [Liste] <!-- ### FORMVARIABLES --> <!-- ### IDENTIFYING VALUES: Voraussetzung PK = 1. Spalte --> <!-- ### Select User-Datatset by ZSQL-Method --> <dtml-if "_.len(row)>0"> <!-- ### UPDATE: Dataset already exists! ### --> <dtml-call "REQUEST.set('ZMS_ACTION','update')"> <dtml-call "REQUEST.set('rowid',row[0][0])"> <dtml-call "REQUEST.set(primary_key,rowid)"> <!-- ### SQL STATEMENT --> <dtml-call "REQUEST.set('sqlStatement',[])" ><dtml-call "sqlstatement.append('select * FROM %s '%SESSION[sessqentitykey])" ><dtml-call "sqlstatement.append('where ' + primary_key + '=' + sql_quote (SESSION[sessqentitykey],primary_key,rowid))" ><dtml-call "REQUEST.set('resSlctStmnt',''.join(sqlStatement))" ><dtml-call "REQUEST.set('res',query(resSlctStmnt)['records'])"> <dtml-else> <!-- ### INSERT: New Dataset ### --> <dtml-call "REQUEST.set('ZMS_ACTION','insert')"> <dtml-call "REQUEST.set('rowid',-1)"> <dtml-call "recordset_init(request)"> <dtml-call "SESSION.set('ZMS_ROWID',REQUEST.get('new_username',_.None))"> <dtml-var actions> </dtml-if> </dtml-let> </dtml-with> <!-- ### INPUT FORM GENERATION / ACTION --> <dtml-in res ><dtml-call "recordset_init(request)" ><dtml-call "writeblock('[input_profil] %s qentitykey: %s, %s=%s'%(actions,qentitykey,primary_key,rowid))" ><dtml-var actions> </dtml-in> http://eln.zms-hosting.com:8080/sites/uct/register_form/manage_main
Folie: 24 LIVE-Demo Beispiel-Datenbank: CD-Sammlung
Folie: 25 ANHANG: Anwendungsbeispiel Userdatenbank ZMSSQLDB Systemfolder ZSQL DTML