-
Notifications
You must be signed in to change notification settings - Fork 2
Website Entwicklung mit Scrivito, JavaScript und ReactJS
- Dienst der in der Could (Amazon, Google, Microsoft) läuft
- Scrivito wird von uns bei AWS betrieben (eu-west-1, Irland)
- ausschließlich Daten verwalten
- macht keine Annahme oder Hilfestellung über Ihre Darstellung
- Scrivito selbst verwaltet nur Daten, jedoch keine Templates
- Inhalte werden in der Cloud gespeichert
- Zugriff nur über API
- keinen eigenen(!) Server als Infrastruktur betreiben
- keinen dedizierten Server - nur API Backend
- Läuft in allen Browser
- Erlaubt dynamisches Verhalten
- Erlaubt Verhalten fast wie eine Desktop Applikation
- node.js
- Server Applikation die JavaScript laufen lässt
- Kann als Webserver fungieren ( Express )
- Erlaubt hohen Grad an “Parallelisierung” aufgrund nicht blockierender Architektur
- Dennoch nur 1 echter Prozess
- Kann zum laufen von serverseitigen Tasks genutzt werden
- Image Processing, css Processing
- Kann für lokale Applikationen genutzt werden ( Electron )
- Zb Atom Code-Editor
- Browser Code kann lokal getestet werden ( Mocha Jasmine )
- Google berücksichtigt JavaScript Seiten auch bei der Indizierung
- JavaScript Framework
- Fokus auf Interface Entwicklung
- Deklarative Programmierung
- Gegeben einen Zustand beschreibe was angezeigt wird
- Zustandsübergänge werden meist von React automatisch gehandhabt
- Komponenten basiert
- Jeder HTML-Tag kann eine Komponente sein
- Komponenten sind hierarchisch angeordnet
- Komponenten aktualisieren sich unabhängig von einander
- Webseite (Live)
- API erlaubt Zugriff auf anzuzeigende Daten
- Browser rendert für jeden Besucher seine(!) Version der Seite
- Wir liefern nur “Verhalten” in Form von JavaScript-Code
- Bearbeitung
- Inhalte direkt in der Seite bearbeiten (WYSIWYG)
- Die Webseite ist das CMS-Interface
- Working Copies
- Ähnlich zu Git Branches
- Konzeptionell eine Kopie der aktuell publizierten Webseite
- Erlaubt paralleles Arbeiten
Basic Scrivito JS Applikation for the Infopark & Friends Workshop: Website-Entwicklung mit Scrivito, JavaScript und ReactJS
- Anmelden bei [scrivito.com]
- neues Trial CMS Anlegen
- App auschecken
git clone git@github.com:infopark/ip-friends.git
- API-Key holen / .env gemäß Hinweisen erstellen
cd ip-friends
npm install
npm start
- Im Borwser zu to
/scrivito
gehen - neue Working Copy anlegen
- In edit Modus wechseln
- Browser Console öffnen
- Frage auf "scrivito-application" wechseln
- In der Konsole folgende Kommandos ausführen
Scrivito.load(() => [...Scrivito.Obj.all()])
.then((objs) => {
objs.forEach(obj => {
console.log("Obj: " + obj.id());
obj.destroy();
});
Scrivito.getClass('Homepage').create({
_path: "/"
});
console.log("Done");
});
- Wir gehen nicht auf alle Details ein
- Ziel ist es ein grobes Verständnis zu geben
- Beispiele zeigen Optionen nicht optimale Lösungen
- Einzelne Blog-Seiten mit einem dedizierten Artikel
- Seiten als auch Widgets bestehen aus 3 Komponenten
- Model
- Enthält welche Daten gespeichert werden sollen
- eventuell Verhalten
- Ansichten/Komponenten
- Eigentliche Darstellung der Seite
- Bearbeitung konfigurieren
- Detailsansicht konfigurieren
- Model
Objs/BlogPost/BlogPostObjClass.js
Scrivito.provideObjClass('BlogPost', {
attributes: {
author: 'string',
body: 'widgetlist',
pageTitle: 'string',
publishedAt: 'date',
title: 'string',
relatedLink: 'linklist',
},
});
- Das wäre ein Obj-Klasse nur mit Attributen
- Attribut typen
- binary
- date
- enum
- multienum
- html
- link / linklist
- reference / referencelist
- string
- stringlist - ergo tags
- integer
- float
- widgetlist
- Modelle können
- tatsächliche Seiten sein
- abstrakte Container für wiederverwendbare Daten / Ressourcen Objekte
- Jede Objekt klasse hat eigene Komponenten
src/Objs/BlogPost/BlogPostComponent.js
Scrivito.provideComponent('BlogPost', ({ page }) => {
const relatedLinks = page.get('relatedLink').map(link => {
return (<li key={ link.hash() }>
<Scrivito.LinkTag to={ link }>{ link.title() }</Scrivito.LinkTag>
</li>);
});
return (
<div className="content blog_post">
<Scrivito.ContentTag tag="h2" content={ page } attribute="title" />
<div>
By <Scrivito.ContentTag tag="span" content={ page } attribute="author" />
</div>
<div>
<Scrivito.ContentTag tag="span" content={ page } attribute="publishedAt" />
</div>
<hr/>
<Scrivito.ContentTag tag="div" content={ page } attribute="body" className="panel-body" />
<ul>
{ relatedLinks }
</ul>
</div>
);
});
src/Objs/BlogPost/BlogPostEditingConfig.js
import HeadlineWidget from '../../Widgets/HeadlineWidget/HeadlineWidgetClass';
import TextWidget from '../../Widgets/TextWidget/TextWidgetClass';
Scrivito.provideEditingConfig('BlogPost', {
title: 'BlogPost',
description: 'Ein Blog Artikel',
attributes: {
title: {
title: 'Titel des Artikels',
},
pageTitle: {
title: 'Seitentitel',
},
author: {
title: 'Author des Artikels',
},
publishedAt: {
title: 'Datum der Veröffentlichung',
},
relatedLink: {
title: 'Verwandte Links',
},
},
properties: [
'title',
'pageTitle',
'author',
'publishedAt',
'relatedLink',
],
initialContent: {
pageTitle: '[Neuer Blog Post]',
body: [
new HeadlineWidget({ headline: 'Ein neuer Blog' }),
new TextWidget({ text: 'Hier Artikel einfügen' }),
],
publishedAt: new Date(Date.now()),
},
titleForContent: obj => obj.get('title'),
});
- Teaser Widget das Bild + Text kombiniert
- ähnlich zu Seiten
src/Widgets/TeaserWidget/TeaserWidgetClass.js
Scrivito.provideWidgetClass('TeaserWidget', {
attributes: {
title: 'string',
summery: 'html',
headerImage: 'reference',
},
});
src/Widgets/TeaserWidget/TeaserWidgetComponent.js
Scrivito.provideComponent('TeaserWidget', ({ widget }) => {
return (
<div>
<Scrivito.ImageTag content={ widget } attribute="headerImage" />
<Scrivito.ContentTag tag="h2" content={ widget } attribute="title" />
<Scrivito.ContentTag tag="div" content={ widget } attribute="summery" />
</div>
);
});
-
Scrivoto.ImageTag
skaliert Bilder automatisch- Verhindert das diese nicht zu groß werde.
- Bilder können auch manuell in einer bestimmten Größe angefragt werden
image.get('blob').optimizeFor({ width: 50, height: 50, fit: 'crop' }).url()
src/Widgets/TeaserWidget/TeaserWidgetEditingConfig.js
Scrivito.provideEditingConfig('TeaserWidget', {
title: 'Teaser Widget',
attributes: {
headerImage: {
title: 'Titelbild',
},
},
properties: [
'headerImage',
],
titleForContent: widget => widget.get('title'),
});
Objs/BlogPost/BlogPostObjClass.js
Scrivito.provideObjClass('BlogPost', {
attributes: {
author: 'string',
body: ['widgetlist', { only: ['HeadlineWidget', 'TextWidget', 'TeaserWidget'] }],
pageTitle: 'string',
publishedAt: 'date',
title: 'string',
relatedLink: 'linklist',
},
});
- Blog Seite mit Artikelübersicht ( zb wie eine Newsseite )
- Attribute alleine reichen nicht, wir brauchen noch Verhalten
- Wir brauchen aber noch Verhalten
- Liste letzten BlogPosts
Objs/Blog/BlogObjClass.js
const BlogBaseClass = Scrivito.createObjClass({
attributes: {
pageTitle: 'string',
},
});
class Blog extends BlogBaseClass {
static latestPosts(page = 0, count = 10) {
return Scrivito.Obj
.where('_objClass', 'equals', 'BlogPost')
.offset(page * count)
.order('publishedAt', 'desc')
.take(count);
}
}
export default Scrivito.provideObjClass('Blog', Blog);
- Objekte können bequem gesucht werden
Scrivito.Obj.where('_objClass', 'equals', ['Image', 'Download'])
Scrivito.Obj.where('_objClass', 'equals', 'BlogPost').and('_permalink', 'equals', null)
Scrivito.Obj.where('_objClass', 'equals', 'BlogPost').and('title', 'contains', "brand")
Scrivito.Obj.where('_objClass', 'equals', 'BlogPost').order('_last_changed','desc').take(1)[0]
Scrivito.Obj.where('*', 'links_to', i)
-
Scrivito.Obj.where('_objClass', 'equals', 'BlogPost').and('_obj_class', 'equals', 'GoogleMapsWidget')
- beschränke Suche auf Objekte der Classe
BlogPost
(und alle enthaltenten Widgets) - Suche Widgets einer bestimmten Klasse
- Gibt aber dennoch
BlogPost
Objekte aus
- beschränke Suche auf Objekte der Classe
src/Objs/Blog/BlogComponent.js
import Blog from './BlogObjClass';
Scrivito.provideComponent('Blog', ({ page }) => {
const blogPosts = () => {
const content = [];
const posts = Blog.latestPosts();
if (!posts) { return; }
for (let i = 0, l = posts.length; i < l; i += 1) {
const post = posts[i];
content.push(
<div key={ post.id() }>
<h3>
<Scrivito.LinkTag to={ post }>{ post.get('title') }</Scrivito.LinkTag> <small>
{ post.get('publishedAt').toDateString() }
<br/>
by { post.get('author')}
</small>
</h3>
</div>
);
}
return content;
};
return (
<div className='content blog'>
<Scrivito.ContentTag tag='h1' content={ page } attribute='pageTitle' />
<div className='blog_posts'>
{ blogPosts() }
</div>
</div>
);
});
src/Objs/Blog/BlogEditingConfig.js
Scrivito.provideEditingConfig('Blog', {
title: 'Blog',
description: 'Ein Blog Artikel',
attributes: {
pageTitle: {
title: 'Seitentitel',
},
},
properties: [
'pageTitle',
],
titleForContent: obj => obj.get('title'),
});
- Bilder in lizenziert und frei unterscheiden
src/Objs/Image/ImageObjClass.js
export default Scrivito.provideObjClass('Image', {
attributes: {
blob: 'binary',
tags: 'stringlist',
alternativeText: 'string',
licence: ['enum', { values: ['free', 'cc', 'payed'] }],
},
});
src/Objs/TimelineEntry/ImageEditingConfig.js
Scrivito.provideEditingConfig('Image', {
attributes: {
blob: {
title: 'Image',
},
tags: {
title: 'Tags',
description: 'Make it easier to find this Image by adding some tags.',
},
licence: {
title: 'Bildlizenz',
values: [
{ value: 'free', title: 'Frei' },
{ value: 'cc', title: 'Creative Commons' },
{ value: 'payed', title: 'Bezahlt' },
],
},
alternativeText: {
title: 'Alternative text',
description: 'Brief description of what the image is about.',
},
},
properties: [
'blob',
'alternativeText',
'tags',
'licence',
],
});
- Fenster zum gesamten Content im CMS für den Redakteur
- Konfiguration definiert hierarchische Filter
src/config/scrivitoContentBrowser.js
Scrivito.configureContentBrowser({
filters: {
_objClass: {
field: '_objClass',
operator: 'equals',
options: {
...
Images: {
title: 'Images',
icon: 'image',
value: 'Image',
options: {
free: {
field: 'licence',
operator: 'equals',
value: 'free',
title: 'Frei',
},
cc: {
field: 'licence',
operator: 'equals',
value: 'cc',
title: 'Creative Commons',
},
payed: {
field: 'licence',
operator: 'equals',
value: 'payed',
title: 'Gekauft',
},
},
}
}
}
}
});
- Ist abstrakte Ressource die keine Seite im Auftritt ist.
- Details Ansicht für Content Browser zum Bearbeiten:
- Content Browser Filter definieren
- Blog Post umstellen von string Autor auf Ressourcen Objekt
Objs/Author/AuthorObjClass.js
export default Scrivito.provideObjClass('Author', {
attributes: {
firstName: 'string',
lastName: 'string',
image: 'reference',
},
});
- BlogPost abändern, um Autoren Referenz zu nutzen
Objs/BlogPost/BlogPostObjClass.js
Scrivito.provideObjClass('BlogPost', {
attributes: {
author: 'reference',
body: ['widgetlist', { only: ['HeadlineWidget', 'TextWidget', 'TeaserWidget'] }],
pageTitle: 'string',
publishedAt: 'date',
title: 'string',
relatedLink: 'linklist',
},
});
src/Objs/Author/AuthorComponent.js
class AuthorComponent extends React.Component {
render() {
let fullName = '';
if (this.props.page) {
fullName = `${this.props.page.get('firstName')} ${this.props.page.get('lastName')}`;
}
return <span>
{ fullName }
</span>;
}
}
export default Scrivito.connect(AuthorComponent);
Scrivito.provideComponent('Author', AuthorComponent);
- BlogPost abändern, um den referenzierten Autor anzuzeigen
src/Objs/BlogPost/BlogPostComponent.js
import Author from '../Author/AuthorComponent';
Scrivito.provideComponent('BlogPost', ({ page }) => {
const relatedLinks = page.get('relatedLink').map(link => {
return (<li key={ link.hash() }>
<Scrivito.LinkTag to={ link }>{ link.title() }</Scrivito.LinkTag>
</li>);
});
return (
<div className="content blog_post">
<Scrivito.ContentTag tag="h2" content={ page } attribute="title" />
<div>
By <Author page={ page.get('author') }/>
</div>
<div>
<Scrivito.ContentTag tag="span" content={ page } attribute="publishedAt" />
</div>
<hr/>
<Scrivito.ContentTag tag="div" content={ page } attribute="body" className="panel-body" />
<ul>
{ relatedLinks }
</ul>
</div>
);
});
src/Objs/Blog/BlogComponent.js
import Blog from './BlogObjClass';
import Author from '../Author/AuthorComponent';
Scrivito.provideComponent('Blog', ({ page }) => {
const blogPosts = () => {
const content = [];
const posts = Blog.latestPosts();
if (!posts) { return; }
for (let i = 0, l = posts.length; i < l; i += 1) {
const post = posts[i];
content.push(
<div key={ post.id() }>
<h3>
<Scrivito.LinkTag to={ post }>{ post.get('title') }</Scrivito.LinkTag> <small>
{ post.get('publishedAt').toDateString() }
<br/>
by <Author page={ post.get('author') }/>
</small>
</h3>
</div>
);
}
return content;
};
return (
<div className='content blog'>
<Scrivito.ContentTag tag='h1' content={ page } attribute='pageTitle' />
<div className='blog_posts'>
{ blogPosts() }
</div>
</div>
);
});
src/Objs/Author/AuthorEditingConfig.js
Scrivito.provideEditingConfig('Author', {
title: 'Author',
description: 'Der Author eines Artikels',
attributes: {
firstName: {
title: 'Vorname des Autors',
},
lastName: {
title: 'Nachname des Autors',
},
image: {
title: 'Avatar Bild',
},
},
properties: [
'firstName',
'lastName',
'image',
],
titleForContent: obj => obj.get('title'),
});
src/config/scrivitoContentBrowser.js
Scrivito.configureContentBrowser({
filters: {
_objClass: {
field: '_objClass',
operator: 'equals',
options: {
...
Authors: {
title: 'Autoren',
icon: 'person',
value: 'Author',
},
}
}
}
});
Scrivito.configure({
homepage: () => {
return Scrivito.Obj.where('_objClass', 'equals', 'Homepage').take(1)[0];
},
...
});
- slug kann individuell festgelegt werden
Objs/Blog/BlogObjClass.js
...
class Blog extends BlogBaseClass {
...
slug() {
return this.get('pageTitle').replace(/ /g, '-').replace(/[^\w-]+/g, '');
}
...
}
...
- Werden am Obj festgelegt
- Erlauben Adressierung über eindeutigen String
#Q&A
- Wie wird die Verbindung vom Browser zur Cloud gesichert
- API-Key + Domain whitelist
- identisch wie GoogleMaps seine Zugriffe Schützt
- Wie erfolgt Authentifizierung/Editor Sicherung?
- Single sign on mit scrivito.com (cookie)
- Wie binde ich alternative Datenbanken an?
- Eigenes Backend
- Erfordert einen API-Key (Credentials)
- Erlaubt nur Anfragen von einer bestimmten Domain
- Eigenes Backend