making sling grunt · apache sling & friends tech meetup berlin, 28-30 september 2015 making...
TRANSCRIPT
![Page 1: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/1.jpg)
APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015
Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling
Philip Hornig (Publicis Pixelpark), Michael Sunaric (Netcentric)
![Page 2: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/2.jpg)
Topics
adaptTo() 2015 2
Modern front-end development and why we need it. How to integrate modern front-end development
with the Sling development stack. The tools necessary to achieve the integration.
![Page 3: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/3.jpg)
adaptTo() 2015 3
Modern Front-End Development
![Page 4: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/4.jpg)
Complexity is increasing
adaptTo() 2015 4
Complex layouts and responsive design Client side apps Modular and object oriented design Automated testing Documentation
![Page 5: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/5.jpg)
Agile development
adaptTo() 2015 5
Rapid prototyping Design in HTML
Prototyping engine with node.js, express.js
![Page 6: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/6.jpg)
Setup
adaptTo() 2015 6
Scaffolding Yeoman, Middleman, …
Libraries jQuery, Bootstrap, Modernizr, Bourbon
Frameworks Ember.js, AngularJS, Backbone.js,
ExtJS, Dojo, ...
![Page 7: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/7.jpg)
Develop
adaptTo() 2015 7
Prototype Node.js, Express
Watch CSS (Sass, Less, Stylus) Javascript (CoffeeScript, TypeScript, ECMAScript 6) HTML (Jade, Haml, Handlebars) Lint
CSS (csslint, sasslint, styluslint) Javascript (jshint, jscs)
Refresh LiveReload
![Page 8: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/8.jpg)
Test
adaptTo() 2015 8
Function PhantomJS, CasperJS,
Selenium
Form Selenium, BackstopJS
![Page 9: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/9.jpg)
Build
adaptTo() 2015 9
1. Code linting 2. Compile 3. Unit tests 4. Concatenate 5. Minify 6. Generate icons/iconfonts
7. Optimize images 8. Measure performance 9. Prototype 10. Integration tests 11. Deploy
![Page 10: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/10.jpg)
Build Tools and Dependency Mgmt.
adaptTo() 2015 10
NPM, Bower, Gulp, Grunt, Broccoli
![Page 11: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/11.jpg)
Gruntfile.js
adaptTo() 2015 11
module.exports = function (grunt) {
"use strict";
require("load-grunt-config")(grunt, {
init: true,
data: {
assetsSrc: "assets",
assetsDist: "src/main/resources/apps/demo/assets"
}
});
grunt.registerTask("default", ["clean", "bowercopy", "scss", "js", "jsonlint:models"]);
grunt.registerTask("dev", ["default", "express:dev", "watch"]);
grunt.registerTask("scss", ["scsslint", "sass"]);
grunt.registerTask("js", ["jscs", "jshint", "concat", "uglify"]);
};
![Page 12: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/12.jpg)
Sass Task
adaptTo() 2015 12
module.exports = function (grunt) {
"use strict";
return {
options: {
quiet: true,
sourcemap: "inline",
unixNewlines: true,
trace: true
},
all: {
options: {
style: "expanded"
},
src: "<%= assetsSrc %>/scss/style.scss",
dest: "<%= assetsDist %>/css/style.css"
}
};
};
![Page 13: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/13.jpg)
Working with Grunt
adaptTo() 2015 13
![Page 14: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/14.jpg)
adaptTo() 2015 14
How to integrate with backend development
![Page 15: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/15.jpg)
How integration used to be
adaptTo() 2015 15
A) Code duplication Front-end delivers HTML prototype, back-end developers recreate templates in back-end (e.g. jsp)
B) Back-end tool chain only Front-end developer works with back-end tools/development environment to create front-end code.
![Page 16: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/16.jpg)
Pitfalls of “code duplication”
adaptTo() 2015 16
Extra work, more code to maintain, error prone Eventually front-end code and back-end code
diverge.
![Page 17: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/17.jpg)
Pitfalls of “back-end tool chain only”
adaptTo() 2015 17
Not compatible with modern front-end stack Each discipline requires more specialized
knowledge. Usage of full back-end stack for front-end
development is time consuming.
![Page 18: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/18.jpg)
How we would like the process to be like
adaptTo() 2015 18
Planning
Model
Development
Java Code
HTML, CSS, JS
Sling
Integration
Back-End
Front-End
Test-Driven
Prototyping
![Page 19: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/19.jpg)
How we would like the process to be like
adaptTo() 2015 19
Planning
Model
Development
Java Code
HTML, CSS, JS
Sling
Integration
Back-End
Front-End
Test-Driven
Prototyping
![Page 20: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/20.jpg)
Planning Phase
adaptTo() 2015 20
The interface between front-end and back-end is the data model
The data model is defined in a JSON file The JSON file serves as documentation of the
interface
![Page 21: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/21.jpg)
How we would like the process to be like
adaptTo() 2015 21
Planning
Model
Development
Java Code
HTML, CSS, JS
Sling
Integration
Back-End
Front-End
Test-Driven
Prototyping
![Page 22: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/22.jpg)
Development Phase
adaptTo() 2015 22
Now front-end and back-end development can start in parallel
The front-end uses the JSON file in development for the HTML prototype
The back-end develops test-driven against the data model
![Page 23: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/23.jpg)
How we would like the process to be like
adaptTo() 2015 23
Planning
Model
Development
Java Code
HTML, CSS, JS
Sling
Integration
Back-End
Front-End
Test-Driven
Prototyping
![Page 24: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/24.jpg)
Integration Phase
adaptTo() 2015 24
In Sling front-end and back-end code is deployed and works together seamlessly.
![Page 25: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/25.jpg)
The Crux
adaptTo() 2015 25
Touchpoint between front-end and back-end is the template engine …which merges the template delivered by the front-end and the data model delivered by the back-end
We need a common template engine!
![Page 26: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/26.jpg)
Unified Template Source
adaptTo() 2015 26
Template development is front-end driven. Templates must be part of the front-end stack. Templates must contain only presentation logic. Templates must be renderable in Sling.
![Page 27: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/27.jpg)
Our solution
adaptTo() 2015 27
Front-end stack with Handlebars as template language
Sling uses a Handlebars Sling Script Engine
![Page 28: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/28.jpg)
Evaluated Handlebars Sling Script Engines
adaptTo() 2015 28
AEM Social Component Framework (https://docs.adobe.com/docs/en/aem/6-1/develop/communities/scf.html)
Handlebars Sling Script Engine by Ian Boston (http://svn.apache.org/repos/asf/sling/whiteboard/ieb/handlebars, see https://issues.apache.org/jira/browse/SLING-2919).
Both use the Handlebars Java port Handlebars.java (https://github.com/jknack/handlebars.java).
![Page 29: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/29.jpg)
AEM Social Component Framework
adaptTo() 2015 29
Closed source, support for our purpose unclear. API and implementation is specific to Social
Communities.
![Page 30: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/30.jpg)
Handlebars Script Engine by Ian Boston
adaptTo() 2015 30
Uses JCR structure as data model. For complex applications, the JCR structure will not
match the presentation model.
![Page 31: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/31.jpg)
adaptTo() 2015 31
Handlebars Sling Script Engine with Context Generators
![Page 32: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/32.jpg)
Script Engine with Context Generators
adaptTo() 2015 32
Add “context generators”. Context generators create a specific presentation
model for each template.
![Page 33: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/33.jpg)
Script Engine with Context Generators - 2
adaptTo() 2015 33
We implemented two context generators: SimpleContextGenerator
Exposes JCR structure as model
PresenterContextGenerator
![Page 34: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/34.jpg)
PresenterContextGenerator
adaptTo() 2015 34
Delegates model generation to a Sling Model bean. Bean is assigned by Presenter annotation to a
sling:resourceType.
![Page 35: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/35.jpg)
Template
adaptTo() 2015 35
Handlebars template
<!doctype html>
<html lang="en">
<head>
<title>{{title}}</title>
<link rel="stylesheet" href="/demo/assets/css/style.css">
</head>
<body>
{{include this template="body"}}
{{include this path="footer" resourceType="demo/components/modules/footer"}}
</body>
</html>
![Page 36: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/36.jpg)
Model
adaptTo() 2015 36
Provide presentation model using Sling Models
@Model(adaptables = Resource.class)
@Presenter(resourceTypes = {"demo/components/page"})
public class PagePresenter {
@ValueMapValue
@XSSProtection(strategy = XSSProtection.Strategy.HTML_FILTER)
private String title;
public String getTitle() {
return title + " presented by PagePresenter";
}
}
![Page 37: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/37.jpg)
Model generation
adaptTo() 2015 37
Find the right Presenter and convert it to a Map
public Map<String, Object> createModel(ScriptContext scriptContext) {
Resource resource = new
ScriptContextAdapter(scriptContext).getResource();
String resourceType = resource.getResourceType();
Class<?> presenterType =
presenterBundleListener.getPresenters().get(resourceType);
Object presenter = null;
if (presenterType != null) {
presenter = resource.adaptTo(presenterType);
}
return beanToMapSerializer.convertToMap(presenter);
}
![Page 38: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/38.jpg)
Server-Side Rendering
adaptTo() 2015 38
Render the template with Handlebars.java
public Object eval(Reader templateReader, ScriptContext scriptContext)…{
Handlebars handlebars = new Handlebars();
…
Template template = getTemplate(templateReader, scriptContext,
handlebars);
Context context = createContext(scriptContext);
if (context != null) {
template.apply(context, scriptContext.getWriter());
}
…
![Page 39: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/39.jpg)
Features
adaptTo() 2015 39
Handlebars.java HTML-escapes by default. Additional, customizable XSS protection by
XSSProtection annotation. Template languages can be mixed (e.g. body.hbs
may include parsys.jsp).
![Page 40: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/40.jpg)
How to use it
adaptTo() 2015 40
Download https://github.com/phornig/sling-handlebars
Demo
https://github.com/phornig/sling-handlebars-demo
![Page 41: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/41.jpg)
Outlook
adaptTo() 2015 41
Adding additional Handlebars helper via a Service Factory
AEM helper plugin (i18N, WCMMode) Presenter lookup by Sling selectors
![Page 42: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/42.jpg)
adaptTo() 2015 42
Thank you
![Page 43: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/43.jpg)
adaptTo() 2015 43
Questions?
Download https://github.com/phornig/sling-handlebars Demo https://github.com/phornig/sling-handlebars-demo
![Page 44: Making Sling Grunt · APACHE SLING & FRIENDS TECH MEETUP BERLIN, 28-30 SEPTEMBER 2015 Making Sling Grunt Or How to Integrate Modern Front-End Development with Sling](https://reader035.vdocuments.mx/reader035/viewer/2022063015/5fd23c051a885242d8266f03/html5/thumbnails/44.jpg)
Contact
adaptTo() 2015 44
Michael Sunaric, Netcentric [email protected] Philip Hornig, Publicis Pixelpark [email protected]