├── tutorial-common ├── header.html ├── gen.sh ├── footer.html ├── template.html └── style.css ├── aikau ├── aikau-tutorial-share │ ├── .gitignore │ ├── src │ │ ├── main │ │ │ ├── amp │ │ │ │ ├── web │ │ │ │ │ └── js │ │ │ │ │ │ └── example │ │ │ │ │ │ └── widgets │ │ │ │ │ │ ├── i18n │ │ │ │ │ │ ├── TemplateWidget.properties │ │ │ │ │ │ ├── PubSub.properties │ │ │ │ │ │ └── AjaxWidget.properties │ │ │ │ │ │ ├── templates │ │ │ │ │ │ ├── TemplateWidget.html │ │ │ │ │ │ ├── RenderWidget.html │ │ │ │ │ │ ├── InputWidget.html │ │ │ │ │ │ └── AjaxWidget.html │ │ │ │ │ │ ├── css │ │ │ │ │ │ ├── TemplateWidget.css │ │ │ │ │ │ └── AjaxWidget.css │ │ │ │ │ │ ├── _TopicsMixin.js │ │ │ │ │ │ ├── MyWidget.js │ │ │ │ │ │ ├── TemplateWidget.js │ │ │ │ │ │ ├── RenderWidget.js │ │ │ │ │ │ ├── InputWidget.js │ │ │ │ │ │ └── AjaxWidget.js │ │ │ │ ├── config │ │ │ │ │ └── alfresco │ │ │ │ │ │ └── web-extension │ │ │ │ │ │ ├── site-webscripts │ │ │ │ │ │ └── com │ │ │ │ │ │ │ └── example │ │ │ │ │ │ │ ├── pages │ │ │ │ │ │ │ ├── pub-sub.get.html.ftl │ │ │ │ │ │ │ ├── ajax-page.get.html.ftl │ │ │ │ │ │ │ ├── simple-page.get.html.ftl │ │ │ │ │ │ │ ├── custom-widget-page.get.html.ftl │ │ │ │ │ │ │ ├── ajax-page.get.desc.xml │ │ │ │ │ │ │ ├── simple-page.get.desc.xml │ │ │ │ │ │ │ ├── custom-widget-page.get.desc.xml │ │ │ │ │ │ │ ├── pub-sub.get.desc.xml │ │ │ │ │ │ │ ├── ajax-page.get.js │ │ │ │ │ │ │ ├── custom-widget-page.get.js │ │ │ │ │ │ │ ├── pub-sub.get.js │ │ │ │ │ │ │ └── simple-page.get.js │ │ │ │ │ │ │ └── header │ │ │ │ │ │ │ └── share-header.get.js │ │ │ │ │ │ └── site-data │ │ │ │ │ │ └── extensions │ │ │ │ │ │ ├── custom-header-extension.xml │ │ │ │ │ │ └── widget-tutorial.xml │ │ │ │ ├── log4j.properties │ │ │ │ ├── file-mapping.properties │ │ │ │ └── module.properties │ │ │ └── resources │ │ │ │ └── META-INF │ │ │ │ └── share-config-custom.xml │ │ └── test │ │ │ ├── properties │ │ │ └── local │ │ │ │ └── alfresco-global.properties │ │ │ └── resources │ │ │ └── log4j.properties │ ├── tomcat │ │ └── context.xml │ └── pom.xml └── tutorial │ ├── tutorial.pdf │ ├── images │ ├── file-structure.png │ ├── part-two-pub-sub.png │ ├── part-three-header-1.png │ ├── part-three-header-2.png │ ├── part-one-simple-page-1.png │ ├── part-one-simple-page-2.png │ ├── part-one-simple-page-3.png │ ├── part-one-simple-page-4.png │ ├── part-two-advanced-page.png │ └── creativecommons-cc-by-sa.png │ ├── tutorial.md │ └── tutorial.html └── README.md /tutorial-common/header.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Alfresco Tutorials 2 | 3 | Source for Alfresco Tutorials written by Ole Hejlskov. 4 | 5 | -------------------------------------------------------------------------------- /aikau/tutorial/tutorial.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohej/alfresco-tutorials/HEAD/aikau/tutorial/tutorial.pdf -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/web/js/example/widgets/i18n/TemplateWidget.properties: -------------------------------------------------------------------------------- 1 | hello-label=Hello from i18n -------------------------------------------------------------------------------- /aikau/tutorial/images/file-structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohej/alfresco-tutorials/HEAD/aikau/tutorial/images/file-structure.png -------------------------------------------------------------------------------- /aikau/tutorial/images/part-two-pub-sub.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohej/alfresco-tutorials/HEAD/aikau/tutorial/images/part-two-pub-sub.png -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/web/js/example/widgets/i18n/PubSub.properties: -------------------------------------------------------------------------------- 1 | renderWidget.heading=Result 2 | inputWidget.inputLabel=Payload -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/web/js/example/widgets/templates/TemplateWidget.html: -------------------------------------------------------------------------------- 1 |
${greeting}
2 | -------------------------------------------------------------------------------- /aikau/tutorial/images/part-three-header-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohej/alfresco-tutorials/HEAD/aikau/tutorial/images/part-three-header-1.png -------------------------------------------------------------------------------- /aikau/tutorial/images/part-three-header-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohej/alfresco-tutorials/HEAD/aikau/tutorial/images/part-three-header-2.png -------------------------------------------------------------------------------- /aikau/tutorial/images/part-one-simple-page-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohej/alfresco-tutorials/HEAD/aikau/tutorial/images/part-one-simple-page-1.png -------------------------------------------------------------------------------- /aikau/tutorial/images/part-one-simple-page-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohej/alfresco-tutorials/HEAD/aikau/tutorial/images/part-one-simple-page-2.png -------------------------------------------------------------------------------- /aikau/tutorial/images/part-one-simple-page-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohej/alfresco-tutorials/HEAD/aikau/tutorial/images/part-one-simple-page-3.png -------------------------------------------------------------------------------- /aikau/tutorial/images/part-one-simple-page-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohej/alfresco-tutorials/HEAD/aikau/tutorial/images/part-one-simple-page-4.png -------------------------------------------------------------------------------- /aikau/tutorial/images/part-two-advanced-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohej/alfresco-tutorials/HEAD/aikau/tutorial/images/part-two-advanced-page.png -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/config/alfresco/web-extension/site-webscripts/com/example/pages/pub-sub.get.html.ftl: -------------------------------------------------------------------------------- 1 | <@processJsonModel group="share"/> 2 | -------------------------------------------------------------------------------- /aikau/tutorial/images/creativecommons-cc-by-sa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohej/alfresco-tutorials/HEAD/aikau/tutorial/images/creativecommons-cc-by-sa.png -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/config/alfresco/web-extension/site-webscripts/com/example/pages/ajax-page.get.html.ftl: -------------------------------------------------------------------------------- 1 | <@processJsonModel group="share"/> 2 | -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/config/alfresco/web-extension/site-webscripts/com/example/pages/simple-page.get.html.ftl: -------------------------------------------------------------------------------- 1 | <@processJsonModel group="share"/> 2 | -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/config/alfresco/web-extension/site-webscripts/com/example/pages/custom-widget-page.get.html.ftl: -------------------------------------------------------------------------------- 1 | <@processJsonModel group="share"/> 2 | -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/web/js/example/widgets/i18n/AjaxWidget.properties: -------------------------------------------------------------------------------- 1 | widgetTitle=Nodes found in Company Home 2 | columnName=Name 3 | columnDescription=Description 4 | -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/web/js/example/widgets/css/TemplateWidget.css: -------------------------------------------------------------------------------- 1 | .my-template-widget { 2 | border: 1px #000000 solid; 3 | padding: 1em; 4 | width: 100px; 5 | } -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/web/js/example/widgets/css/AjaxWidget.css: -------------------------------------------------------------------------------- 1 | .ajax-widget { 2 | border: 1px #EBEBEB solid; 3 | padding: 1em; 4 | } 5 | 6 | .ajax-widget td,th{ 7 | padding: 0.8em; 8 | } -------------------------------------------------------------------------------- /tutorial-common/gen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | pandoc -s --toc -c ../../tutorial-common/style.css -A ../../tutorial-common/footer.html -B ../../tutorial-common/header.html --template ../../tutorial-common/template.html tutorial.md -o tutorial.html -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/web/js/example/widgets/templates/RenderWidget.html: -------------------------------------------------------------------------------- 1 |
2 | ${renderWidgetHeading} 3 | 4 |
5 | -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/web/js/example/widgets/_TopicsMixin.js: -------------------------------------------------------------------------------- 1 | define(["dojo/_base/declare"], 2 | function(declare) { 3 | 4 | return declare(null, { 5 | TutorialTopic: "ALFRESCO_TUTORIAL_TOPIC" 6 | }); 7 | }); -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/web/js/example/widgets/templates/InputWidget.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 |
6 | -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/config/alfresco/web-extension/site-webscripts/com/example/pages/ajax-page.get.desc.xml: -------------------------------------------------------------------------------- 1 | 2 | Ajax Page 3 | Ajax page with a bit of DOM 4 | Share 5 | /ajax-page 6 | -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/config/alfresco/web-extension/site-webscripts/com/example/pages/simple-page.get.desc.xml: -------------------------------------------------------------------------------- 1 | 2 | Simple Page 3 | Simple page definition 4 | Share 5 | /simple-page 6 | -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/config/alfresco/web-extension/site-webscripts/com/example/pages/custom-widget-page.get.desc.xml: -------------------------------------------------------------------------------- 1 | 2 | Custom Widget Page 3 | Page with custom widget 4 | Share 5 | /custom-widget-page 6 | -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/config/alfresco/web-extension/site-webscripts/com/example/pages/pub-sub.get.desc.xml: -------------------------------------------------------------------------------- 1 | 2 | Publish-Subscription page 3 | Example on using pub/sub with widgets 4 | Share 5 | /pub-sub-example 6 | -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/log4j.properties: -------------------------------------------------------------------------------- 1 | # Define here logging properties for your AMP specific classes 2 | # This will end up in alfresco.war/WEB-INF/classes/alfresco/module/log4j.properties 3 | # and loaded as per http://wiki.alfresco.com/wiki/Developing_an_Alfresco_Module#log4j.properties 4 | log4j.logger.org.alfresco.demoamp=DEBUG -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/resources/META-INF/share-config-custom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/config/alfresco/web-extension/site-webscripts/com/example/pages/ajax-page.get.js: -------------------------------------------------------------------------------- 1 | model.jsonModel = { 2 | widgets: [{ 3 | id: "SET_PAGE_TITLE", 4 | name: "alfresco/header/SetTitle", 5 | config: { 6 | title: "This is an advanced page with Ajax and DOM operations" 7 | } 8 | }, 9 | { 10 | name: "example/widgets/AjaxWidget" 11 | }] 12 | }; -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/web/js/example/widgets/templates/AjaxWidget.html: -------------------------------------------------------------------------------- 1 |
2 |

${widgetTitle}

3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
${columnName}${columnDescription}
13 |
14 | -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/web/js/example/widgets/MyWidget.js: -------------------------------------------------------------------------------- 1 | define(["dojo/_base/declare", 2 | "dijit/_WidgetBase", 3 | "alfresco/core/Core" 4 | ], 5 | function(declare, _Widget, Core) { 6 | return declare([_Widget, Core], { 7 | 8 | postCreate: function example_widgets_MyWidget__postCreate() { 9 | this.inherited(arguments); 10 | alert("Hello world!"); 11 | } 12 | 13 | }); 14 | }); -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/config/alfresco/web-extension/site-webscripts/com/example/pages/custom-widget-page.get.js: -------------------------------------------------------------------------------- 1 | model.jsonModel = { 2 | widgets: [{ 3 | id: "SET_PAGE_TITLE", 4 | name: "alfresco/header/SetTitle", 5 | config: { 6 | title: "This is a simple page" 7 | } 8 | }, 9 | { 10 | name: "example/widgets/MyWidget" 11 | }, 12 | { 13 | name: "example/widgets/TemplateWidget" 14 | }] 15 | }; -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/file-mapping.properties: -------------------------------------------------------------------------------- 1 | # Custom AMP to WAR location mappings 2 | 3 | # 4 | # The following property can be used to include the standard set of mappings. 5 | # The contents of this file will override any defaults. The default is 6 | # 'true', i.e. the default mappings will be augmented or modified by values in 7 | # this file. 8 | # 9 | include.default=true 10 | 11 | # 12 | # Custom mappings. If 'include.default' is false, then this is the complete set. 13 | # 14 | /web=/ 15 | -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/config/alfresco/web-extension/site-data/extensions/custom-header-extension.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Add custom menu item to header 5 | 1.0 6 | true 7 | 8 | 9 | org.alfresco.share.header 10 | com.example.header 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/config/alfresco/web-extension/site-data/extensions/widget-tutorial.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Tutorial widgets 5 | 1.0 6 | true 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/config/alfresco/web-extension/site-webscripts/com/example/pages/pub-sub.get.js: -------------------------------------------------------------------------------- 1 | model.jsonModel = { 2 | widgets: [{ 3 | id: "SET_PAGE_TITLE", 4 | name: "alfresco/header/SetTitle", 5 | config: { 6 | title: "Pub/sub example" 7 | } 8 | }, 9 | { 10 | name: "alfresco/layout/HorizontalWidgets", 11 | config: { 12 | widgetWidth: 50, 13 | widgets: [ 14 | { 15 | name: "example/widgets/InputWidget" 16 | }, 17 | { 18 | name: "example/widgets/RenderWidget" 19 | } 20 | ] 21 | } 22 | }] 23 | }; -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/web/js/example/widgets/TemplateWidget.js: -------------------------------------------------------------------------------- 1 | define(["dojo/_base/declare", 2 | "dijit/_WidgetBase", 3 | "alfresco/core/Core", 4 | "dijit/_TemplatedMixin", 5 | "dojo/text!./templates/TemplateWidget.html" 6 | ], 7 | function(declare, _Widget, Core, _Templated, template) { 8 | return declare([_Widget, Core, _Templated], { 9 | templateString: template, 10 | i18nRequirements: [ {i18nFile: "./i18n/TemplateWidget.properties"} ], 11 | cssRequirements: [{cssFile:"./css/TemplateWidget.css"}], 12 | 13 | buildRendering: function example_widgets_TemplateWidget__buildRendering() { 14 | this.greeting = this.message('hello-label'); 15 | this.inherited(arguments); 16 | } 17 | }); 18 | }); -------------------------------------------------------------------------------- /tutorial-common/footer.html: -------------------------------------------------------------------------------- 1 | 12 |
13 |
14 | Fork me on GitHub 15 |
16 |
-------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/config/alfresco/web-extension/site-webscripts/com/example/pages/simple-page.get.js: -------------------------------------------------------------------------------- 1 | model.jsonModel = { 2 | widgets: [{ 3 | id: "SET_PAGE_TITLE", 4 | name: "alfresco/header/SetTitle", 5 | config: { 6 | title: "This is a simple page" 7 | } 8 | }, 9 | { 10 | id: "MY_HORIZONTAL_WIDGET_LAYOUT", 11 | name: "alfresco/layout/HorizontalWidgets", 12 | config: { 13 | widgetWidth: 50, 14 | widgets: [ 15 | { 16 | name: "alfresco/logo/Logo", 17 | config: { 18 | logoClasses: "alfresco-logo-only" 19 | } 20 | }, 21 | { 22 | name: "alfresco/buttons/AlfButton", 23 | config: { 24 | label: "Hello world" 25 | } 26 | } 27 | ] 28 | } 29 | }] 30 | }; -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/web/js/example/widgets/RenderWidget.js: -------------------------------------------------------------------------------- 1 | define(["dojo/_base/declare", 2 | "dijit/_WidgetBase", 3 | "alfresco/core/Core", 4 | "dojo/_base/lang", 5 | "example/widgets/_TopicsMixin", 6 | "dojo/dom-construct", 7 | "dijit/_TemplatedMixin", 8 | "dojo/text!./templates/RenderWidget.html" 9 | ], 10 | function(declare, _Widget, Core, lang, _TopicsMixin, domConstruct, _Templated, template) { 11 | return declare([_Widget, Core, _TopicsMixin, _Templated], { 12 | 13 | templateString: template, 14 | i18nRequirements: [ {i18nFile: "./i18n/PubSub.properties"} ], 15 | 16 | buildRendering: function example_widgets_renderWidget__buildRendering() { 17 | this.renderWidgetHeading = this.message('renderWidget.heading'); 18 | this.inherited(arguments); 19 | }, 20 | 21 | postCreate: function example_widget_renderWidget__postCreate() { 22 | this.alfSubscribe(this.TutorialTopic, lang.hitch(this, "_onPayloadReceive")); 23 | }, 24 | 25 | _onPayloadReceive: function example_widgets_renderWidget__onPayloadReceive(payload) { 26 | var txt = domConstruct.create( "p", { innerHTML: payload }, this.payloadContainer ); 27 | } 28 | 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/web/js/example/widgets/InputWidget.js: -------------------------------------------------------------------------------- 1 | define(["dojo/_base/declare", 2 | "dijit/_WidgetBase", 3 | "alfresco/core/Core", 4 | "dojo/_base/lang", 5 | "alfresco/buttons/AlfButton", 6 | "alfresco/forms/controls/DojoTextarea", 7 | "example/widgets/_TopicsMixin", 8 | "dijit/_TemplatedMixin", 9 | "dojo/text!./templates/InputWidget.html" 10 | ], 11 | function(declare, _Widget, Core, lang, AlfButton, DojoTextarea, _TopicsMixin, _Templated, template) { 12 | return declare([_Widget, Core, _Templated, _TopicsMixin], { 13 | 14 | templateString: template, 15 | i18nRequirements: [ {i18nFile: "./i18n/PubSub.properties"} ], 16 | 17 | postCreate: function example_widgets_InputWidget__postCreate() { 18 | this.inherited(arguments); 19 | 20 | this.textArea = new DojoTextarea({ 21 | label: this.message("inputWidget.inputLabel") 22 | }); 23 | this.textArea.placeAt(this.inputTopicNode); 24 | 25 | var btn = new AlfButton({ 26 | label: "Publish", 27 | onClick: lang.hitch(this, '_onPublish') 28 | }); 29 | btn.placeAt(this.publishTopicNode); 30 | 31 | }, 32 | 33 | _onPublish: function example_widgets_InputWidget__onPublish() { 34 | this.alfPublish(this.TutorialTopic, this.textArea.getValue()); 35 | } 36 | 37 | }); 38 | }); -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/tomcat/context.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/web/js/example/widgets/AjaxWidget.js: -------------------------------------------------------------------------------- 1 | define(["dojo/_base/declare", 2 | "dijit/_WidgetBase", 3 | "alfresco/core/Core", 4 | "alfresco/core/CoreXhr", 5 | "dojo/dom-construct", 6 | "dojo/_base/array", 7 | "dijit/_TemplatedMixin", 8 | "dojo/text!./templates/AjaxWidget.html" 9 | ], 10 | function(declare, _Widget, Core, AlfCoreXhr, domConstruct, array, _Templated, template) { 11 | return declare([_Widget, Core, AlfCoreXhr, _Templated], { 12 | templateString: template, 13 | cssRequirements: [{cssFile:"./css/AjaxWidget.css"}], 14 | i18nRequirements: [ {i18nFile: "./i18n/AjaxWidget.properties"} ], 15 | 16 | buildRendering: function example_widgets_AjaxWidget__buildRendering() { 17 | this.widgetTitle = this.message('widgetTitle'); 18 | this.columnName = this.message('columnName'); 19 | this.columnDescription = this.message('columnDescription'); 20 | this.inherited(arguments); 21 | }, 22 | 23 | postCreate: function example_widgets_AjaxWidget__postCreate() { 24 | var url = Alfresco.constants.PROXY_URI + "slingshot/doclib/treenode/node/alfresco/company/home"; 25 | this.serviceXhr({url : url, 26 | method: "GET", 27 | successCallback: this._onSuccessCallback, 28 | callbackScope: this}); 29 | }, 30 | 31 | _onSuccessCallback: function example_widgets_AjaxWidget__onSuccessCallback(response, config) { 32 | if (response.totalResults != undefined && response.totalResults > 0) { 33 | var parentNode = this.containerNode; 34 | array.forEach( response.items, function(item) { 35 | var row = domConstruct.create( "tr", {}, parentNode ); 36 | domConstruct.create( "td", { innerHTML: item.name }, row); 37 | domConstruct.create( "td", { innerHTML: item.description }, row); 38 | }); 39 | } 40 | } 41 | }); 42 | }); -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/module.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one or more 2 | # contributor license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright ownership. 4 | # The ASF licenses this file to You under the Apache License, Version 2.0 5 | # (the "License"); you may not use this file except in compliance with 6 | # the License. You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | 17 | 18 | # SDK Sample module 19 | 20 | # ==== Beginning of Alfresco required/optional properties ====== # 21 | # NB: These properties are filtered at build time by Maven, single 22 | # sourcing from POM properties 23 | module.id=${project.artifactId} 24 | #module.aliases=myModule-123, my-module 25 | module.title=${project.name} 26 | module.description=${project.description} 27 | module.version=${noSnapshotVersion} 28 | 29 | # The following optional properties can be used to prevent the module from being added 30 | # to inappropriate versions of the WAR file. 31 | # module.repo.version.min=2.0 32 | # module.repo.version.max=2.1 33 | 34 | # FIXME: This dependencies should come out of mvn dependencies on amp 35 | 36 | # The following describe dependencies on other modules 37 | # Depends on net.sf.myproject.module.SupportModuleA version ${version} or later 38 | # module.depends.net.sf.myproject.module.SupportModuleA=${version}-* 39 | # Depends on net.sf.myproject.module.SupportModuleA version ${version} to 2.0 40 | # module.depends.net.sf.myproject.module.SupportModuleB=${version}-2.0 41 | # Depends on net.sf.myproject.module.SupportModuleC - any version 42 | # module.depends.net.sf.myproject.module.SupportModuleB=* 43 | 44 | 45 | # ==== End of Alfresco required/optional properties ======= # 46 | 47 | 48 | # ==== Beginning of module required properties/optional ====== # -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/test/properties/local/alfresco-global.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one or more 2 | # contributor license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright ownership. 4 | # The ASF licenses this file to You under the Apache License, Version 2.0 5 | # (the "License"); you may not use this file except in compliance with 6 | # the License. You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | 17 | 18 | # RUN TIME PROPERTIES 19 | # ------------------- 20 | 21 | # Sample custom content and index data location 22 | # This will create alf_data Relative to appserver run folder 23 | # In this default file we take the property from the POM (for compatbility with local jetty and jboss deployments) but it can also be edited here. 24 | dir.root=${alfresco.data.location} 25 | # Allowed values are: NONE, AUTO, FULL 26 | index.recovery.mode=NONE 27 | # As we run embedded, we set Lucene 28 | index.subsystem.name=lucene 29 | 30 | #dir.keystore=. 31 | #keystore.password=storepassword 32 | #metadata.password=metapassword 33 | 34 | # Fail or not when there are node integrity checker errors 35 | integrity.failOnError=true 36 | 37 | # Database connection properties 38 | # These are also filtered from Maven at build time from POM properties. 39 | # Alternatively you can directly define them directly here 40 | db.driver=${alfresco.db.datasource.class} 41 | db.url=${alfresco.db.url} 42 | db.username=${alfresco.db.username} 43 | db.password=${alfresco.db.password} 44 | db.pool.initial=10 45 | db.pool.max=100 46 | 47 | # File servers related properties 48 | # For local builds we disable CIFS and FTP. Edit the following property to reenable them 49 | smb.server.enabled=false 50 | smb.server.name=CFS_SHARE_LOCAL 51 | smb.server.domain=mycompany.com 52 | smb.server.bindto=127.0.0.1 53 | smb.tcpip.port=1445 54 | netbios.session.port=1139 55 | netbios.name.port=1137 56 | netbios.datagram.port=1138 57 | ftp.server.enables=false 58 | ftp.port=1121 59 | ftp.authenticator=alfresco -------------------------------------------------------------------------------- /tutorial-common/template.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | $for(author-meta)$ 11 | 12 | $endfor$ 13 | $if(date-meta)$ 14 | 15 | $endif$ 16 | $if(title-prefix)$$title-prefix$ - $endif$$pagetitle$ | Alfresco Tutorials 17 | 18 | $if(quotes)$ 19 | 20 | $endif$ 21 | $if(highlighting-css)$ 22 | 25 | $endif$ 26 | $for(css)$ 27 | 28 | $endfor$ 29 | $if(math)$ 30 | $math$ 31 | $endif$ 32 | $for(header-includes)$ 33 | $header-includes$ 34 | $endfor$ 35 | 36 | 37 | 46 | 47 | 48 | 49 | $for(include-before)$ 50 | $include-before$ 51 | $endfor$ 52 | $if(title)$ 53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |

$title$

61 | $if(subtitle)$ 62 |

$subtitle$

63 | $endif$ 64 | $for(author)$ 65 |

$author$

66 | $endfor$ 67 | $if(date)$ 68 |

$date$

69 | $endif$ 70 |
71 | $endif$ 72 | $if(toc)$ 73 |
74 | $toc$ 75 |
76 | $endif$ 77 | $body$ 78 |
79 |
80 |
81 |
82 |
83 | $for(include-after)$ 84 | $include-after$ 85 | $endfor$ 86 |
87 | 88 | 89 | -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/main/amp/config/alfresco/web-extension/site-webscripts/com/example/header/share-header.get.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copied from https://issues.alfresco.com/jira/browse/ALF-19930 3 | * because of https://issues.alfresco.com/jira/browse/ALF-20384 4 | * For 4.2.e it's simply not possible to remove stuff from the header 5 | */ 6 | function findAndRemoveIn(obj, arrContext, arrIdx, id) { 7 | var idx, max, key; 8 | if (obj !== undefined && obj !== null) { 9 | if (Object.prototype.toString.apply(obj) === "[object Object]") { 10 | if (obj.hasOwnProperty("id") && obj.id === id) { 11 | if (arrContext !== null && arrIdx !== null) 12 | { 13 | arrContext.splice(arrIdx, 1); 14 | } 15 | else 16 | { 17 | logger.debug("Unexpected match outside of array structure: " + jsonUtils.toJSONString(obj)); 18 | } 19 | } else { 20 | for (key in obj) { 21 | if (obj.hasOwnProperty(key)) 22 | { 23 | findAndRemoveIn(obj[key], null, null, id); 24 | } 25 | } 26 | } 27 | } else if (Object.prototype.toString.apply(obj) === "[object Array]") { 28 | for (idx = 0, max = obj.length; idx < max; idx++) 29 | { 30 | findAndRemoveIn(obj[idx], obj, idx, id); 31 | } 32 | } 33 | } 34 | } 35 | 36 | 37 | var headerMenu = widgetUtils.findObject(model.jsonModel, "id", "HEADER_APP_MENU_BAR"); 38 | 39 | if (headerMenu != null) { 40 | /* Add menu item to My profile */ 41 | headerMenu.config.widgets.push({ 42 | id: "HEADER_CUSTOM_PROFILE_LINK", 43 | name: "alfresco/menus/AlfMenuBarItem", 44 | config: { 45 | label: "My profile", 46 | targetUrl: "user/" + encodeURIComponent(user.name) + "/profile" 47 | } 48 | }); 49 | 50 | /* Dropdown example */ 51 | headerMenu.config.widgets.push({ 52 | id: "HEADER_CUSTOM_DROPDOWN", 53 | name: "alfresco/header/AlfMenuBarPopup", 54 | config: { 55 | label: "Dropdown", 56 | widgets: [ 57 | { 58 | name: "alfresco/menus/AlfMenuBarItem", 59 | config: { 60 | label: "Link #1", 61 | targetUrl: "/" 62 | } 63 | } 64 | ] 65 | } 66 | }); 67 | 68 | /* Remove Shared files */ 69 | findAndRemoveIn(model.jsonModel.widgets, null, null, "HEADER_SHARED_FILES"); 70 | } 71 | 72 | /* Add link on user dashboard */ 73 | if (page.titleId == "page.userDashboard.title") { 74 | var navMenu = widgetUtils.findObject(model.jsonModel, "id", "HEADER_NAVIGATION_MENU_BAR"); 75 | if (navMenu != null) { 76 | navMenu.config.widgets.push({ 77 | name: "alfresco/menus/AlfMenuBarItem", 78 | config: { 79 | label: "Custom link" 80 | } 81 | }); 82 | } 83 | } 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | com.example 6 | aikau-tutorial-share 7 | 1.0-SNAPSHOT 8 | Aikau Tutorial 9 | amp 10 | Example code for the Aikau tutorial 11 | 12 | 13 | org.alfresco.maven 14 | alfresco-sdk-parent 15 | 1.1.1 16 | 17 | 18 | 23 | 24 | 25 | org.alfresco 26 | 27 | 4.2.f 28 | WARN 29 | alf_data_dev 30 | 32 | share 33 | 35 | org.alfresco 36 | 37 | 4.2.f 38 | 39 | local 40 | 41 | 42 | 8081 43 | 44 | 45 | 46 | 48 | 49 | 50 | 54 | 55 | ${alfresco.groupId} 56 | alfresco-platform-distribution 57 | ${alfresco.version} 58 | pom 59 | import 60 | 61 | 62 | 63 | 66 | 67 | 68 | ${alfresco.groupId} 69 | alfresco-repository 70 | 71 | 72 | 73 | junit 74 | junit 75 | 4.8.1 76 | test 77 | 78 | 79 | 80 | org.springframework.extensions.surf 81 | spring-webscripts 82 | 1.2.0-M14 83 | provided 84 | 85 | 86 | 87 | 88 | 89 | 97 | 98 | 99 | alfresco-public 100 | https://artifacts.alfresco.com/nexus/content/groups/public 101 | 102 | 103 | alfresco-public-snapshots 104 | https://artifacts.alfresco.com/nexus/content/groups/public-snapshots 105 | 106 | true 107 | daily 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /aikau/aikau-tutorial-share/src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | # This is a full override of Alfresco 4.2.b log4j.properties 2 | # This file overwrites the alfresco.war log4j.properties 3 | 4 | # Set root logger level to error 5 | log4j.rootLogger=${app.log.root.level}, Console, File 6 | 7 | ###### Console appender definition ####### 8 | 9 | # All outputs currently set to be a ConsoleAppender. 10 | log4j.appender.Console=org.apache.log4j.ConsoleAppender 11 | log4j.appender.Console.layout=org.apache.log4j.PatternLayout 12 | 13 | # use log4j NDC to replace %x with tenant domain / username 14 | log4j.appender.Console.layout.ConversionPattern=%d{ISO8601} %x %-5p [%c{3}] [%t] %m%n 15 | #log4j.appender.Console.layout.ConversionPattern=%d{ABSOLUTE} %-5p [%c] %m%n 16 | 17 | ###### File appender definition ####### 18 | log4j.appender.File=org.apache.log4j.DailyRollingFileAppender 19 | log4j.appender.File.File=${app.log.dir}alfresco.log 20 | log4j.appender.File.Append=true 21 | log4j.appender.File.DatePattern='.'yyyy-MM-dd 22 | log4j.appender.File.layout=org.apache.log4j.PatternLayout 23 | log4j.appender.File.layout.ConversionPattern=%d{ABSOLUTE} %-5p [%c] %m%n 24 | 25 | ###### Hibernate specific appender definition ####### 26 | #log4j.appender.file=org.apache.log4j.FileAppender 27 | #log4j.appender.file.File=hibernate.log 28 | #log4j.appender.file.layout=org.apache.log4j.PatternLayout 29 | #log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n 30 | 31 | ###### Log level overrides ####### 32 | 33 | # Commented-in loggers will be exposed as JMX MBeans (refer to org.alfresco.repo.admin.Log4JHierarchyInit) 34 | # Hence, generally useful loggers should be listed with at least ERROR level to allow simple runtime 35 | # control of the level via a suitable JMX Console. Also, any other loggers can be added transiently via 36 | # Log4j addLoggerMBean as long as the logger exists and has been loaded. 37 | 38 | # Hibernate 39 | log4j.logger.org.hibernate=error 40 | log4j.logger.org.hibernate.util.JDBCExceptionReporter=fatal 41 | log4j.logger.org.hibernate.event.def.AbstractFlushingEventListener=fatal 42 | log4j.logger.org.hibernate.type=warn 43 | log4j.logger.org.hibernate.cfg.SettingsFactory=warn 44 | 45 | # Spring 46 | log4j.logger.org.springframework=warn 47 | # Turn off Spring remoting warnings that should really be info or debug. 48 | log4j.logger.org.springframework.remoting.support=error 49 | log4j.logger.org.springframework.util=error 50 | 51 | # Axis/WSS4J 52 | log4j.logger.org.apache.axis=info 53 | log4j.logger.org.apache.ws=info 54 | 55 | # CXF 56 | log4j.logger.org.apache.cxf=error 57 | 58 | # MyFaces 59 | log4j.logger.org.apache.myfaces.util.DebugUtils=info 60 | log4j.logger.org.apache.myfaces.el.VariableResolverImpl=error 61 | log4j.logger.org.apache.myfaces.application.jsp.JspViewHandlerImpl=error 62 | log4j.logger.org.apache.myfaces.taglib=error 63 | 64 | # OpenOfficeConnection 65 | log4j.logger.net.sf.jooreports.openoffice.connection=fatal 66 | 67 | # log prepared statement cache activity ### 68 | log4j.logger.org.hibernate.ps.PreparedStatementCache=info 69 | 70 | # Alfresco 71 | log4j.logger.org.alfresco=error 72 | log4j.logger.org.alfresco.repo.admin=info 73 | log4j.logger.org.alfresco.repo.cache.TransactionalCache=warn 74 | log4j.logger.org.alfresco.repo.model.filefolder=warn 75 | log4j.logger.org.alfresco.repo.tenant=info 76 | log4j.logger.org.alfresco.repo.avm=info 77 | log4j.logger.org.alfresco.config=warn 78 | log4j.logger.org.alfresco.config.JndiObjectFactoryBean=warn 79 | log4j.logger.org.alfresco.config.JBossEnabledWebApplicationContext=warn 80 | log4j.logger.org.alfresco.repo.management.subsystems=warn 81 | log4j.logger.org.alfresco.repo.management.subsystems.ChildApplicationContextFactory=info 82 | log4j.logger.org.alfresco.repo.management.subsystems.ChildApplicationContextFactory$ChildApplicationContext=warn 83 | log4j.logger.org.alfresco.repo.security.sync=info 84 | log4j.logger.org.alfresco.repo.security.person=info 85 | 86 | log4j.logger.org.alfresco.sample=info 87 | log4j.logger.org.alfresco.web=info 88 | #log4j.logger.org.alfresco.web.app.AlfrescoNavigationHandler=debug 89 | #log4j.logger.org.alfresco.web.ui.repo.component.UIActions=debug 90 | #log4j.logger.org.alfresco.web.ui.repo.tag.PageTag=debug 91 | #log4j.logger.org.alfresco.web.bean.clipboard=debug 92 | log4j.logger.org.alfresco.repo.webservice=info 93 | log4j.logger.org.alfresco.service.descriptor.DescriptorService=info 94 | #log4j.logger.org.alfresco.web.page=debug 95 | 96 | log4j.logger.org.alfresco.repo.importer.ImporterBootstrap=error 97 | #log4j.logger.org.alfresco.repo.importer.ImporterBootstrap=info 98 | 99 | log4j.logger.org.alfresco.web.ui.common.Utils=error 100 | #log4j.logger.org.alfresco.web.ui.common.Utils=info 101 | 102 | log4j.logger.org.alfresco.repo.admin.patch.PatchExecuter=info 103 | log4j.logger.org.alfresco.repo.domain.patch.ibatis.PatchDAOImpl=info 104 | 105 | # Specific patches 106 | log4j.logger.org.alfresco.repo.admin.patch.impl.DeploymentMigrationPatch=info 107 | log4j.logger.org.alfresco.repo.version.VersionMigrator=info 108 | log4j.logger.org.alfresco.repo.admin.patch.impl.ResetWCMToGroupBasedPermissionsPatch=info 109 | 110 | log4j.logger.org.alfresco.repo.module.ModuleServiceImpl=info 111 | log4j.logger.org.alfresco.repo.domain.schema.SchemaBootstrap=info 112 | log4j.logger.org.alfresco.repo.admin.ConfigurationChecker=info 113 | log4j.logger.org.alfresco.repo.node.index.AbstractReindexComponent=warn 114 | log4j.logger.org.alfresco.repo.node.index.IndexTransactionTracker=warn 115 | log4j.logger.org.alfresco.repo.node.index.FullIndexRecoveryComponent=info 116 | log4j.logger.org.alfresco.repo.node.index.AVMFullIndexRecoveryComponent=info 117 | log4j.logger.org.alfresco.util.OpenOfficeConnectionTester=info 118 | log4j.logger.org.alfresco.repo.node.db.hibernate.HibernateNodeDaoServiceImpl=warn 119 | log4j.logger.org.alfresco.repo.domain.hibernate.DirtySessionMethodInterceptor=warn 120 | log4j.logger.org.alfresco.repo.transaction.RetryingTransactionHelper=warn 121 | log4j.logger.org.alfresco.util.transaction.SpringAwareUserTransaction.trace=warn 122 | log4j.logger.org.alfresco.util.AbstractTriggerBean=warn 123 | log4j.logger.org.alfresco.enterprise.repo.cache.cluster.KeepAliveHeartbeatReceiver=info 124 | log4j.logger.org.alfresco.repo.version.Version2ServiceImpl=warn 125 | 126 | #log4j.logger.org.alfresco.web.app.DebugPhaseListener=debug 127 | 128 | log4j.logger.org.alfresco.repo.workflow=info 129 | 130 | # CIFS server debugging 131 | log4j.logger.org.alfresco.smb.protocol=error 132 | #log4j.logger.org.alfresco.smb.protocol.auth=debug 133 | #log4j.logger.org.alfresco.acegi=debug 134 | 135 | # FTP server debugging 136 | log4j.logger.org.alfresco.ftp.protocol=error 137 | #log4j.logger.org.alfresco.ftp.server=debug 138 | 139 | # WebDAV debugging 140 | #log4j.logger.org.alfresco.webdav.protocol=debug 141 | log4j.logger.org.alfresco.webdav.protocol=error 142 | 143 | # NTLM servlet filters 144 | #log4j.logger.org.alfresco.web.app.servlet.NTLMAuthenticationFilter=debug 145 | #log4j.logger.org.alfresco.repo.webdav.auth.NTLMAuthenticationFilter=debug 146 | 147 | # Kerberos servlet filters 148 | #log4j.logger.org.alfresco.web.app.servlet.KerberosAuthenticationFilter=debug 149 | #log4j.logger.org.alfresco.repo.webdav.auth.KerberosAuthenticationFilter=debug 150 | 151 | # File servers 152 | log4j.logger.org.alfresco.fileserver=warn 153 | 154 | # Repo filesystem debug logging 155 | #log4j.logger.org.alfresco.filesys.repo.ContentDiskDriver=debug 156 | 157 | # AVM filesystem debug logging 158 | #log4j.logger.org.alfresco.filesys.avm.AVMDiskDriver=debug 159 | 160 | # Integrity message threshold - if 'failOnViolation' is off, then WARNINGS are generated 161 | log4j.logger.org.alfresco.repo.node.integrity=ERROR 162 | 163 | # Indexer debugging 164 | log4j.logger.org.alfresco.repo.search.Indexer=error 165 | #log4j.logger.org.alfresco.repo.search.Indexer=debug 166 | 167 | log4j.logger.org.alfresco.repo.search.impl.lucene.index=error 168 | log4j.logger.org.alfresco.repo.search.impl.lucene.fts.FullTextSearchIndexerImpl=warn 169 | #log4j.logger.org.alfresco.repo.search.impl.lucene.index=DEBUG 170 | 171 | # Audit debugging 172 | # log4j.logger.org.alfresco.repo.audit=DEBUG 173 | # log4j.logger.org.alfresco.repo.audit.model=DEBUG 174 | 175 | # Forms debugging 176 | # log4j.logger.org.alfresco.web.forms=debug 177 | # log4j.logger.org.chiba.xml.xforms=debug 178 | log4j.logger.org.alfresco.web.forms.xforms.XFormsBean=error 179 | log4j.logger.org.alfresco.web.forms.XSLTRenderingEngine=error 180 | 181 | # Property sheet and modelling debugging 182 | # change to error to hide the warnings about missing properties and associations 183 | log4j.logger.alfresco.missingProperties=warn 184 | log4j.logger.org.alfresco.web.ui.repo.component.property.UIChildAssociation=warn 185 | log4j.logger.org.alfresco.web.ui.repo.component.property.UIAssociation=warn 186 | #log4j.logger.org.alfresco.web.ui.repo.component.property=debug 187 | 188 | # Dictionary/Model debugging 189 | log4j.logger.org.alfresco.repo.dictionary=warn 190 | log4j.logger.org.alfresco.repo.dictionary.types.period=warn 191 | 192 | # Virtualization Server Registry 193 | log4j.logger.org.alfresco.mbeans.VirtServerRegistry=error 194 | 195 | # Spring context runtime property setter 196 | log4j.logger.org.alfresco.util.RuntimeSystemPropertiesSetter=info 197 | 198 | # Debugging options for clustering 199 | log4j.logger.org.alfresco.repo.content.ReplicatingContentStore=error 200 | log4j.logger.org.alfresco.repo.content.replication=error 201 | 202 | #log4j.logger.org.alfresco.repo.deploy.DeploymentServiceImpl=debug 203 | 204 | # Activity service 205 | log4j.logger.org.alfresco.repo.activities=warn 206 | 207 | # User usage tracking 208 | log4j.logger.org.alfresco.repo.usage=info 209 | 210 | # Sharepoint 211 | log4j.logger.org.alfresco.module.vti=info 212 | 213 | # Forms Engine 214 | log4j.logger.org.alfresco.repo.forms=info 215 | log4j.logger.org.alfresco.web.config.forms=info 216 | log4j.logger.org.alfresco.web.scripts.forms=info 217 | 218 | # CMIS 219 | log4j.logger.org.alfresco.opencmis=error 220 | log4j.logger.org.alfresco.opencmis.AlfrescoCmisServiceInterceptor=error 221 | log4j.logger.org.alfresco.cmis=error 222 | log4j.logger.org.alfresco.cmis.dictionary=warn 223 | log4j.logger.org.apache.chemistry.opencmis=info 224 | 225 | # IMAP 226 | log4j.logger.org.alfresco.repo.imap=info 227 | 228 | # JBPM 229 | # Note: non-fatal errors (eg. logged during job execution) should be handled by Alfresco's retrying transaction handler 230 | log4j.logger.org.jbpm.graph.def.GraphElement=fatal 231 | 232 | #log4j.logger.org.alfresco.repo.googledocs=debug 233 | 234 | ###### Scripting ####### 235 | 236 | # Web Framework 237 | log4j.logger.org.springframework.extensions.webscripts=info 238 | log4j.logger.org.springframework.extensions.webscripts.ScriptLogger=warn 239 | log4j.logger.org.springframework.extensions.webscripts.ScriptDebugger=off 240 | 241 | # Repository 242 | log4j.logger.org.alfresco.repo.web.scripts=warn 243 | log4j.logger.org.alfresco.repo.web.scripts.BaseWebScriptTest=info 244 | log4j.logger.org.alfresco.repo.web.scripts.AlfrescoRhinoScriptDebugger=off 245 | log4j.logger.org.alfresco.repo.jscript=error 246 | log4j.logger.org.alfresco.repo.jscript.ScriptLogger=warn 247 | log4j.logger.org.alfresco.repo.cmis.rest.CMISTest=info 248 | 249 | log4j.logger.org.alfresco.repo.avm.actions=info 250 | 251 | # Freemarker 252 | # Note the freemarker.runtime logger is used to log non-fatal errors that are handled by Alfresco's retrying transaction handler 253 | log4j.logger.freemarker.runtime= 254 | 255 | # Metadata extraction 256 | log4j.logger.org.alfresco.repo.content.metadata.AbstractMappingMetadataExtracter=warn 257 | 258 | # Reduces PDFont error level due to ALF-7105 259 | log4j.logger.org.apache.pdfbox.pdmodel.font.PDSimpleFont=fatal 260 | log4j.logger.org.apache.pdfbox.pdmodel.font.PDFont=fatal 261 | log4j.logger.org.apache.pdfbox.pdmodel.font.PDCIDFont=fatal 262 | 263 | # no index support 264 | log4j.logger.org.alfresco.repo.search.impl.noindex.NoIndexIndexer=fatal 265 | log4j.logger.org.alfresco.repo.search.impl.noindex.NoIndexSearchService=fatal 266 | log4j.logger.org.alfresco.demoamp.test=DEBUG -------------------------------------------------------------------------------- /tutorial-common/style.css: -------------------------------------------------------------------------------- 1 | /* 2 | Theme Name: Twenty Ten 3 | Theme URI: http://wordpress.org/themes/twentyten 4 | Description: The 2010 theme for WordPress is stylish, customizable, simple, and readable -- make it yours with a custom menu, header image, and background. Twenty Ten supports six widgetized areas (two in the sidebar, four in the footer) and featured images (thumbnails for gallery posts and custom header images for posts and pages). It includes stylesheets for print and the admin Visual Editor, special styles for posts in the "Asides" and "Gallery" categories, and has an optional one-column page template that removes the sidebar. 5 | Author: the WordPress team 6 | Author URI: http://wordpress.org/ 7 | Version: 1.6 8 | License: GNU General Public License v2 or later 9 | License URI: http://www.gnu.org/licenses/gpl-2.0.html 10 | Tags: black, blue, white, two-columns, fixed-width, custom-header, custom-background, threaded-comments, sticky-post, translation-ready, microformats, rtl-language-support, editor-style, custom-menu, flexible-header 11 | Text Domain: twentyten 12 | */ 13 | 14 | 15 | /* =Reset default browser CSS. Based on work by Eric Meyer: http://meyerweb.com/eric/tools/css/reset/index.html 16 | -------------------------------------------------------------- */ 17 | 18 | html, body, div, span, applet, object, iframe, 19 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 20 | a, abbr, acronym, address, big, cite, code, 21 | del, dfn, em, font, img, ins, kbd, q, s, samp, 22 | small, strike, strong, sub, sup, tt, var, 23 | b, u, i, center, 24 | dl, dt, dd, ol, ul, li, 25 | fieldset, form, label, legend, 26 | table, caption, tbody, tfoot, thead, tr, th, td { 27 | background: transparent; 28 | border: 0; 29 | margin: 0; 30 | padding: 0; 31 | vertical-align: baseline; 32 | } 33 | body { 34 | line-height: 1; 35 | } 36 | p.caption { 37 | font-size: 90%; 38 | font-family: "Helvetica Neue", Arial, Helvetica, "Nimbus Sans L", sans-serif; 39 | font-style: italic; 40 | } 41 | h1 a, h2 a, h3 a, h4 a, h5 a { 42 | text-decoration: none; 43 | color: #000000; 44 | } 45 | h1, h2, h3, h4, h5 { 46 | font-family: "Helvetica Neue", Arial, Helvetica, "Nimbus Sans L", sans-serif; 47 | color: #000000; 48 | } 49 | h1 { 50 | clear: both; 51 | font-weight: normal; 52 | font-size: 130%; 53 | } 54 | h2 { 55 | clear: both; 56 | font-weight: normal; 57 | font-size: 110%; 58 | } 59 | h3 { 60 | clear: both; 61 | font-weight: normal; 62 | font-size: 95%; 63 | } 64 | h4, h5, h6 { 65 | clear: both; 66 | font-weight: normal; 67 | font-size: 90%; 68 | font-style: italic; 69 | } 70 | ol img {margin-bottom: 1ex} 71 | ol, ul { 72 | list-style: none; 73 | } 74 | blockquote { 75 | quotes: none; 76 | } 77 | blockquote:before, blockquote:after { 78 | content: ''; 79 | content: none; 80 | } 81 | del { 82 | text-decoration: line-through; 83 | } 84 | /* tables still need 'cellspacing="0"' in the markup */ 85 | table { 86 | border-collapse: collapse; 87 | border-spacing: 0; 88 | } 89 | a img { 90 | border: none; 91 | } 92 | 93 | /* =Layout 94 | -------------------------------------------------------------- */ 95 | 96 | /* 97 | LAYOUT: Two columns 98 | DESCRIPTION: Two-column fixed layout with one sidebar right of content 99 | */ 100 | 101 | #container { 102 | float: left; 103 | margin: 0 -240px 0 0; 104 | width: 100%; 105 | } 106 | #content { 107 | margin: 0 280px 0 20px; 108 | } 109 | #primary, 110 | #secondary { 111 | float: right; 112 | overflow: hidden; 113 | width: 220px; 114 | } 115 | #secondary { 116 | clear: right; 117 | } 118 | #footer { 119 | clear: both; 120 | width: 100%; 121 | } 122 | 123 | /* 124 | LAYOUT: One column, no sidebar 125 | DESCRIPTION: One centered column with no sidebar 126 | */ 127 | 128 | .one-column #content { 129 | margin: 0 auto; 130 | width: 710px; 131 | } 132 | 133 | /* 134 | LAYOUT: Full width, no sidebar 135 | DESCRIPTION: Full width content with no sidebar; used for attachment pages 136 | */ 137 | 138 | .single-attachment #content { 139 | margin: 0 auto; 140 | width: 900px; 141 | } 142 | 143 | 144 | /* =Fonts 145 | -------------------------------------------------------------- */ 146 | body, 147 | input, 148 | textarea, 149 | .page-title span, 150 | .pingback a.url { 151 | font-family: Georgia, "Bitstream Charter", serif; 152 | } 153 | h3#comments-title, 154 | h3#reply-title, 155 | #access .menu, 156 | #access div.menu ul, 157 | #cancel-comment-reply-link, 158 | .form-allowed-tags, 159 | #site-info, 160 | #site-title, 161 | #wp-calendar, 162 | .comment-meta, 163 | .comment-body tr th, 164 | .comment-body thead th, 165 | .entry-content label, 166 | .entry-content tr th, 167 | .entry-content thead th, 168 | .entry-meta, 169 | .entry-title, 170 | .entry-utility, 171 | #respond label, 172 | .navigation, 173 | .page-title, 174 | .pingback p, 175 | .reply, 176 | .widget-title, 177 | .wp-caption-text { 178 | font-family: "Helvetica Neue", Arial, Helvetica, "Nimbus Sans L", sans-serif; 179 | } 180 | input[type="submit"] { 181 | font-family: "Helvetica Neue", Arial, Helvetica, "Nimbus Sans L", sans-serif; 182 | } 183 | pre { 184 | font-family: "Courier 10 Pitch", Courier, monospace; 185 | } 186 | code { 187 | font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; 188 | } 189 | 190 | 191 | /* =Structure 192 | -------------------------------------------------------------- */ 193 | 194 | /* The main theme structure */ 195 | #access .menu-header, 196 | div.menu, 197 | #colophon, 198 | #branding, 199 | #main, 200 | #wrapper { 201 | margin: 0 auto; 202 | width: 940px; 203 | } 204 | #wrapper { 205 | background: #fff; 206 | margin-top: 20px; 207 | padding: 0 20px; 208 | } 209 | 210 | /* Structure the footer area */ 211 | #footer-widget-area { 212 | overflow: hidden; 213 | } 214 | #footer-widget-area .widget-area { 215 | float: left; 216 | margin-right: 20px; 217 | width: 220px; 218 | } 219 | #footer-widget-area #fourth { 220 | margin-right: 0; 221 | } 222 | #site-info { 223 | float: left; 224 | font-size: 14px; 225 | font-weight: bold; 226 | width: 700px; 227 | } 228 | #site-generator { 229 | float: right; 230 | width: 220px; 231 | } 232 | 233 | 234 | /* =Global Elements 235 | -------------------------------------------------------------- */ 236 | 237 | /* Main global 'theme' and typographic styles */ 238 | body { 239 | background: #f1f1f1; 240 | } 241 | body, 242 | input, 243 | textarea { 244 | color: #666; 245 | font-size: 12px; 246 | line-height: 18px; 247 | } 248 | hr { 249 | background-color: #e7e7e7; 250 | border: 0; 251 | clear: both; 252 | height: 1px; 253 | margin-bottom: 18px; 254 | } 255 | 256 | /* Text elements */ 257 | p { 258 | margin-bottom: 18px; 259 | } 260 | ul { 261 | list-style: square; 262 | margin: 0 0 18px 1.5em; 263 | } 264 | ol { 265 | list-style: decimal; 266 | margin: 0 0 18px 1.5em; 267 | } 268 | ol ol { 269 | list-style: upper-alpha; 270 | } 271 | ol ol ol { 272 | list-style: lower-roman; 273 | } 274 | ol ol ol ol { 275 | list-style: lower-alpha; 276 | } 277 | ul ul, 278 | ol ol, 279 | ul ol, 280 | ol ul { 281 | margin-bottom: 0; 282 | } 283 | dl { 284 | margin: 0 0 24px 0; 285 | } 286 | dt { 287 | font-weight: bold; 288 | } 289 | dd { 290 | margin-bottom: 18px; 291 | } 292 | strong { 293 | font-weight: bold; 294 | } 295 | cite, 296 | em, 297 | i { 298 | font-style: italic; 299 | } 300 | big { 301 | font-size: 131.25%; 302 | } 303 | ins { 304 | background: #ffc; 305 | text-decoration: none; 306 | } 307 | blockquote { 308 | font-style: italic; 309 | padding: 0 3em; 310 | } 311 | blockquote cite, 312 | blockquote em, 313 | blockquote i { 314 | font-style: normal; 315 | } 316 | pre { 317 | background: #f7f7f7; 318 | color: #222; 319 | line-height: 18px; 320 | margin-bottom: 18px; 321 | overflow: auto; 322 | padding: 1.5em; 323 | } 324 | abbr, 325 | acronym { 326 | border-bottom: 1px dotted #666; 327 | cursor: help; 328 | } 329 | sup, 330 | sub { 331 | height: 0; 332 | line-height: 1; 333 | position: relative; 334 | vertical-align: baseline; 335 | } 336 | sup { 337 | bottom: 1ex; 338 | } 339 | sub { 340 | top: .5ex; 341 | } 342 | small { 343 | font-size: smaller; 344 | } 345 | input[type="text"], 346 | input[type="password"], 347 | input[type="email"], 348 | input[type="url"], 349 | input[type="number"], 350 | textarea { 351 | background: #f9f9f9; 352 | border: 1px solid #ccc; 353 | box-shadow: inset 1px 1px 1px rgba(0,0,0,0.1); 354 | -moz-box-shadow: inset 1px 1px 1px rgba(0,0,0,0.1); 355 | -webkit-box-shadow: inset 1px 1px 1px rgba(0,0,0,0.1); 356 | padding: 2px; 357 | } 358 | 359 | /* Text meant only for screen readers */ 360 | .screen-reader-text { 361 | position: absolute; 362 | left: -9000px; 363 | } 364 | 365 | 366 | /* =Header 367 | -------------------------------------------------------------- */ 368 | 369 | #header { 370 | padding: 30px 0 0 0; 371 | } 372 | #site-title { 373 | float: left; 374 | font-size: 30px; 375 | line-height: 36px; 376 | margin: 0 0 18px 0; 377 | width: 700px; 378 | } 379 | #site-title a { 380 | color: #000; 381 | font-weight: bold; 382 | text-decoration: none; 383 | } 384 | #site-description { 385 | clear: right; 386 | float: right; 387 | font-style: italic; 388 | margin: 15px 0 18px 0; 389 | width: 220px; 390 | } 391 | 392 | /* This is the custom header image */ 393 | #branding img { 394 | border-top: 4px solid #000; 395 | border-bottom: 1px solid #000; 396 | display: block; 397 | float: left; 398 | } 399 | 400 | 401 | /* =Menu 402 | -------------------------------------------------------------- */ 403 | 404 | #access { 405 | background: #000; 406 | display: block; 407 | float: left; 408 | margin: 0 auto; 409 | width: 940px; 410 | } 411 | #access .menu-header, 412 | div.menu { 413 | font-size: 13px; 414 | margin-left: 12px; 415 | width: 928px; 416 | } 417 | #access .menu-header ul, 418 | div.menu ul { 419 | list-style: none; 420 | margin: 0; 421 | } 422 | #access .menu-header li, 423 | div.menu li { 424 | float: left; 425 | position: relative; 426 | } 427 | #access a { 428 | color: #aaa; 429 | display: block; 430 | line-height: 38px; 431 | padding: 0 10px; 432 | text-decoration: none; 433 | } 434 | #access ul ul { 435 | box-shadow: 0px 3px 3px rgba(0,0,0,0.2); 436 | -moz-box-shadow: 0px 3px 3px rgba(0,0,0,0.2); 437 | -webkit-box-shadow: 0px 3px 3px rgba(0,0,0,0.2); 438 | display: none; 439 | position: absolute; 440 | top: 38px; 441 | left: 0; 442 | float: left; 443 | width: 180px; 444 | z-index: 99999; 445 | } 446 | #access ul ul li { 447 | min-width: 180px; 448 | } 449 | #access ul ul ul { 450 | left: 100%; 451 | top: 0; 452 | } 453 | #access ul ul a { 454 | background: #333; 455 | line-height: 1em; 456 | padding: 10px; 457 | width: 160px; 458 | height: auto; 459 | } 460 | #access li:hover > a, 461 | #access ul ul :hover > a { 462 | background: #333; 463 | color: #fff; 464 | } 465 | #access ul li:hover > ul { 466 | display: block; 467 | } 468 | #access ul li.current_page_item > a, 469 | #access ul li.current_page_ancestor > a, 470 | #access ul li.current-menu-ancestor > a, 471 | #access ul li.current-menu-item > a, 472 | #access ul li.current-menu-parent > a { 473 | color: #fff; 474 | } 475 | * html #access ul li.current_page_item a, 476 | * html #access ul li.current_page_ancestor a, 477 | * html #access ul li.current-menu-ancestor a, 478 | * html #access ul li.current-menu-item a, 479 | * html #access ul li.current-menu-parent a, 480 | * html #access ul li a:hover { 481 | color: #fff; 482 | } 483 | 484 | 485 | /* =Content 486 | -------------------------------------------------------------- */ 487 | 488 | #main { 489 | clear: both; 490 | overflow: hidden; 491 | padding: 40px 0 0 0; 492 | } 493 | #content { 494 | margin-bottom: 36px; 495 | } 496 | #content, 497 | #content input, 498 | #content textarea { 499 | color: #333; 500 | font-size: 16px; 501 | line-height: 24px; 502 | } 503 | #content p, 504 | #content ul, 505 | #content ol, 506 | #content dd, 507 | #content pre, 508 | #content hr { 509 | margin-bottom: 24px; 510 | } 511 | #content ul ul, 512 | #content ol ol, 513 | #content ul ol, 514 | #content ol ul { 515 | margin-bottom: 0; 516 | } 517 | #content pre, 518 | #content kbd, 519 | #content tt, 520 | #content var { 521 | font-size: 15px; 522 | line-height: 21px; 523 | } 524 | #content code { 525 | font-size: 12px; 526 | background-color: #F7F7F7; 527 | } 528 | #content dt, 529 | #content th { 530 | color: #000; 531 | } 532 | #content h1, 533 | #content h2, 534 | #content h3, 535 | #content h4, 536 | #content h5, 537 | #content h6 { 538 | color: #000; 539 | line-height: 1.5em; 540 | margin: 0 0 20px 0; 541 | } 542 | #content table { 543 | border: 1px solid #e7e7e7; 544 | margin: 0 -1px 24px 0; 545 | text-align: left; 546 | width: 100%; 547 | } 548 | #content tr th, 549 | #content thead th { 550 | color: #777; 551 | font-size: 12px; 552 | font-weight: bold; 553 | line-height: 18px; 554 | padding: 9px 24px; 555 | } 556 | #content tr td { 557 | border-top: 1px solid #e7e7e7; 558 | padding: 6px 24px; 559 | } 560 | #content tr.odd td { 561 | background: #f2f7fc; 562 | } 563 | .hentry { 564 | margin: 0 0 48px 0; 565 | } 566 | .home .sticky { 567 | background: #f2f7fc; 568 | border-top: 4px solid #000; 569 | margin-left: -20px; 570 | margin-right: -20px; 571 | padding: 18px 20px; 572 | } 573 | .single .hentry { 574 | margin: 0 0 36px 0; 575 | } 576 | .page-title { 577 | color: #000; 578 | font-size: 14px; 579 | font-weight: bold; 580 | margin: 0 0 36px 0; 581 | } 582 | .page-title span { 583 | color: #333; 584 | font-size: 16px; 585 | font-style: italic; 586 | font-weight: normal; 587 | } 588 | .page-title a:link, 589 | .page-title a:visited { 590 | color: #777; 591 | text-decoration: none; 592 | } 593 | .page-title a:active, 594 | .page-title a:hover { 595 | color: #ff4b33; 596 | } 597 | #content .entry-title { 598 | color: #000; 599 | font-size: 21px; 600 | font-weight: bold; 601 | line-height: 1.3em; 602 | margin-bottom: 0; 603 | } 604 | .entry-title a:link, 605 | .entry-title a:visited { 606 | color: #000; 607 | text-decoration: none; 608 | } 609 | .entry-title a:active, 610 | .entry-title a:hover { 611 | color: #ff4b33; 612 | } 613 | .entry-meta { 614 | color: #777; 615 | font-size: 12px; 616 | } 617 | .entry-meta abbr, 618 | .entry-utility abbr { 619 | border: none; 620 | } 621 | .entry-meta abbr:hover, 622 | .entry-utility abbr:hover { 623 | border-bottom: 1px dotted #666; 624 | } 625 | .entry-content, 626 | .entry-summary { 627 | clear: both; 628 | padding: 12px 0 0 0; 629 | } 630 | #content .entry-summary p:last-child { 631 | margin-bottom: 12px; 632 | } 633 | .entry-content fieldset { 634 | border: 1px solid #e7e7e7; 635 | margin: 0 0 24px 0; 636 | padding: 24px; 637 | } 638 | .entry-content fieldset legend { 639 | background: #fff; 640 | color: #000; 641 | font-weight: bold; 642 | padding: 0 24px; 643 | } 644 | .entry-content input { 645 | margin: 0 0 24px 0; 646 | } 647 | .entry-content input.file, 648 | .entry-content input.button { 649 | margin-right: 24px; 650 | } 651 | .entry-content label { 652 | color: #777; 653 | font-size: 12px; 654 | } 655 | .entry-content select { 656 | margin: 0 0 24px 0; 657 | } 658 | .entry-content sup, 659 | .entry-content sub { 660 | font-size: 10px; 661 | } 662 | .entry-content blockquote.left { 663 | float: left; 664 | margin-left: 0; 665 | margin-right: 24px; 666 | text-align: right; 667 | width: 33%; 668 | } 669 | .entry-content blockquote.right { 670 | float: right; 671 | margin-left: 24px; 672 | margin-right: 0; 673 | text-align: left; 674 | width: 33%; 675 | } 676 | .page-link { 677 | clear: both; 678 | color: #000; 679 | font-weight: bold; 680 | line-height: 48px; 681 | word-spacing: 0.5em; 682 | } 683 | .page-link a:link, 684 | .page-link a:visited { 685 | background: #f1f1f1; 686 | color: #333; 687 | font-weight: normal; 688 | padding: 0.5em 0.75em; 689 | text-decoration: none; 690 | } 691 | .home .sticky .page-link a { 692 | background: #d9e8f7; 693 | } 694 | .page-link a:active, 695 | .page-link a:hover { 696 | color: #ff4b33; 697 | } 698 | body.page .edit-link { 699 | clear: both; 700 | display: block; 701 | } 702 | #entry-author-info { 703 | background: #f2f7fc; 704 | border-top: 4px solid #000; 705 | clear: both; 706 | font-size: 14px; 707 | line-height: 20px; 708 | margin: 24px 0; 709 | overflow: hidden; 710 | padding: 18px 20px; 711 | } 712 | #entry-author-info #author-avatar { 713 | background: #fff; 714 | border: 1px solid #e7e7e7; 715 | float: left; 716 | height: 60px; 717 | margin: 0 -104px 0 0; 718 | padding: 11px; 719 | } 720 | #entry-author-info #author-description { 721 | float: left; 722 | margin: 0 0 0 104px; 723 | } 724 | #entry-author-info h2 { 725 | color: #000; 726 | font-size: 100%; 727 | font-weight: bold; 728 | margin-bottom: 0; 729 | } 730 | .entry-utility { 731 | clear: both; 732 | color: #777; 733 | font-size: 12px; 734 | line-height: 18px; 735 | } 736 | .entry-meta a, 737 | .entry-utility a { 738 | color: #777; 739 | } 740 | .entry-meta a:hover, 741 | .entry-utility a:hover { 742 | color: #ff4b33; 743 | } 744 | #content .video-player { 745 | padding: 0; 746 | } 747 | 748 | 749 | /* =Asides 750 | -------------------------------------------------------------- */ 751 | 752 | .home #content .format-aside p, 753 | .home #content .category-asides p { 754 | font-size: 14px; 755 | line-height: 20px; 756 | margin-bottom: 10px; 757 | margin-top: 0; 758 | } 759 | .home .hentry.format-aside, 760 | .home .hentry.category-asides { 761 | padding: 0; 762 | } 763 | .home #content .format-aside .entry-content, 764 | .home #content .category-asides .entry-content { 765 | padding-top: 0; 766 | } 767 | 768 | 769 | /* =Gallery listing 770 | -------------------------------------------------------------- */ 771 | 772 | .format-gallery .size-thumbnail img, 773 | .category-gallery .size-thumbnail img { 774 | border: 10px solid #f1f1f1; 775 | margin-bottom: 0; 776 | } 777 | .format-gallery .gallery-thumb, 778 | .category-gallery .gallery-thumb { 779 | float: left; 780 | margin-right: 20px; 781 | margin-top: -4px; 782 | } 783 | .home #content .format-gallery .entry-utility, 784 | .home #content .category-gallery .entry-utility { 785 | padding-top: 4px; 786 | } 787 | 788 | 789 | /* =Attachment pages 790 | -------------------------------------------------------------- */ 791 | 792 | .attachment .entry-content .entry-caption { 793 | font-size: 140%; 794 | margin-top: 24px; 795 | } 796 | .attachment .entry-content .nav-previous a:before { 797 | content: '\2190\00a0'; 798 | } 799 | .attachment .entry-content .nav-next a:after { 800 | content: '\00a0\2192'; 801 | } 802 | 803 | 804 | /* =Images 805 | -------------------------------------------------------------- */ 806 | 807 | /* 808 | Resize images to fit the main content area. 809 | - Applies only to images uploaded via WordPress by targeting size-* classes. 810 | - Other images will be left alone. Use "size-auto" class to apply to other images. 811 | */ 812 | img.size-auto, 813 | img.size-full, 814 | img.size-large, 815 | img.size-medium, 816 | .attachment img, 817 | .widget-container img { 818 | max-width: 100%; /* When images are too wide for containing element, force them to fit. */ 819 | height: auto; /* Override height to match resized width for correct aspect ratio. */ 820 | } 821 | .alignleft, 822 | img.alignleft { 823 | display: inline; 824 | float: left; 825 | margin-right: 24px; 826 | margin-top: 4px; 827 | } 828 | .alignright, 829 | img.alignright { 830 | display: inline; 831 | float: right; 832 | margin-left: 24px; 833 | margin-top: 4px; 834 | } 835 | .aligncenter, 836 | img.aligncenter { 837 | clear: both; 838 | display: block; 839 | margin-left: auto; 840 | margin-right: auto; 841 | } 842 | img.alignleft, 843 | img.alignright, 844 | img.aligncenter { 845 | margin-bottom: 12px; 846 | } 847 | .wp-caption { 848 | background: #f1f1f1; 849 | line-height: 18px; 850 | margin-bottom: 20px; 851 | max-width: 632px !important; /* prevent too-wide images from breaking layout */ 852 | padding: 4px; 853 | text-align: center; 854 | } 855 | .wp-caption img { 856 | margin: 5px 5px 0; 857 | max-width: 622px; /* caption width - 10px */ 858 | } 859 | .wp-caption p.wp-caption-text { 860 | color: #777; 861 | font-size: 12px; 862 | margin: 5px; 863 | } 864 | .wp-smiley { 865 | margin: 0; 866 | } 867 | .gallery { 868 | margin: 0 auto 18px; 869 | } 870 | .gallery .gallery-item { 871 | float: left; 872 | margin-top: 0; 873 | text-align: center; 874 | width: 33%; 875 | } 876 | .gallery-columns-2 .gallery-item { 877 | width: 50%; 878 | } 879 | .gallery-columns-4 .gallery-item { 880 | width: 25%; 881 | } 882 | .gallery img { 883 | border: 2px solid #cfcfcf; 884 | } 885 | .gallery-columns-2 .attachment-medium { 886 | max-width: 92%; 887 | height: auto; 888 | } 889 | .gallery-columns-4 .attachment-thumbnail { 890 | max-width: 84%; 891 | height: auto; 892 | } 893 | .gallery .gallery-caption { 894 | color: #777; 895 | font-size: 12px; 896 | margin: 0 0 12px; 897 | } 898 | .gallery dl { 899 | margin: 0; 900 | } 901 | .gallery img { 902 | border: 10px solid #f1f1f1; 903 | } 904 | .gallery br+br { 905 | display: none; 906 | } 907 | #content .attachment img {/* single attachment images should be centered */ 908 | display: block; 909 | margin: 0 auto; 910 | } 911 | 912 | 913 | /* =Navigation 914 | -------------------------------------------------------------- */ 915 | 916 | .navigation { 917 | color: #777; 918 | font-size: 12px; 919 | line-height: 18px; 920 | overflow: hidden; 921 | } 922 | .navigation a:link, 923 | .navigation a:visited { 924 | color: #777; 925 | text-decoration: none; 926 | } 927 | .navigation a:active, 928 | .navigation a:hover { 929 | color: #ff4b33; 930 | } 931 | .nav-previous { 932 | float: left; 933 | width: 50%; 934 | } 935 | .nav-next { 936 | float: right; 937 | text-align: right; 938 | width: 50%; 939 | } 940 | #nav-above { 941 | margin: 0 0 18px 0; 942 | } 943 | #nav-above { 944 | display: none; 945 | } 946 | .paged #nav-above, 947 | .single #nav-above { 948 | display: block; 949 | } 950 | #nav-below { 951 | margin: -18px 0 0 0; 952 | } 953 | 954 | 955 | /* =Comments 956 | -------------------------------------------------------------- */ 957 | #comments { 958 | clear: both; 959 | } 960 | #comments .navigation { 961 | padding: 0 0 18px 0; 962 | } 963 | h3#comments-title, 964 | h3#reply-title { 965 | color: #000; 966 | font-size: 20px; 967 | font-weight: bold; 968 | margin-bottom: 0; 969 | } 970 | h3#comments-title { 971 | padding: 24px 0; 972 | } 973 | .commentlist { 974 | list-style: none; 975 | margin: 0; 976 | } 977 | .commentlist li.comment { 978 | border-bottom: 1px solid #e7e7e7; 979 | line-height: 24px; 980 | margin: 0 0 24px 0; 981 | padding: 0 0 0 56px; 982 | position: relative; 983 | } 984 | .commentlist li:last-child { 985 | border-bottom: none; 986 | margin-bottom: 0; 987 | } 988 | #comments .comment-body ul, 989 | #comments .comment-body ol { 990 | margin-bottom: 18px; 991 | } 992 | #comments .comment-body p:last-child { 993 | margin-bottom: 6px; 994 | } 995 | #comments .comment-body blockquote p:last-child { 996 | margin-bottom: 24px; 997 | } 998 | .commentlist ol { 999 | list-style: decimal; 1000 | } 1001 | .commentlist .avatar { 1002 | position: absolute; 1003 | top: 4px; 1004 | left: 0; 1005 | } 1006 | .comment-author { 1007 | } 1008 | .comment-author cite { 1009 | color: #000; 1010 | font-style: normal; 1011 | font-weight: bold; 1012 | } 1013 | .comment-author .says { 1014 | font-style: italic; 1015 | } 1016 | .comment-meta { 1017 | font-size: 12px; 1018 | margin: 0 0 18px 0; 1019 | } 1020 | .comment-meta a:link, 1021 | .comment-meta a:visited { 1022 | color: #777; 1023 | text-decoration: none; 1024 | } 1025 | .comment-meta a:active, 1026 | .comment-meta a:hover { 1027 | color: #ff4b33; 1028 | } 1029 | .commentlist .even { 1030 | } 1031 | .commentlist .bypostauthor { 1032 | } 1033 | .reply { 1034 | font-size: 12px; 1035 | padding: 0 0 24px 0; 1036 | } 1037 | .reply a, 1038 | a.comment-edit-link { 1039 | color: #777; 1040 | } 1041 | .reply a:hover, 1042 | a.comment-edit-link:hover { 1043 | color: #ff4b33; 1044 | } 1045 | .commentlist .children { 1046 | list-style: none; 1047 | margin: 0; 1048 | } 1049 | .commentlist .children li { 1050 | border: none; 1051 | margin: 0; 1052 | } 1053 | .nopassword, 1054 | .nocomments { 1055 | display: none; 1056 | } 1057 | #comments .pingback { 1058 | border-bottom: 1px solid #e7e7e7; 1059 | margin-bottom: 18px; 1060 | padding-bottom: 18px; 1061 | } 1062 | .commentlist li.comment+li.pingback { 1063 | margin-top: -6px; 1064 | } 1065 | #comments .pingback p { 1066 | color: #777; 1067 | display: block; 1068 | font-size: 12px; 1069 | line-height: 18px; 1070 | margin: 0; 1071 | } 1072 | #comments .pingback .url { 1073 | font-size: 13px; 1074 | font-style: italic; 1075 | } 1076 | 1077 | /* Comments form */ 1078 | input[type="submit"] { 1079 | color: #333; 1080 | } 1081 | #respond { 1082 | border-top: 1px solid #e7e7e7; 1083 | margin: 24px 0; 1084 | overflow: hidden; 1085 | position: relative; 1086 | } 1087 | #respond p { 1088 | margin: 0; 1089 | } 1090 | #respond .comment-notes { 1091 | margin-bottom: 1em; 1092 | } 1093 | .form-allowed-tags { 1094 | line-height: 1em; 1095 | } 1096 | .children #respond { 1097 | margin: 0 48px 0 0; 1098 | } 1099 | h3#reply-title { 1100 | margin: 18px 0; 1101 | } 1102 | #comments-list #respond { 1103 | margin: 0 0 18px 0; 1104 | } 1105 | #comments-list ul #respond { 1106 | margin: 0; 1107 | } 1108 | #cancel-comment-reply-link { 1109 | font-size: 12px; 1110 | font-weight: normal; 1111 | line-height: 18px; 1112 | } 1113 | #respond .required { 1114 | color: #ff4b33; 1115 | font-weight: bold; 1116 | } 1117 | #respond label { 1118 | color: #777; 1119 | font-size: 12px; 1120 | } 1121 | #respond input { 1122 | margin: 0 0 9px; 1123 | width: 98%; 1124 | } 1125 | #respond textarea { 1126 | width: 98%; 1127 | } 1128 | #respond .form-allowed-tags { 1129 | color: #777; 1130 | font-size: 12px; 1131 | line-height: 18px; 1132 | } 1133 | #respond .form-allowed-tags code { 1134 | font-size: 11px; 1135 | } 1136 | #respond .form-submit { 1137 | margin: 12px 0; 1138 | } 1139 | #respond .form-submit input { 1140 | font-size: 14px; 1141 | width: auto; 1142 | } 1143 | 1144 | 1145 | /* =Widget Areas 1146 | -------------------------------------------------------------- */ 1147 | 1148 | .widget-area ul { 1149 | list-style: none; 1150 | margin-left: 0; 1151 | } 1152 | .widget-area ul ul { 1153 | list-style: square; 1154 | margin-left: 1.3em; 1155 | } 1156 | .widget-area select { 1157 | max-width: 100%; 1158 | } 1159 | .widget_search #s {/* This keeps the search inputs in line */ 1160 | width: 60%; 1161 | } 1162 | .widget_search label { 1163 | display: none; 1164 | } 1165 | .widget-container { 1166 | word-wrap: break-word; 1167 | -webkit-hyphens: auto; 1168 | -moz-hyphens: auto; 1169 | hyphens: auto; 1170 | margin: 0 0 18px 0; 1171 | } 1172 | .widget-container .wp-caption img { 1173 | margin: auto; 1174 | } 1175 | .widget-title { 1176 | color: #222; 1177 | font-weight: bold; 1178 | } 1179 | .widget-area a:link, 1180 | .widget-area a:visited { 1181 | text-decoration: none; 1182 | } 1183 | .widget-area a:active, 1184 | .widget-area a:hover { 1185 | text-decoration: underline; 1186 | } 1187 | .widget-area .entry-meta { 1188 | font-size: 11px; 1189 | } 1190 | #wp_tag_cloud div { 1191 | line-height: 1.6em; 1192 | } 1193 | #wp-calendar { 1194 | width: 100%; 1195 | } 1196 | #wp-calendar caption { 1197 | color: #222; 1198 | font-size: 14px; 1199 | font-weight: bold; 1200 | padding-bottom: 4px; 1201 | text-align: left; 1202 | } 1203 | #wp-calendar thead { 1204 | font-size: 11px; 1205 | } 1206 | #wp-calendar thead th { 1207 | } 1208 | #wp-calendar tbody { 1209 | color: #aaa; 1210 | } 1211 | #wp-calendar tbody td { 1212 | background: #f5f5f5; 1213 | border: 1px solid #fff; 1214 | padding: 3px 0 2px; 1215 | text-align: center; 1216 | } 1217 | #wp-calendar tbody .pad { 1218 | background: none; 1219 | } 1220 | #wp-calendar tfoot #next { 1221 | text-align: right; 1222 | } 1223 | .widget_rss a.rsswidget { 1224 | color: #000; 1225 | } 1226 | .widget_rss a.rsswidget:hover { 1227 | color: #ff4b33; 1228 | } 1229 | .widget_rss .widget-title img { 1230 | width: 11px; 1231 | height: 11px; 1232 | } 1233 | 1234 | /* Main sidebars */ 1235 | #main .widget-area ul { 1236 | margin-left: 0; 1237 | padding: 0 20px 0 0; 1238 | } 1239 | #main .widget-area ul ul { 1240 | border: none; 1241 | margin-left: 1.3em; 1242 | padding: 0; 1243 | } 1244 | #primary { 1245 | } 1246 | #secondary { 1247 | } 1248 | 1249 | /* Footer widget areas */ 1250 | #footer-widget-area { 1251 | } 1252 | 1253 | 1254 | /* =Footer 1255 | -------------------------------------------------------------- */ 1256 | 1257 | #footer { 1258 | margin-bottom: 20px; 1259 | } 1260 | #colophon { 1261 | border-top: 4px solid #000; 1262 | margin-top: -4px; 1263 | overflow: hidden; 1264 | padding: 18px 0; 1265 | } 1266 | #site-info { 1267 | font-weight: bold; 1268 | } 1269 | #site-info a { 1270 | color: #000; 1271 | text-decoration: none; 1272 | } 1273 | #site-generator { 1274 | font-style: italic; 1275 | position: relative; 1276 | } 1277 | #site-generator a { 1278 | background: url(images/wordpress.png) center left no-repeat; 1279 | color: #666; 1280 | display: inline-block; 1281 | line-height: 16px; 1282 | padding-left: 20px; 1283 | text-decoration: none; 1284 | } 1285 | #site-generator a:hover { 1286 | text-decoration: underline; 1287 | } 1288 | img#wpstats { 1289 | display: block; 1290 | margin: 0 auto 10px; 1291 | } 1292 | 1293 | 1294 | /* =Mobile Safari ( iPad, iPhone and iPod Touch ) 1295 | -------------------------------------------------------------- */ 1296 | 1297 | pre { 1298 | -webkit-text-size-adjust: 140%; 1299 | } 1300 | code { 1301 | -webkit-text-size-adjust: 160%; 1302 | } 1303 | #access, 1304 | .entry-meta, 1305 | .entry-utility, 1306 | .navigation, 1307 | .widget-area { 1308 | -webkit-text-size-adjust: 120%; 1309 | } 1310 | #site-description { 1311 | -webkit-text-size-adjust: none; 1312 | } 1313 | 1314 | 1315 | /* =Print Style 1316 | -------------------------------------------------------------- */ 1317 | 1318 | @media print { 1319 | body { 1320 | background: none !important; 1321 | } 1322 | #wrapper { 1323 | clear: both !important; 1324 | display: block !important; 1325 | float: none !important; 1326 | position: relative !important; 1327 | } 1328 | #header { 1329 | border-bottom: 2pt solid #000; 1330 | padding-bottom: 18pt; 1331 | } 1332 | #colophon { 1333 | border-top: 2pt solid #000; 1334 | } 1335 | #site-title, 1336 | #site-description { 1337 | float: none; 1338 | line-height: 1.4em; 1339 | margin: 0; 1340 | padding: 0; 1341 | } 1342 | #site-title { 1343 | font-size: 13pt; 1344 | } 1345 | .entry-content { 1346 | font-size: 14pt; 1347 | line-height: 1.6em; 1348 | } 1349 | .entry-title { 1350 | font-size: 21pt; 1351 | } 1352 | #access, 1353 | #branding img, 1354 | #respond, 1355 | .comment-edit-link, 1356 | .edit-link, 1357 | .navigation, 1358 | .page-link, 1359 | .widget-area { 1360 | display: none !important; 1361 | } 1362 | #container, 1363 | #header, 1364 | #footer { 1365 | margin: 0; 1366 | width: 100%; 1367 | } 1368 | #content, 1369 | .one-column #content { 1370 | margin: 24pt 0 0; 1371 | width: 100%; 1372 | } 1373 | .wp-caption p { 1374 | font-size: 11pt; 1375 | } 1376 | #site-info, 1377 | #site-generator { 1378 | float: none; 1379 | width: auto; 1380 | } 1381 | #colophon { 1382 | width: auto; 1383 | } 1384 | img#wpstats { 1385 | display: none; 1386 | } 1387 | #site-generator a { 1388 | margin: 0; 1389 | padding: 0; 1390 | } 1391 | #entry-author-info { 1392 | border: 1px solid #e7e7e7; 1393 | } 1394 | #main { 1395 | display: inline; 1396 | } 1397 | .home .sticky { 1398 | border: none; 1399 | } 1400 | } 1401 | 1402 | /* Left will inherit from right (so we don't need to duplicate code) */ 1403 | .github-fork-ribbon { 1404 | /* The right and left classes determine the side we attach our banner to */ 1405 | position: absolute; 1406 | 1407 | /* Add a bit of padding to give some substance outside the "stitching" */ 1408 | padding: 2px 0; 1409 | 1410 | /* Set the base colour */ 1411 | background-color: #a00; 1412 | 1413 | /* Set a gradient: transparent black at the top to almost-transparent black at the bottom */ 1414 | background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 0)), to(rgba(0, 0, 0, 0.15))); 1415 | background-image: -webkit-linear-gradient(top, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.15)); 1416 | background-image: -moz-linear-gradient(top, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.15)); 1417 | background-image: -ms-linear-gradient(top, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.15)); 1418 | background-image: -o-linear-gradient(top, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.15)); 1419 | background-image: linear-gradient(top, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.15)); 1420 | 1421 | /* Add a drop shadow */ 1422 | -webkit-box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.5); 1423 | -moz-box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.5); 1424 | box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.5); 1425 | 1426 | z-index: 9999; 1427 | pointer-events: auto; 1428 | } 1429 | 1430 | .github-fork-ribbon a, 1431 | .github-fork-ribbon a:hover { 1432 | /* Set the font */ 1433 | font: 700 13px "Helvetica Neue", Helvetica, Arial, sans-serif; 1434 | color: #fff; 1435 | 1436 | /* Set the text properties */ 1437 | text-decoration: none; 1438 | text-shadow: 0 -1px rgba(0, 0, 0, 0.5); 1439 | text-align: center; 1440 | 1441 | /* Set the geometry. If you fiddle with these you'll also need 1442 | to tweak the top and right values in .github-fork-ribbon. */ 1443 | width: 200px; 1444 | line-height: 20px; 1445 | 1446 | /* Set the layout properties */ 1447 | display: inline-block; 1448 | padding: 2px 0; 1449 | 1450 | /* Add "stitching" effect */ 1451 | border-width: 1px 0; 1452 | border-style: dotted; 1453 | border-color: #fff; 1454 | border-color: rgba(255, 255, 255, 0.7); 1455 | } 1456 | 1457 | .github-fork-ribbon-wrapper { 1458 | width: 150px; 1459 | height: 150px; 1460 | position: absolute; 1461 | overflow: hidden; 1462 | top: 0; 1463 | z-index: 9999; 1464 | pointer-events: none; 1465 | } 1466 | 1467 | .github-fork-ribbon-wrapper.fixed { 1468 | position: fixed; 1469 | } 1470 | 1471 | .github-fork-ribbon-wrapper.left { 1472 | left: 0; 1473 | } 1474 | 1475 | .github-fork-ribbon-wrapper.right { 1476 | right: 0; 1477 | } 1478 | 1479 | .github-fork-ribbon-wrapper.left-bottom { 1480 | position: fixed; 1481 | top: inherit; 1482 | bottom: 0; 1483 | left: 0; 1484 | } 1485 | 1486 | .github-fork-ribbon-wrapper.right-bottom { 1487 | position: fixed; 1488 | top: inherit; 1489 | bottom: 0; 1490 | right: 0; 1491 | } 1492 | 1493 | .github-fork-ribbon-wrapper.right .github-fork-ribbon { 1494 | top: 42px; 1495 | right: -43px; 1496 | 1497 | -webkit-transform: rotate(45deg); 1498 | -moz-transform: rotate(45deg); 1499 | -ms-transform: rotate(45deg); 1500 | -o-transform: rotate(45deg); 1501 | transform: rotate(45deg); 1502 | } 1503 | 1504 | .github-fork-ribbon-wrapper.left .github-fork-ribbon { 1505 | top: 42px; 1506 | left: -43px; 1507 | 1508 | -webkit-transform: rotate(-45deg); 1509 | -moz-transform: rotate(-45deg); 1510 | -ms-transform: rotate(-45deg); 1511 | -o-transform: rotate(-45deg); 1512 | transform: rotate(-45deg); 1513 | } 1514 | 1515 | 1516 | .github-fork-ribbon-wrapper.left-bottom .github-fork-ribbon { 1517 | top: 80px; 1518 | left: -43px; 1519 | 1520 | -webkit-transform: rotate(45deg); 1521 | -moz-transform: rotate(45deg); 1522 | -ms-transform: rotate(45deg); 1523 | -o-transform: rotate(45deg); 1524 | transform: rotate(45deg); 1525 | } 1526 | 1527 | .github-fork-ribbon-wrapper.right-bottom .github-fork-ribbon { 1528 | top: 80px; 1529 | right: -43px; 1530 | 1531 | -webkit-transform: rotate(-45deg); 1532 | -moz-transform: rotate(-45deg); 1533 | -ms-transform: rotate(-45deg); 1534 | -o-transform: rotate(-45deg); 1535 | transform: rotate(-45deg); 1536 | } 1537 | 1538 | .figure img{ 1539 | width: 700px; 1540 | } -------------------------------------------------------------------------------- /aikau/tutorial/tutorial.md: -------------------------------------------------------------------------------- 1 | % Working with the new Aikau framework in Alfresco Share 4.2 2 | % Ole Hejlskov 3 | % May 2014 4 | 5 | # License 6 | 7 | This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/. 8 | ![License](images/creativecommons-cc-by-sa.png) 9 | 10 | 11 | # Introduction 12 | 13 | Alfresco Share is a great front end for the Alfresco Repository. Out of the box it comes with full collaboration features, content management, and pretty much what you would expect for a full blown Enterprise Content Management System. 14 | 15 | Different users have different needs. Sometimes you'll want to add a few buttons and create a theme, but from time to time developers need to make significant user interface customizations to meet business requirements. Up until recent releases this would prove to be a challenge, and many developers chose to go down a different path and write a UI from scratch using a framework and language of their choosing. The new page and widget framework (Aikau) in Share provides a great way to create a complete custom interface, but keeps the code - and the users, within Share. 16 | 17 | This tutorial assumes knowledge of the Alfresco Maven SDK, or an existing development setup. All examples in this tutorial are available with full source which can be built with Maven. 18 | 19 | For a full tutorial on how to work with the Maven SDK, please see [Jeff Potts' excellent tutorial on the subject](http://ecmarchitect.com/alfresco-developer-series-tutorials/maven-sdk/tutorial/tutorial.html). 20 | 21 | ## Credits 22 | 23 | A few people have helped getting this article done: 24 | 25 | * [Jeff Potts](http://ecmarchitect.com), for his encouragement and seal of approval 26 | * Lanre Abiwon (DarkStar1), for going through the early drafts and contributions 27 | * [Magenta Aps](http://magenta.dk), for allowing me to spend a bit of time, at work, on this article 28 | 29 | And of course all of the members of the Alfresco Community! 30 | 31 | 32 | 33 | # Setup 34 | 35 | To make sure everyone can understand and relate to the code in this tutorial, here is a description of the tools used for building these examples, and the file structure within the examples. 36 | 37 | ## Tools 38 | 39 | While writing this tutorial the following tools have been used: 40 | 41 | - Mac OS X 10.9.1 42 | - Maven 3.1.1 (Installed with [Homebrew](http://brew.sh/)) 43 | - Java 1.7.0_51 44 | - Alfresco Maven SDK 1.1.1 45 | - Alfresco 4.2.f 46 | 47 | ## Getting the code 48 | 49 | All examples in this tutorial are available on [GitHub](https://github.com/ohej/alfresco-tutorials). 50 | 51 | ## File structure 52 | 53 | The Maven SDK will provide us with the initial directory structure, which contains some sample data. For all examples in this tutorial the sample data has been removed, and the `src/main/amp/config/alfresco/web-extension` folder has been added, as well as the `src/main/amp/file-mapping.properties` file. 54 | 55 | Here are some notes on what goes where: 56 | 57 | File | Description 58 | ---- | ----------- 59 | `src/main/amp/config/alfresco/web-extension` | share-config-custom.xml and other customization XML-files 60 | `src/main/amp/config/alfresco/web-extension/site-webscripts`| Web scripts 61 | `src/main/amp/web` | Static assets, Javascript, CSS, Images etc 62 | `src/main/java` | Java classes 63 | `src/test` | Test resources, Unit tests etc 64 | 65 | ## Running the examples 66 | 67 | Clone the code from [GitHub](https://github.com/ohej/alfresco-tutorials), make sure you have an Alfresco instance running on port 8080, enter the `aikau/aikau-tutorial-share/` folder and run the following command: 68 | 69 | mvn integration-test -Pamp-to-war 70 | 71 | This will build an [AMP file](https://wiki.alfresco.com/wiki/AMP_Files), then apply the amp to the Share war-file, and startup an embedded Tomcat instance. Normally, Tomcat will run on the default port 8080, but since Alfresco is already running on that port, we need to change this. This can be done either by giving the port number as an argument to Maven, or defined in the `pom.xml` as the example in this tutorial. 72 | 73 | The Tomcat port number is defined in `pom.xml` with the following XML: 74 | 75 | 8081 76 | 77 | With this in place, Tomcat will run on port 8081. 78 | 79 | Once Tomcat has started Share is accessible on [http://localhost:8081/share](http://localhost:8081/share). 80 | 81 | To shut down it down, simply hit CTRL+C. 82 | 83 | # Part One: Creating a page 84 | 85 | Share is a great frontend for Alfresco, but sometimes it doesn't meet a specific business requirement. A common use case is the need for a completely separate page to display custom metadata on a set of nodes, where the built in search or repository browser isn't enough. Many people end up writing separate applications outside Share to meet these requirements, because there is a lot of work involved in making a custom page in Share. 86 | 87 | If you have worked with Share in the past, you may be familiar with the Surf Framework. Defining a page using Surf requires template instances, regions, and components. Using the new widget framework, a single web script defines a page. This makes it much easier to develop custom pages that meets all business requirements, while still keeping the users inside the Share application. 88 | 89 | This chapter will focus on how to create a custom page in Share with a single web script and go through the JSON model that is used to setup the widgets on the page. 90 | 91 | ## Create the page 92 | 93 | Let's create a page named "Simple page", with a "Hello World" button. Create a new web script "simple-page", like this: 94 | 95 | `src/main/amp/config/alfresco/web-extension/site-webscripts/com/example/pages/simple-page.get.desc.xml` 96 | 97 | 98 | Simple Page 99 | Simple page definition 100 | Share 101 | /simple-page 102 | 103 | 104 | `src/main/amp/config/alfresco/web-extension/site-webscripts/com/example/pages/simple-page.get.html.ftl` 105 | 106 | <@processJsonModel group="share"/> 107 | 108 | `src/main/amp/config/alfresco/web-extension/site-webscripts/com/example/pages/simple-page.get.js` 109 | 110 | model.jsonModel = { 111 | widgets: [{ 112 | name: "alfresco/buttons/AlfButton", 113 | config: { 114 | label: "Hello world!" 115 | } 116 | }] 117 | }; 118 | 119 | 120 | Start up Share via Maven with this command: 121 | 122 | mvn integration-test -Pamp-to-war 123 | 124 | Navigate to `http://localhost:8081/share/page/dp/ws/simple-page` to see the result, which looks like this: 125 | 126 | ![Simple page with a widget](images/part-one-simple-page-1.png) 127 | 128 | ### A closer look 129 | 130 | The example above is the basic setup of a page with the new page model. Let's review what we just did. 131 | 132 | The web script defines a JSON model for a page. The JSON model consists of widgets, and their configuration. In this example we defined a single button-widget with a simple configuration which defines the label of the button. 133 | 134 | Widgets can be nested (if the widget supports it). This could be used to define the overall layout of the page, or define a simple widget to split widgets up into columns. Share provides a number of layout widgets, for example, sidebar container, left-and-right, tabs, vertical/horizontal etc. Since there is yet to be proper documentation on this, all widgets can be found in [the source for Share](https://svn.alfresco.com/repos/alfresco-open-mirror/alfresco/COMMUNITYTAGS/V4.2f/root/projects/slingshot/source/web/js/alfresco/). 135 | 136 | The freemarker template is really simple. It calls the `@processJsonModel` macro, which takes care of rendering everything. 137 | 138 | ## Where is the Share header and footer? 139 | 140 | After seeing the first example, the obvious question is how to add the Share header and footer. Luckily, this is not something you need to define manually. 141 | 142 | Share enables you to render pages in different contexts, which means the URL will determine how the page is rendered. Notice the URL `http://localhost:8081/share/page/dp/ws/simple-page`. 143 | Notice `/dp/ws/simple-page`. 144 | 145 | The first part, "dp", defines that we want a dynamic page. This will render the page in a stand-alone manner, useful for embedding the page into other applications, or creating a complete custom interface for specific needs but, still in the Alfresco/Share ecosystem. 146 | 147 | The second part, "ws", means that we want to load the page from a web script. It's possible to remotely load the pages from the repository. That way we can create pages and store them within Alfresco and have them rendered. 148 | 149 | To add the Share header and footer simply switch the "dp" with "hdp": `http://localhost:8081/share/page/hdp/ws/simple-page` 150 | 151 | The result looks like this: 152 | 153 | ![Simple page with a widget rendered with header and footer](images/part-one-simple-page-2.png) 154 | 155 | 156 | ### Adding a title next to the logo 157 | 158 | The Share header widget is currently (as of 4.2.f) the only component in Share using the new Aikau framework. The header includes the menu bar, search, logo, and title. If we wish to add a title next to the logo, we'll need to define a title widget, which Share will take into account when rendering it with the header. If the page is rendered without the header, it simply ignores the widget. 159 | 160 | To add the title to our simple page, modify the JSON model and add the title: 161 | 162 | model.jsonModel = { 163 | widgets: [{ 164 | id: "SET_PAGE_TITLE", 165 | name: "alfresco/header/SetTitle", 166 | config: { 167 | title: "This is a simple page" 168 | } 169 | }, 170 | { 171 | name: "alfresco/buttons/AlfButton", 172 | config: { 173 | label: "Hello world!" 174 | } 175 | }] 176 | }; 177 | 178 | After restarting Share, the result looks like this: 179 | 180 | ![Simple page with a widget and title](images/part-one-simple-page-3.png) 181 | 182 | ### Adding a layout 183 | 184 | To add a simple horizontal layout we can use the `alfresco/layout/HorizontalWidgets` widget, which makes it possible to horizontally align widgets and define their width in percentages. 185 | 186 | The JSON model in `src/main/amp/config/alfresco/web-extension/site-webscripts/com/example/pages/simple-page.get.js` should look like this: 187 | 188 | 189 | model.jsonModel = { 190 | widgets: [{ 191 | id: "SET_PAGE_TITLE", 192 | name: "alfresco/header/SetTitle", 193 | config: { 194 | title: "This is a simple page" 195 | } 196 | }, 197 | { 198 | id: "MY_HORIZONTAL_WIDGET_LAYOUT", 199 | name: "alfresco/layout/HorizontalWidgets", 200 | config: { 201 | widgetWidth: 50, 202 | widgets: [ 203 | { 204 | name: "alfresco/logo/Logo", 205 | config: { 206 | logoClasses: "alfresco-logo-only" 207 | } 208 | }, 209 | { 210 | name: "alfresco/buttons/AlfButton", 211 | config: { 212 | label: "Hello world" 213 | } 214 | } 215 | ] 216 | } 217 | }] 218 | }; 219 | 220 | 221 | The result looks like this: 222 | 223 | ![Simple page two widgets in a horizontal layout](images/part-one-simple-page-4.png) 224 | 225 | Notice how the widgets are nested inside each other. The `HorizontalWidgets` has a config object with a list of widgets, each with their own properties. The configuration includes a parameter for the horizontal widget `widgetWidth: 50` which defines the percentage width each widget should recieve. 226 | 227 | ## The built-in widgets 228 | 229 | As mentioned above, the only component in Share (as of 4.2.f) using the new Aikau framework is the header. However, this does not mean that we're limited to logo, buttons, and drop downs. 230 | 231 | The good folks at Alfresco are working hard on converting the Document Library to use the Aikau framework. This means that there is a lot of widgets in the library that can be used right now. 232 | 233 | Take a look at the [Share source](https://svn.alfresco.com/repos/alfresco-open-mirror/alfresco/COMMUNITYTAGS/V4.2e/root/projects/slingshot/source/web/js/alfresco/) to see all the existing widgets. 234 | 235 | There are widgets for loading up nodes, rendering properties, buttons, menus, forms, dialogs, selectors for users, nodes, categories, and much, much more. 236 | 237 | Not all widgets are production ready, but they are excellent examples to look at, and to get inspiration from. Use them as a reference on good practises on writing good, and generic widgets. 238 | 239 | # Part Two: Introduction to widgets 240 | 241 | Using the existing widgets in Share provides a lot of features, but when developing a custom page, custom widgets are usually needed. To fully understand, use and extend the existing widgets we need to look at how widgets are created. 242 | 243 | This chapter will focus on how to write widgets, and will provide examples on how to utilize templates, i18n, css, and Ajax. 244 | 245 | ## YUI, Dojo and AMD 246 | 247 | Up until now, Share has been built on top of the YUI2 framework, and almost all of the current features in Share still use YUI2. 248 | 249 | 250 | 251 | Share will eventually move away from YUI2 as it is not being developed anymore, and since there is no clear upgrade path from YUI2 to YUI3, Alfresco had to make a decision and chose to go with [Dojo's AMD Module system](http://dojotoolkit.org/documentation/tutorials/1.9/modules/). 252 | 253 | The Aikau framework relies on [Dojo's AMD Module system](http://dojotoolkit.org/documentation/tutorials/1.9/modules/) to provide features for loading and extending widgets. 254 | 255 | This does not mean that Alfresco is switching away from YUI and forcing users into Dojo. 256 | 257 | Most of Share's future core modules will be based on Dojo, but this does not mean developers needs to learn and use Dojo for everything. However a bit of AMD knowledge will be needed, which is what defines widget and their dependencies. That's it. Once AMD is in place, it's up to the developers to load up another framework or use vanilla Javascript. 258 | 259 | ## Creating a simple widget 260 | 261 | Let's create a really simple widget on a new page. The widget will not do much, other than do an alert with "Hello world". Let's start out with the code, see that it works and then go into the details. 262 | 263 | First we need to create the widget. Create this file: 264 | `src/main/amp/web/js/example/widgets/MyWidget.js` 265 | 266 | define(["dojo/_base/declare", 267 | "dijit/_WidgetBase", 268 | "alfresco/core/Core" 269 | ], 270 | function(declare, _Widget, Core) { 271 | return declare([_Widget, Core], { 272 | 273 | postCreate: function example_widgets_MyWidget__postCreate() { 274 | this.inherited(arguments); 275 | alert("Hello world!"); 276 | 277 | } 278 | 279 | }); 280 | }); 281 | 282 | Once the widget is created, create a new web script to define the new page: 283 | 284 | `src/main/amp/config/alfresco/web-extension/site-webscripts/com/example/pages/custom-widget-page.get.desc.xml` 285 | 286 | 287 | Custom Widget Page 288 | Page with custom widget 289 | Share 290 | /custom-widget-page 291 | 292 | 293 | Add a Freemarker template to render the page: 294 | `src/main/amp/config/alfresco/web-extension/site-webscripts/com/example/pages/custom-widget-page.get.html.ftl` 295 | 296 | <@processJsonModel group="share"/> 297 | 298 | Add the JSON file: 299 | `src/main/amp/config/alfresco/web-extension/site-webscripts/com/example/pages/custom-widget-page.get.js` 300 | 301 | model.jsonModel = { 302 | widgets: [{ 303 | id: "SET_PAGE_TITLE", 304 | name: "alfresco/header/SetTitle", 305 | config: { 306 | title: "This is a simple page" 307 | } 308 | }, 309 | { 310 | name: "example/widgets/MyWidget" 311 | }] 312 | }; 313 | 314 | The last thing that needs to be done is to tell Share about the path for the widget, otherwise it will not know where to look for it. This is done by creating an extension module for Share. Create the module in this file: 315 | `src/main/amp/config/alfresco/web-extension/site-data/extensions/widget-extension.xml` 316 | 317 | 318 | 319 | 320 | Tutorial widgets 321 | 1.0 322 | true 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | Restart share and visit [http://localhost:8081/share/page/dp/ws/custom-widget-page](http://localhost:8081/share/page/dp/ws/custom-widget-page) to see the result. 339 | 340 | 341 | ## Breaking down the widget code 342 | 343 | A lot of code was involved in creating the widget. We had to create a new web script, an extension module, and the widget. Let's look at each part to understand what's going on. 344 | 345 | ### Loading dependencies 346 | 347 | If you have worked with Javascript in general, you'll notice a few things. We haven't included any Javascript files and there is no initialization of the widget object. This is because everything is handled by the Surf framework which powers Share. By defining our extension module, we told Share where to find the javascript files, and by defining our widget in the JSON model, it knows how to initialize it. 348 | 349 | When processing the JSON page model, Share will identify which widgets need to be included to render the page. It knows by the name attribute roughly where to look for it, but we didn't specify an absolute path or an extension to the file. 350 | 351 | Share registers a list of packages to include - a kind of namespace. This is done in `WEB-INF/surf.xml` with this sniplet of XML: 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | The packages has a name and a location. So by referencing `alfresco/[folder]/[widget]` in the JSON model, Share will look for the file in `WEB-INF/js/alfresco/[folder]/[widget].js`. 361 | 362 | However, it is always good practise to create a custom namespace to avoid conflicts in the code. To do this, we added a new package in the extension module, which tells Share that when referencing `example/[folder]/[widget]` it will include `WEB-INF/js/example/[folder]/[widget].js`. 363 | 364 | Now that Share knows how to locate the widget files, it will run it through it's internal dependency aggregation and caching. Our widget might depend on other widgets, and those widgets might depend on other widgets. This will create a complex matrix of dependencies to load, but Surf is smart enough to only include the dependencies we need - and it will only include them once. 365 | 366 | The `<@processJsonModel group="share"/>` macro in the Freemarker template triggers the process of resolving the dependencies and rendering everything from including the scripts to the initialization of the widget. 367 | 368 | ### Defining and declaring a widget 369 | 370 | This is not an Alfresco specific thing - it's purely Dojo and AMD. Since Surf already pre-processed and initialized our widget, Share will already have taken care of loading all the resources we need. One less thing to worry about! 371 | 372 | The first lines of any widget consists of a definition of dependencies: 373 | 374 | define(["dojo/_base/declare", 375 | "dijit/_WidgetBase", 376 | "alfresco/core/Core" 377 | ] 378 | 379 | This defines that we need to include the declare feature, WidgetBase and an Alfresco core widget. 380 | 381 | The next line returns a declaration of our widget: 382 | 383 | function(declare, _Widget, Core) { 384 | return declare([_Widget, Core], { 385 | }); 386 | } 387 | 388 | This might be a bit complex to read and understand, so let's spend some more time on it. 389 | 390 | We create a function, which will take "declare", "_Widget" and "Core" as arguments. The order of the arguments has to be the exact same as the definitions above, as they will be passed into this function. 391 | 392 | So, first we define our dependencies, then create a function which takes these dependencies in as arguments. 393 | 394 | Inside this function, we call [the declare function, which is an AMD specific function](http://dojotoolkit.org/documentation/tutorials/1.9/declare/). The declare function allows us to inherit and extend other AMD modules. In our case we extend the Dojo _WidgetBase and Alfresco's Core module. 395 | 396 | This will provide us with the basic framework, and after that we can start defining our widget object. 397 | 398 | So if we wanted to extend `alfresco/buttons/AlfButton`, we would simply define the module, and add it to the list of modules, it can be extended like this: 399 | 400 | define(["dojo/_base/declare", 401 | "dijit/_WidgetBase", 402 | "alfresco/core/Core", 403 | "alfresco/buttons/AlfButton" 404 | ], 405 | function(declare, _Widget, Core, AlfButton) { 406 | return declare([_Widget, Core, AlfButton], { 407 | /* Our code goes here */ 408 | }); 409 | } 410 | }); 411 | 412 | This way we can extend the standard button and modify it. 413 | 414 | Notice that the function definition and return statements are two different, separate things. The function arguments are the dependencies, we request with the define statement. The `return declare([])` defines which objects we want to extend. We do not always want to extend other objects, but only make them available in the object. One example of this could be if we wanted Dojo's array features. We would define them as a dependency, and make them available in the function, but we *do not* want to extend the object. This example shows how to include the array features: 415 | 416 | define(["dojo/_base/declare", 417 | "dijit/_WidgetBase", 418 | "alfresco/core/Core", 419 | "dojo/_base/array", 420 | ], 421 | function(declare, _Widget, Core, array) { 422 | return declare([_Widget, Core, array], { 423 | var list = [1,2,3]; 424 | array.forEach( list, function(x) { 425 | console.log(x); 426 | }); 427 | }); 428 | } 429 | }); 430 | 431 | Notice that `dojo/_base/array` was only defined and passed in as an argument to our widget. We did not extend the array object. 432 | 433 | ## Beyond a "Hello world" 434 | 435 | Now that we know a lot more about how a widget is written, let's look at some more examples. This example will feature the `Templated` module, which enables you to specify a HTML template for your widget, bind values to it, and access it from within the widget. We'll also have a look at the basic DOM-operations Dojo supplies. Last but not least, we'll look at the `CoreXhr` module for doing Ajax as well as utilizing i18n and CSS. 436 | 437 | ### Templates, CSS, and I18N 438 | 439 | Dojo provides a template system for widgets, called [Templated](http://dojotoolkit.org/documentation/tutorials/1.9/templated/). This module will allow us to create HTML templates and manipulate them. We'll be able to bind events and elements directly to our widget for future reference. This way we can create the basic structure directly in HTML and hook into it from our widget. 440 | 441 | To use the `Templated` module, the widget need to define the `dijit/_TemplatedMixin` module, and specify a path and name for the template, like this: `dojo/text!./templates/TemplateWidget.html`. The path is relative to the widget's location. The widget also needs to extend the `Templated` module with the declare feature discussed earlier. 442 | 443 | Let's look at an example. Create a new file in `src/main/amp/web/js/example/widgets/templates/TemplateWidget.html` with some HTML: 444 | 445 |
${greeting}
446 | 447 | Then a new widget in `src/main/amp/web/js/example/widgets/TemplateWidget.js` 448 | 449 | define(["dojo/_base/declare", 450 | "dijit/_WidgetBase", 451 | "alfresco/core/Core", 452 | "dijit/_TemplatedMixin", 453 | "dojo/text!./templates/TemplateWidget.html" 454 | ], 455 | function(declare, _Widget, Core, _Templated, template) { 456 | return declare([_Widget, Core, _Templated], { 457 | templateString: template, 458 | buildRendering: function example_widgets_TemplateWidget__buildRendering() { 459 | this.greeting = "Hello!"; 460 | this.inherited(arguments); 461 | } 462 | }); 463 | }); 464 | 465 | This widget will render the template, and replace the variable with the string "Hello". Notice that we're overriding the buildRendering method from the Templated module. This method is in charge of rendering the template and setting up the DOM correctly, so it's important to call the original method to ensure that everything will work out. This is done by calling `this.inherited(arguments)`. 466 | 467 | The Templated module also takes care of binding together the variables, so once a variable has been defined in the template, like this `${greeting}`, it can accessed and modified directly in the widget within the buildRendering method. This is useful for setting up labels and headings with translations. Notice that the variables can only be modified from the buildRendering method. 468 | 469 | This was a basic example of using the Templated module. We'll see more of this later. Let's add i18n and load a CSS file. 470 | 471 | i18n works exactly like you'd expect. We create properties files like you do for web scripts. Create the i18n file `src/main/amp/web/js/example/widgets/templates/i18n/TemplateWidget.properties`. Multiple languages can be supported by adding a prefix to the filename, like `TemplateWidget_en.properties`. 472 | 473 | Let's move our greeting into the properties file: 474 | 475 | hello-label=Hello from i18n 476 | 477 | All that's left to do now, is to load up the i18n file by setting the `i18nRequirements` property and call `this.message()` to grab the label out of the translation system. Everything else is taken care of by the Aikau framework. 478 | 479 | With the translations done, let's add a CSS file `src/main/amp/web/js/example/widgets/css/TemplateWidget.css` 480 | 481 | .my-template-widget { 482 | border: 1px #000000 solid; 483 | padding: 1em; 484 | width: 100px; 485 | } 486 | 487 | Once the CSS file is in place, add the `cssRequirements` property and point it to the CSS file. The final code looks like this: 488 | 489 | define(["dojo/_base/declare", 490 | "dijit/_WidgetBase", 491 | "alfresco/core/Core", 492 | "dijit/_TemplatedMixin", 493 | "dojo/text!./templates/TemplateWidget.html" 494 | ], 495 | function(declare, _Widget, Core, _Templated, template) { 496 | return declare([_Widget, Core, _Templated], { 497 | templateString: template, 498 | i18nRequirements: [ {i18nFile: "./i18n/TemplateWidget.properties"} ], 499 | cssRequirements: [{cssFile:"./css/TemplateWidget.css"}], 500 | 501 | buildRendering: function example_widgets_TemplateWidget__buildRendering() { 502 | this.greeting = this.message('hello-label'); 503 | this.inherited(arguments); 504 | } 505 | }); 506 | }); 507 | 508 | Add the widget to the `custom-widget-page.get.js` JSON model: 509 | 510 | model.jsonModel = { 511 | widgets: [{ 512 | id: "SET_PAGE_TITLE", 513 | name: "alfresco/header/SetTitle", 514 | config: { 515 | title: "This is a simple page" 516 | } 517 | }, 518 | { 519 | name: "example/widgets/MyWidget" 520 | }, 521 | { 522 | name: "example/widgets/TemplateWidget" 523 | }] 524 | }; 525 | 526 | Restart Share, and access [http://localhost:8081/share/page/hdp/ws/custom-widget-page](http://localhost:8081/share/page/hdp/ws/custom-widget-page) to see the result. 527 | 528 | ### Ajax 529 | 530 | Alfresco provides a CoreXhr module, which enables us to do Ajax calls. This module is fairly easy to use. We simply define `alfresco/core/CoreXhr` as a dependency and extend it, and then we can call `this.serviceXhr` to do GET/POST/PUT/DELETE operations against Alfresco. The serviceXhr method takes an object of parameters with parameters like URL, method (GET/POST etc), and callback methods for success/failure. 531 | 532 | Here is an example on how to use the `CoreXhr` module: 533 | 534 | define(["dojo/_base/declare", 535 | "dijit/_WidgetBase", 536 | "alfresco/core/Core", 537 | "alfresco/core/CoreXhr" 538 | ], 539 | function(declare, _Widget, Core, CoreXhr) { 540 | return declare([_Widget, Core, CoreXhr], { 541 | 542 | postCreate: function example_widgets_AjaxWidget__postCreate() { 543 | var url = Alfresco.constants.PROXY_URI + "slingshot/doclib/treenode/node/alfresco/company/home"; 544 | this.serviceXhr({url : url, 545 | method: "GET", 546 | successCallback: this._onSuccessCallback, 547 | callbackScope: this}); 548 | }, 549 | 550 | _onSuccessCallback: function example_widgets_AjaxWidget__onSuccessCallback(response, config) { 551 | console.log(response); 552 | } 553 | 554 | }); 555 | }); 556 | 557 | If we want to do a POST operation, we usually want to provide some data to go along with it. The object we pass to the serviceXhr method takes in a few parameters, so we add a `data` value in the object with our data for the POST operation. Unfortunately there is not much (if any) documentation on this just yet, but by looking at the code, the default options we can modify, look like this: 558 | 559 | var config = { 560 | handleAs: "text", 561 | method: "POST", 562 | data: null, 563 | query: null, 564 | headers: { "Content-Type": "application/json" }, 565 | successCallbackScope: this, 566 | successCallback: this.defaultSuccessCallback, 567 | failureCallbackScope: this, 568 | failureCallback: this.defaultFailureCallback, 569 | progressCallbackScope: this, 570 | progressCallback: defaultProgressCallback, 571 | alfTopic: null 572 | }; 573 | 574 | The callbacks will get two arguments: the response and the config object used for the request. 575 | 576 | ### DOM helpers and modules 577 | 578 | Since this article is not about Dojo, we won't go in too much detail with this subject. Dojo comes with modules for manipulating DOM, which we will see in the later examples by using `domConstruct` to create DOM nodes and attach them with attach points in our template. 579 | 580 | `domConstruct` enables us to create DOM nodes, and if we want, we can easily inject them into our templates with [attach points](http://dojotoolkit.org/reference-guide/1.9/dijit/_WidgetsInTemplateMixin.html#data-dojo-attach-point). 581 | 582 | Attach points link nodes from our template into our widget, so we do not have to do queries for the nodes. Attach points are defined by setting a data attribute on the element we wish to attach to our widget, and after that we can access the DOM node directly like this `this.containerNode`. The template would look like this: 583 | 584 |
585 | 586 | Here is an example on how to use `domConstruct` to create a new node inside `containerNode` with a text: 587 | 588 | define(["dojo/_base/declare", 589 | "dijit/_WidgetBase", 590 | "alfresco/core/Core", 591 | "dojo/dom-construct", 592 | "dijit/_TemplatedMixin", 593 | "dojo/text!./templates/TemplateWidget.html" 594 | ], 595 | function(declare, _Widget, Core, domConstruct, _Templated, template) { 596 | return declare([_Widget, Core, _Templated], { 597 | templateString: template, 598 | 599 | postCreate: function example_widgets_TemplateWidget__postCreate () { 600 | domConstruct.create( "div", { innerHTML: "Hello" }, this.containerNode ); 601 | } 602 | }); 603 | }); 604 | 605 | 606 | To get a full picture of working with DOM in Dojo, here is a list of recommended reading material: 607 | 608 | * [Dojo DOM functions](http://dojotoolkit.org/documentation/tutorials/1.9/dom_functions/) 609 | * [domConstruct](http://dojotoolkit.org/reference-guide/1.9/dojo/dom-construct.html) 610 | * [domClass](http://dojotoolkit.org/reference-guide/1.9/dojo/dom-class.html) 611 | * [query](http://dojotoolkit.org/reference-guide/1.9/dojo/query.html) 612 | 613 | 614 | 615 | ## Bringing it all together 616 | 617 | Let's build a widget that combines everything we've read so far. The advanced widget will use a template, it will have a CSS file, and use i18n. It will do an Ajax call to get all nodes in Company Home and use the `domConstruct` module to populate a table with the data. 618 | 619 | All this example code can be found in the part-two directory of the tutorial source code. 620 | 621 | The template is pretty simple: we define a table, variables for column headers, and a `tbody` with an attach-point so we can access it from the widget: 622 | 623 | `src/main/amp/web/js/example/widgets/templates/AjaxWidget.html` 624 | 625 |
626 |

${widgetTitle}

627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 |
${columnName}${columnDescription}
637 |
638 | 639 | 640 | We'll also need an i18n file to enable translations of our widget: 641 | 642 | `src/main/amp/web/js/example/widgets/i18n/AjaxWidget.properties` 643 | 644 | widgetTitle=Nodes found in Company Home 645 | columnName=Name 646 | columnDescription=Description 647 | 648 | 649 | Last but not least, the widget itself: 650 | 651 | `src/main/amp/web/js/example/widgets/AjaxWidget.js` 652 | 653 | define(["dojo/_base/declare", 654 | "dijit/_WidgetBase", 655 | "alfresco/core/Core", 656 | "alfresco/core/CoreXhr", 657 | "dojo/dom-construct", 658 | "dojo/_base/array", 659 | "dijit/_TemplatedMixin", 660 | "dojo/text!./templates/AjaxWidget.html" 661 | ], 662 | function(declare, _Widget, Core, AlfCoreXhr, domConstruct, array, _Templated, template) { 663 | return declare([_Widget, Core, AlfCoreXhr, _Templated], { 664 | templateString: template, 665 | cssRequirements: [{cssFile:"./css/AjaxWidget.css"}], 666 | i18nRequirements: [ {i18nFile: "./i18n/AjaxWidget.properties"} ], 667 | 668 | buildRendering: function example_widgets_AjaxWidget__buildRendering() { 669 | this.widgetTitle = this.message('widgetTitle'); 670 | this.columnName = this.message('columnName'); 671 | this.columnDescription = this.message('columnDescription'); 672 | this.inherited(arguments); 673 | }, 674 | 675 | postCreate: function example_widgets_AjaxWidget__postCreate() { 676 | var url = Alfresco.constants.PROXY_URI + "slingshot/doclib/treenode/node/alfresco/company/home"; 677 | this.serviceXhr({url : url, 678 | method: "GET", 679 | successCallback: this._onSuccessCallback, 680 | callbackScope: this}); 681 | }, 682 | 683 | _onSuccessCallback: function example_widgets_AjaxWidget__onSuccessCallback(response, config) { 684 | if (response.totalResults != undefined && response.totalResults > 0) { 685 | var parentNode = this.containerNode; 686 | array.forEach( response.items, function(item) { 687 | var row = domConstruct.create( "tr", {}, parentNode ); 688 | domConstruct.create( "td", { innerHTML: item.name }, row); 689 | domConstruct.create( "td", { innerHTML: item.description }, row); 690 | }); 691 | } 692 | } 693 | }); 694 | }); 695 | 696 | Create a new web script `ajax-page` that defines a page with the widget: 697 | 698 | `src/main/amp/config/alfresco/web-extension/site-webscripts/com/example/pages/ajax-page.get.desc.xml` 699 | 700 | 701 | Ajax Page 702 | Ajax page with a bit of DOM 703 | Share 704 | /ajax-page 705 | 706 | 707 | `src/main/amp/config/alfresco/web-extension/site-webscripts/com/example/pages/ajax-page.get.html.ftl` 708 | 709 | <@processJsonModel group="share"/> 710 | 711 | `src/main/amp/config/alfresco/web-extension/site-webscripts/com/example/pages/ajax-page.get.js` 712 | 713 | model.jsonModel = { 714 | widgets: [{ 715 | id: "SET_PAGE_TITLE", 716 | name: "alfresco/header/SetTitle", 717 | config: { 718 | title: "This is an advanced page with Ajax and DOM operations" 719 | } 720 | }, 721 | { 722 | name: "example/widgets/AjaxWidget" 723 | }] 724 | }; 725 | 726 | The end result looks like this: 727 | 728 | ![Ajax example widget](images/part-two-advanced-page.png) 729 | 730 | If we break it down a bit, there really is not much to it. We pull in the dependencies we need: `CoreXhr`, `dom-construct`, `array`, and `TemplatedMixin`. We also specify the template, css, and i18n files. 731 | 732 | The `buildRendering` method sets up the template with i18n, `postCreate` does an Ajax call to `Alfresco.constants.PROXY_URI + "slingshot/doclib/treenode/node/alfresco/company/home"` to get a list of all nodes in "Company Home", and uses the `_onSuccessCallback` method to do the actual rendering. 733 | 734 | In the `_onSuccessCallback` method we check that we actually got results, then catch the `containerNode` attach-point from the template, loop the reulsts, and construct a row with two columns for each result. 735 | 736 | ## Inter-widget communication 737 | 738 | Now that we know how to setup a page, build a widget and do various basic things with it (templates, i18n, Ajax etc), the next step is to build a full blown page with lots of widgets. Having a page with single serving widgets is fine, but we will quickly need to have the widgets communicate with each other and trigger different actions across the whole page. But how can widgets communicate with each other? They're completely separate and decoupled, which is a good thing. 739 | 740 | The Aikau framework introduces a publication/subscription system which in many aspects works like the old YUI2 Bubbling system. The idea is that to have a "channel" where widgets broadcast information, and setup other widgets to subscribe to the channel and act upon the information. 741 | 742 | This allows us to create a navigation widget where nodes can be selected. The navigation widget is only responsible for fetching the node. The rendering of the node is handled by another widget. Think about the document library for a moment, where there is a left menu that allows us to navigate the folder structure, and the content area is responsible for rendering the contents of the folder. In the document library the left menu fetches and renders a list of folders, but once you click on a folder, it also broadcasts information to a channel. The content area subscribes on the channel, and renders the folder contents once it receives information on the channel. 743 | 744 | Let's skip the metaphor, because when we're looking at the code, the terminology is a bit different. The channels are called topics and the information being broadcast is a payload, which could be anything, but usually it will be a JSON object. Once we extend `alfresco/core/Core`, we inherit two methods: `alfPublish` and `alfSubscribe`, which allows us to publish and subscribe to a topic. A topic is simply a string, which gives us something to identify it by. The `alfPublish` method takes the topic as the first argument, followed by the payload like this: 745 | 746 | this.alfPublish("MY_TOPIC", "my payload"); 747 | 748 | The payload can either be a plain string, or you can pass objects directly like this: 749 | 750 | this.alfPublish("MY_TOPIC", {nodes: [{name:"node1"}, {name: "node2"}]}); 751 | 752 | To subscribe to a topic we use the `alfSubscribe` method, which takes the topic as the first argument, followed by a callback to act on the payload: 753 | 754 | this.alfSubscribe(this.TutorialTopic, function(payload) { 755 | console.log(payload); 756 | }); 757 | 758 | Sometimes we don't want to have all our subscription logic inside a closure. In these cases we can use Dojo's `lang.hitch` method to connect the subscription with a method on our object: 759 | 760 | define(["dojo/_base/declare", 761 | "dijit/_WidgetBase", 762 | "alfresco/core/Core", 763 | "dojo/_base/lang", 764 | ], 765 | function(declare, _Widget, Core, lang) { 766 | return declare([_Widget, Core], { 767 | 768 | postCreate: function () { 769 | this.alfSubscribe("MY_TOPIC", lang.hitch(this, "_onPayloadReceive")); 770 | }, 771 | 772 | _onPayloadReceive: function (payload) { 773 | console.log(payload); 774 | } 775 | 776 | }); 777 | }); 778 | 779 | One last thing to note, is that whenever we receive a JSON object as a payload, the topic will be added to the object as the key `alfTopic`. A simple JSON object like `{nodes: []}` published to "MY_TOPIC" will become `{alfTopic: "MY_TOPIC", nodes: []}`. This is useful in identifying where the payload originated from. 780 | 781 | This is the very basic idea behind the pub/sub system. When it comes down to it, there really isn't much to it. However, this is what gives the widgets so much power 782 | 783 | ### A basic pub/sub example 784 | 785 | Let's create a very simple page with two widgets. The first widget will have a textarea for inputting a payload and a button. Once the button is clicked, it publishes the value from the textarea to a topic. Next to the input widget there will be a widget that subscribes to the topic and puts it on the page. 786 | 787 | Create a new web script `pub-sub` that defines a page with the widget: 788 | 789 | `src/main/amp/config/alfresco/web-extension/site-webscripts/com/example/pages/pub-sub.get.desc.xml` 790 | 791 | 792 | Publish-Subscription page 793 | Example on using pub/sub with widgets 794 | Share 795 | /pub-sub-example 796 | 797 | 798 | `src/main/amp/config/alfresco/web-extension/site-webscripts/com/example/pages/pub-sub.get.html.ftl` 799 | 800 | <@processJsonModel group="share"/> 801 | 802 | `src/main/amp/config/alfresco/web-extension/site-webscripts/com/example/pages/pub-sub.get.js` 803 | 804 | model.jsonModel = { 805 | widgets: [{ 806 | id: "SET_PAGE_TITLE", 807 | name: "alfresco/header/SetTitle", 808 | config: { 809 | title: "Pub/sub example" 810 | } 811 | }, 812 | { 813 | name: "alfresco/layout/HorizontalWidgets", 814 | config: { 815 | widgetWidth: 50, 816 | widgets: [ 817 | { 818 | name: "example/widgets/InputWidget" 819 | }, 820 | { 821 | name: "example/widgets/RenderWidget" 822 | } 823 | ] 824 | } 825 | }] 826 | }; 827 | 828 | 829 | This example uses the `alfresco/layout/HorizontalWidgets` layout widget to put the two widgets next to each other. 830 | 831 | #### A simple Mixin for constants 832 | 833 | We have to be careful when handling topics. If we hardcode the topic name into each file, things start to get messy. To solve this, we create a very simple object that defines a constant with the topic name. This way we can pull in the topic mixin and reference the constants instead. The topics mixin looks like this: 834 | 835 | 836 | `src/main/amp/web/js/example/widgets/_TopicsMixin.js` 837 | 838 | define(["dojo/_base/declare"], 839 | function(declare) { 840 | 841 | return declare(null, { 842 | TutorialTopic: "ALFRESCO_TUTORIAL_TOPIC" 843 | }); 844 | }); 845 | 846 | To use the mixin we simply define and declare the object. This way we extend it, and we can reference the topic name with `this.TutorialTopic` instead of hardcoding the topic into each file. 847 | 848 | For a very simple example like this, it might seem a bit overkill. However, when developing a big and complex matrix of widgets, we'll be grateful to have this. 849 | 850 | #### InputWidget 851 | 852 | For the InputWidget we'll use a few tricks to create a textarea and a button. First of all we need a simple template with a few attach points so we can place elements on the page: 853 | 854 | `src/main/amp/web/js/example/widgets/templates/InputWidget.html` 855 | 856 |
857 | 858 | 859 |
860 | 861 | To create a widget ad-hoc from within our widget, we simple define the widgets and initialize them as normal objects: 862 | 863 | this.textArea = new DojoTextarea({ 864 | label: this.message("inputWidget.inputLabel") 865 | }); 866 | 867 | Once we have initialized the DojoTextArea widget, we can use the `placeAt` method to have the widget rendered at an attach point in the template. The complete InputWidget looks like this: 868 | 869 | `src/main/amp/web/js/example/widgets/InputWidget.js` 870 | 871 | define(["dojo/_base/declare", 872 | "dijit/_WidgetBase", 873 | "alfresco/core/Core", 874 | "dojo/_base/lang", 875 | "alfresco/buttons/AlfButton", 876 | "alfresco/forms/controls/DojoTextarea", 877 | "alfresco/tutorial/_TopicsMixin", 878 | "dijit/_TemplatedMixin", 879 | "dojo/text!./templates/InputWidget.html" 880 | ], 881 | function(declare, _Widget, Core, lang, AlfButton, DojoTextarea, _TopicsMixin, _Templated, template) { 882 | return declare([_Widget, Core, _Templated, _TopicsMixin], { 883 | 884 | templateString: template, 885 | i18nRequirements: [ {i18nFile: "./i18n/PubSub.properties"} ], 886 | 887 | postCreate: function example_widgets_InputWidget__postCreate() { 888 | this.inherited(arguments); 889 | 890 | this.textArea = new DojoTextarea({ 891 | label: this.message("inputWidget.inputLabel") 892 | }); 893 | this.textArea.placeAt(this.inputTopicNode); 894 | 895 | var btn = new AlfButton({ 896 | label: "Publish", 897 | onClick: lang.hitch(this, '_onPublish') 898 | }); 899 | btn.placeAt(this.publishTopicNode); 900 | 901 | }, 902 | 903 | _onPublish: function example_widgets_InputWidget__onPublish() { 904 | this.alfPublish(this.TutorialTopic, this.textArea.getValue()); 905 | } 906 | 907 | }); 908 | }); 909 | 910 | The code is straight forward. Create a `DojoTextarea` and a `AlfButton`. Hitch the `_onPublish` method to the `onClick` event on the button, and place them in the template. Once the button is clicked, we publish the value of the textarea to the topic. 911 | 912 | #### RenderWidget 913 | 914 | For the render widget we also need a simple template with an attach point so we can display the value. The template looks like this: 915 | 916 | `src/main/amp/web/js/example/widgets/templates/RenderWidget.html` 917 | 918 |
919 | ${renderWidgetHeading} 920 | 921 |
922 | 923 | The widget itself is also easy, it pulls in TopicsMixin and setup the template with i18n. It also subscribes to the topic, and once it receives something, it uses `domConstruct` to create a `

` element with the text. 924 | 925 | `src/main/amp/web/js/example/widgets/RenderWidget.js` 926 | 927 | define(["dojo/_base/declare", 928 | "dijit/_WidgetBase", 929 | "alfresco/core/Core", 930 | "dojo/_base/lang", 931 | "alfresco/tutorial/_TopicsMixin", 932 | "dojo/dom-construct", 933 | "dijit/_TemplatedMixin", 934 | "dojo/text!./templates/RenderWidget.html" 935 | ], 936 | function(declare, _Widget, Core, lang, _TopicsMixin, domConstruct, _Templated, template) { 937 | return declare([_Widget, Core, _TopicsMixin, _Templated], { 938 | 939 | templateString: template, 940 | i18nRequirements: [ {i18nFile: "./i18n/PubSub.properties"} ], 941 | 942 | buildRendering: function example_widgets_renderWidget__buildRendering() { 943 | this.renderWidgetHeading = this.message('renderWidget.heading'); 944 | this.inherited(arguments); 945 | }, 946 | 947 | postCreate: function example_widget_renderWidget__postCreate() { 948 | this.alfSubscribe(this.TutorialTopic, lang.hitch(this, "_onPayloadReceive")); 949 | }, 950 | 951 | _onPayloadReceive: function example_widgets_renderWidget__onPayloadReceive(payload) { 952 | var txt = domConstruct.create( "p", { innerHTML: payload }, this.payloadContainer ); 953 | } 954 | 955 | }); 956 | }); 957 | 958 | 959 | 960 | For the final result, access the page on [http://localhost:8081/share/page/hdp/ws/pub-sub-example](http://localhost:8081/share/page/hdp/ws/pub-sub-example). There will be a textarea and a publish button. Enter something into the textarea, click the publish button, and the result will be rendered on the right side of the page: 961 | 962 | ![Basic Pub/Sub example](images/part-two-pub-sub.png) 963 | 964 | 965 | # Part Three: Extending JSON models 966 | 967 | Now that we know a lot more about how to build pages and utilize the Aikau framework, let's back up and think about the JSON page definition. It's nice that we can define new pages, but what happens once Alfresco starts to really work the Aikau framework into Share? We still want to be able to extend it. 968 | 969 | As of 4.2.f, the only component that uses Aikau is the header. Now, what is the header component exactly? 970 | 971 | ![Share 4.2 Header](images/part-three-header-1.png) 972 | 973 | The header component consists of the black top menu and the logo/title/buttons below it. It's defined in `WEB-INF/classes/alfresco/site-webscripts/org/alfresco/share/header/share-header.get.js`. 974 | This file is pretty simple. It includes `WEB-INF/classes/alfresco/site-webscripts/org/alfresco/share/imports/share-header.lib.js` which is where all the magic happens. 975 | 976 | Now, the header is a big component. It handles everything from the menu, user dashboard title, site title, buttons etc, so there is a lot of code there. In the end it ends up with a big JSON page model definition, but the definition is too big to go through all of it. 977 | 978 | Luckily, we have some very good options for extending a JSON model, instead of overwriting it. First of all, we need to create a Share module extension that will define which customizations we have and which files to target. This is done in an XML file in the `src/main/amp/config/alfresco/web-extension/site-data/extensions/` folder. Once it has been created, we need to create a javascript file where we can use a new root scoped object called `widgetUtils`, which allows us to find components in an existing JSON model and work with it. 979 | 980 | ## Creating an extension 981 | 982 | To extend any web script in Share, we need to define a custom extension module. Create a new XML file ending in `src/main/amp/config/alfresco/web-extension/site-data/extensions/` like this: 983 | 984 | 985 | 986 | 987 | Add custom menu item to header 988 | 1.0 989 | true 990 | 991 | 992 | org.alfresco.share.header 993 | com.example.header 994 | 995 | 996 | 997 | 998 | 999 | 1000 | Here we define a module, give it an ID, version and tell it to auto deploy. If you do not specify the auto deploy, you will have to manually go to the module deployment page on `http://localhost:8081/share/page/modules/deploy` to deploy it. 1001 | 1002 | The `` section is used to tell Share which folder we want to target, and where it can find the source for the customizations. This is not specific to the Aikau framework, as it's been in Share for a while, and it's the best way to do clean extensions of Share's own files. We're targeting `org.alfresco.share.header` which translates into `WEB-INF/classes/alfresco/site-webscripts/org/alfresco/share/header`. Anything in this folder can be extended in our own folder in `src/main/amp/config/alfresco/web-extension/site-webscripts/com/example/header`. 1003 | 1004 | The general idea behind this is that you can extend any web script in Share. Our script will run after Share's own. That way we can hook into the model object and modify as much as we want. We can extend/overwrite Freemarker templates using this same technique. 1005 | 1006 | On top of all of this, you can inject evaluators into the extension, so you can create custom evaluators or use the existing evaluators to only have your extension apply in certain situations. 1007 | 1008 | ## Adding a menu item to the header 1009 | 1010 | Now that we've defined an extension and told Share that the target is `org.alfresco.share.header`, create a web script controller called `share-header.get.js` in `src/main/amp/config/alfresco/web-extension/site-webscripts/com/example/header`. Let's find the menu and add our own menu item that links to the user's profile: 1011 | 1012 | `src/main/amp/config/alfresco/web-extension/site-webscripts/com/example/header/share-header.get.js` 1013 | 1014 | var headerMenu = widgetUtils.findObject(model.jsonModel, "id", "HEADER_APP_MENU_BAR"); 1015 | if (headerMenu != null) { 1016 | headerMenu.config.widgets.push({ 1017 | id: "HEADER_CUSTOM_PROFILE_LINK", 1018 | name: "alfresco/menus/AlfMenuBarItem", 1019 | config: { 1020 | label: "My profile", 1021 | targetUrl: "user/" + encodeURIComponent(user.name) + "/profile" 1022 | } 1023 | }); 1024 | } 1025 | 1026 | This is all that is required to extend an existing JSON model. We're using `widgetUtils` to find the `HEADER_APP_MENU_BAR` widget. Once we have it, we simply push a widget into it. We can push any widget we want, but since we're in the header, we should probably limit it to smaller things. Here we could remove, add or change any of the things in the header. Want a new search box? Find the `HEADER_SEARCH_BOX` widget and replace it with a new one. 1027 | 1028 | To figure out what ID's to target we have two options. First option is to go through the `share-header.lib.js` file, which defines the whole header. The problem with this file is that it's very big and confusing to dig into. The alternative is to use a tool like Firebug/Developer tools in Chrome and inspect the elements from the browser. If we look at the search box, we'll see that its markup looks like this: 1029 | 1030 |