resource registries plone conf 2014
DESCRIPTION
New resource registry in Plone 5! Plone Conference 2014. Using RequireJS/Bower/less integrated with Plone!TRANSCRIPT
Resource RegistriesRamon Navarro Bosch & Rob Gietema
Ramon Navarro Bosch
• Iskra.CAT
• Member Plone Foundation
• FWT member
Rob Gietema
• Four Digits
• Plone Foundation Member
Current State<?xml version="1.0"?> <object name="portal_javascripts" meta_type="JavaScripts Registry"> <javascript compression="safe" authenticated="True" id="hello-world.js" insert-after="*" /> </object>
<?xml version="1.0"?> <object name="portal_css"> <stylesheet title="" id="hello-world.css" media="screen" rel="stylesheet" rendering="link" cacheable="True" compression="safe" cookable="True" enabled="1" expression="" insert-before="print.css" /> </object>
• JS Registry
• CSS Registry
• Tools
Limitations• JS & CSS have evolved
• Dependency management
• Precompiling (LESS / SASS)
• Versioning
• Mockup
RequireJS
// File: app.js // For any third party dependencies, like jQuery, place them in the lib folder.
// Configure loading modules from the lib directory, // except for 'app' ones, which are in a sibling // directory. requirejs.config({ baseUrl: 'lib', paths: { app: '../app' } });
// Start loading the main app file. Put all of // your application logic in there. requirejs(['app/main']);
<!DOCTYPE html> <html> <head> <script data-main="app" src="lib/require.js"></script> </head> <body> <h1>Hello World</h1> </body> </html>• Include require.js
• Add all your dependencies in Javascript
• Async loading of files and modules
GRUNTmodule.exports = function(grunt) {
grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), concat: { options: { separator: ';' }, dist: { src: ['src/**/*.js'], dest: 'dist/<%= pkg.name %>.js' } }, qunit: { files: ['test/**/*.html'] }, watch: { files: ['<%= jshint.files %>'], tasks: ['jshint', 'qunit'] } });
grunt.loadNpmTasks('grunt-contrib-qunit'); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-contrib-concat');
grunt.registerTask('test', ['jshint', 'qunit']);
grunt.registerTask('default', ['qunit', 'concat']);
};
• Task runner
• Run tests, concat, minimize, JSHint etc
NPM
npm install (with no args in a package dir) npm install <tarball file> npm install <tarball url> npm install <folder> npm install [@<scope>/]<name> [--save|--save-dev|--save-optional] [--save-exact] npm install [@<scope>/]<name>@<tag> npm install [@<scope>/]<name>@<version> npm install [@<scope>/]<name>@<version range> npm i (with any of the previous argument usage)
• Node.js package manager
• Dependencies
• Versioning
Bower
{ "name": "my-project", "version": "1.0.0", "main": "path/to/main.js", "ignore": [ ".jshintrc", "**/*.txt" ], "dependencies": { "<name>": "<version>", "<name>": "<folder>", "<name>": "<package>" }, "devDependencies": { "<test-framework-name>": "<version>" } }
• Javascript package manager
• Dependencies
• Versioning
LESS / SASS@base: #f938ab;
.box-shadow(@style, @c) when (iscolor(@c)) { -webkit-box-shadow: @style @c; box-shadow: @style @c; } .box-shadow(@style, @alpha: 50%) when (isnumber(@alpha)) { .box-shadow(@style, rgba(0, 0, 0, @alpha)); } .box { color: saturate(@base, 5%); border-color: lighten(@base, 30%); div { .box-shadow(0 0 5px, 30%) } }
.box { color: #fe33ac; border-color: #fdcdea; } .box div { -webkit-box-shadow: 0 0 5px rgba(0, 0, 0, 0.3); box-shadow: 0 0 5px rgba(0, 0, 0, 0.3); }
• Precompiler for CSS
• Variables
• Nesting
• Macro's
Design of the SolutionBUNDLE
RESOURCE
BOWER COMPONENT
MOCKUP COMPONENT
STANDALONE FILES
DIAZO
STATIC HTML REGISTRY
Bundles
• Dependency
• Compilation
• Enabled <records prefix="plone.bundles/plone" interface='Products.CMFPlone.interfaces.IBundleRegistry'> <value key="resources"> <element>plone</element> </value> <value key="enabled">True</value> <value key="jscompilation">++plone++static/plone-compiled.js</value> <value key="csscompilation">++plone++static/plone-compiled.css</value> <value key="last_compilation">2014-08-14 00:00:00</value> </records>
<records prefix="plone.bundles/plone-legacy" interface='Products.CMFPlone.interfaces.IBundleRegistry'> <value key="resources" purge="false"> <element>plone_javascript_variables</element> <element>unlockOnFormUnload</element> <element>table_sorter</element> <element>inline-validation</element> <element>jquery-highlightsearchterms</element> <element>cookie_functions</element> </value> <value key="jscompilation">++plone++static/plone-legacy-compiled.js</value> <value key="csscompilation">++plone++static/plone-legacy-compiled.css</value> <value key="last_compilation">2014-08-14 00:00:00</value> <value key="compile">False</value> <value key="enabled">True</value> </records>
Resources
• js
• export/init
• css
• url <records prefix="plone.resources/mockup-patterns-structure" interface='Products.CMFPlone.interfaces.IResourceRegistry'> <value key="js">++resource++mockup/structure/pattern.js</value> <value key="url">++resource++mockup/structure</value> <value key="css"> <element>++resource++mockup/structure/less/pattern.structure.less</element> </value> </records>
<records prefix="plone.resources/tinymce" interface='Products.CMFPlone.interfaces.IResourceRegistry'> <value key="js">++plone++static/components/tinymce/tinymce.js</value> <value key="export">window.tinyMCE</value> <value key="init">function () { this.tinyMCE.DOM.events.domLoaded = true; return this.tinyMCE; }</value> <value key="css"> <element>++plone++static/components/tinymce/skins/lightgray/skin.min.css</element> <element>++plone++static/components/tinymce/skins/lightgray/content.inline.min.css</element> </value> </records>
<records prefix="plone.resources/mockup-patterns-select2" interface='Products.CMFPlone.interfaces.IResourceRegistry'> <value key="js">++resource++mockup/select2/patterns.js</value> <value key="css"> <element>++resource++mockup/select2/pattern.select2.less</element> </value> </records>
LESS Vars <!-- Mixins vars for less with the paths --> <record name="plone.lessvariables"> <field type="plone.registry.field.Dict"> <title>Less variables</title> <description>Variables that are going to be compiled on less</description> <key_type type="plone.registry.field.ASCIILine" /> <value_type type="plone.registry.field.TextLine" /> </field> <value> <element key="bowerPath">\"{site_url}/++plone++static/components/\"</element> <element key="mockupPath">\"{site_url}/++resource++mockup/\"</element> <element key="mockuplessPath">\"{site_url}/++resource++mockupless/\"</element>
<element key="plone-link-color">rgba(0,123,179,1)</element> <element key="plone-gray-lighter">lighten(#000, 80%)</element> <element key="plone-gray-light">lighten(#000, 46.5%)</element>
<element key="plone-toolbar-bg">rgba(0,0,0,.9)</element> <element key="plone-toolbar-submenu-bg">rgba(20,20,20,.9)</element> <element key="plone-toolbar-font-primary">'Roboto Condensed', sans-serif</element>
Pattern Options <record name="plone.patternoptions"> <field type="plone.registry.field.Dict"> <title>Patterns configuration</title> <description>Base pattern configuration options</description> <key_type type="plone.registry.field.ASCIILine" /> <value_type type="plone.registry.field.Text" /> </field> <value> <element key="pickadate">{"selectYears": 200}</element>
Development & Production
• Compile on browser on each reload using RequireJS / LESS
• Develop static HTML with compiled bundles
• Grunt using browser config.js/mixins.less
• Get the compiled css/js bundles
• Deployment with compiled css/js
<record name="plone.resources.development"> <field type="plone.registry.field.Bool"> <title>Frontend development mode</title> </field> <value>true</value> </record>
Diazo[theme] title = Barceloneta Theme description = Plone 5 theme rules = /++theme++barceloneta/rules.xml prefix = /++theme++barceloneta doctype = <!DOCTYPE html> enabled-bundles = barceloneta disabled-bundles =
<!-- CSS --> <after theme-children="/html/head" content="/html/head/link" /> <!-- Script --> <after theme-children="/html/head" content="/html/head/script" />
Views: config.jsrequirejs.config({ baseUrl: 'http://localhost:8080/Plone', paths: {
'tinymce-save': ‘++plone++static/components/tinymce/plugins/save/plugin', 'mockup-patterns-accessibility': ‘++resource++mockup/accessibility/pattern’, 'mockup-patterns-tinymce-url': '++resource++mockup/tinymce' … 'mockup-patterns-formautofocus': '++resource++mockup/formautofocus/pattern', 'bootstrap-tooltip': '++plone++static/components/bootstrap/js/tooltip', 'resource-plone-app-event-portlet_calendar-js': '++resource++plone.app.event.portlet_calendar', 'dropzone': '++plone++static/components/dropzone/downloads/dropzone-amd-module'},
shim: { 'tinymce-save': {deps: ['tinymce']}, 'mockup-router': {}, 'tinymce-spellchecker': {deps: ['tinymce']}, 'jquery.event.drop': {exports: '$.drop', deps: ['jquery']}, 'tinymce-autosave': {deps: ['tinymce']}, … 'bootstrap-tooltip': {deps: ['jquery']}},
optimize: 'uglify', wrapShim: true });
Views: less-variables.js window.less = { env: "development", logLevel: 2, async: false, fileAsync: false, errorReporting: window.lessErrorReporting || 'console', poll: 1000, functions: {}, relativeUrls: true, dumpLineNumbers: "comments", globalVars: {
sitePath: '"http://localhost:8080/Plone"', isPlone: 'true', isMockup: 'false', 'plone-toolbar-font-primary': "'Roboto Condensed', sans-serif", 'plone-gray-lighter': "lighten(#000, 80%)", 'mockuplessPath': "\"http://localhost:8080/Plone/++resource++mockupless/\"", 'bowerPath': "\"http://localhost:8080/Plone/++plone++static/components/\"", 'plone-toolbar-internally-published-color': "rgb(136,61,250)", 'mockupPath': "\"http://localhost:8080/Plone/++resource++mockup/\"", … 'plone-toolbar-private-color': "rgb(196,24,60)", 'plone-screen-xs-max': "(@plone-screen-sm-min + 1)", 'plone-toolbar-bg': "rgba(255,0,0,.9)", 'plone-gray-light': "lighten(#000, 46.5%)", 'barceloneta': ‘"http://localhost:8080/Plone/++plone++barceloneta/less/barceloneta.plone.less"', … 'picker_time': '"http://localhost:8080/Plone/++plone++static/components/pickadate/lib/themes/classic.time.css"', 'picker': '"http://localhost:8080/Plone/++plone++static/components/pickadate/lib/themes/classic.css"', 'plone-patterns-toolbar': '"http://localhost:8080/Plone/++plone++static/patterns/toolbar/src/css/toolbar.plone.less"', 'plone': '"http://localhost:8080/Plone/++plone++static/plone.less"', …}};
plone.less… @import url("@{mockup-patterns-upload}"); @import url("@{plone-patterns-toolbar}"); @import url("@{mockup-patterns-tinymce}");
@import "@{bowerPath}bootstrap/less/dropdowns.less"; …
plone.jsrequire([ 'jquery', 'mockup-registry', 'mockup-patterns-base', 'mockup-patterns-select2', 'mockup-patterns-pickadate', 'mockup-patterns-relateditems', 'mockup-patterns-querystring', 'mockup-patterns-tinymce', 'plone-patterns-toolbar', 'mockup-patterns-accessibility', 'mockup-patterns-autotoc', 'mockup-patterns-formunloadalert', 'mockup-patterns-preventdoublesubmit', 'mockup-patterns-formautofocus', 'mockup-patterns-modal', 'mockup-patterns-structure', 'bootstrap-dropdown', 'bootstrap-collapse', ‘bootstrap-tooltip' …
TTW customization
• Fully customizable trough the web
• Controlpanel in Plone
Legacy Plone
• cssregistry.xml and jsregistry.xml still work
• Legacy bundle
• Not all attributes are supported
<?xml version="1.0"?> <object name="portal_javascripts" meta_type="JavaScripts Registry"> <javascript compression="safe" authenticated="True" id="hello-world.js" insert-after="*" /> </object>
<?xml version="1.0"?> <object name="portal_css"> <stylesheet title="" id="hello-world.css" media="screen" rel="stylesheet" rendering="link" cacheable="True" compression="safe" cookable="True" enabled="1" expression="" insert-before="print.css" /> </object>
Standard resources
• Mockup
• Bower components
accessibility autotoc backdrop eventedit filemanager formautofocus formunloadalert modal moment pickadate preventdoublesubmit querystring relateditems resourceregistry select2 sortable structure tablesorter texteditor thememapper tinymce toggle tooltip tree upload
Are you able to do"console dev" ?
• generate_gruntfile.py -> creates Gruntconfig.js
Demo
Migration• Resources defined as pattern and less file
• Always generate production bundle
• Move jsregistry and cssregistry to registry.xml
• On/live jquery 1.11 modifications
• Never loose hope
Questions ?
• Additional info at:
• https://www.nathanvangheem.com/news/plone-5-resource-registries
SPRINT• Static CSS/JS for mockups / CDN
• Cooking on production
• Mockup issues
• Write documentation!!
• Write tests!!