Aikau Dynamic UI Generation

Download Report

Transcript Aikau Dynamic UI Generation

Dynamic UI Generation with Alfresco Share and Aikau

Kevin Dorr Sr. Solutions Engineer Alfresco Channel Americas

Agenda

• What • Why • Where • How

What is Aikau?

A Interesting Story

Aikau History

UI / UX is Terrifying and Complex

Custom UI Development, Really?

Yeah Really!

Some Things People Say to Me

Share would be great if it just had a way to… Share seems just like Sharepoint , except then you find out it’s not.

Share is way more than I want to expose my users to… I really liked that Workdesk thing that you guys had. Why did you get rid of it again?

I want my user experience to look exactly like this: I’m porting my product so that it will work with Share. So how do I do that?

Example Aikau User Interfaces

Ok, So Where is this Stuff, Anyway

Using Aikau and Aikau Tools

The Aikau Screen Builder

Displaying an Aikau Page

• Stand Alone Page Webscripts /share/page/dp/ws/{webscript URL} /share/page/site/{site}/dp/ws/{ws URL} • Hybrid Pages (Share header and footer) /share/page/hdp/ws/{ws URL} • Hybrid Remote Pages (from Repo) /share/page/site/{site}/p/{page name} /share/page/hrp/p/{page name}

Adding an Aikau Page to a Site

• Hook to an Existing Page • I will show an example • Pass the Current Context to a New Page • We will publish an example • Or, Create a New Standalone Page and Configure it In through the Site Menu • Same as it was

Aikau Resources

• Blogs!

• Search for Dave Draper • Summit Videos • Last year = 100 level • This year = 200 level • Ole Hejlskov’s Tutorial • http://ohej.github.io/alfresco-tutorials/tutorial/aikau/tutorial.html#where-to-find more-information • Documentation!

• http://dev.alfresco.com/resource/docs/jsdoc-haiku/ • Email Me! • • We are interested in hearing about your projects!

[email protected]

Just How Flexible Is It?

Aikau Widgets, Styling, and Internationalization

Alfresco Widget Library

Adding Widgets

• Easy to Add New Widgets • Please open source if appropriate!

• Commerical opportunity!

• Dojo Widgets can be Used Directly • I showed an example of extending a Dojo widget last year • But… You can Configure in ANY JS Library!

• I will show a jQuery example • We will publish an Angluar example • Let us know what other examples you would like!

Styling and Themes

• Widgets can Have an Individual CSS File • Add the CSS to the META-INF directory • Theme support is limited right now • Widgets need to use a common convention in order for themes to work • Engineering has not focused on this yet • But, it will be coming!

Text and Internationalization

• Best Practice is to Use a Properties File • Add all of your UI strings as properties • Get them using the msg.get() function • This Allows the Interface to be Internationalized using Filename Extensions • A properties file with a “fr” extension will be selected if the interface is in French mode • Just like standard Java

The Nitty Gritty

“Wax Your Board!”

To Review…

• page-name .get.desc.xml (Webscript Descriptor) • • page-name .get.js page-name .get.html.ftl

(WebScript Controller) (Freemarker Template) • page-name .get.properties (Optional Text Strings) Files need to be in the same directory Anywhere under site-webscripts is ok

To Review… Page Descriptor

Create page via JSON editor A page definition for creating pages using a JSON editor Aikau

/page-editor

To Review… Freemarker Template

<@processJsonModel group="share"/>

To Review… the JS Page Controller

{ } "widgets": [ { "id": "SET_PAGE_TITLE", "name": "

alfresco/header/SetTitle

", "config": { "title": "Alfresco Summit Demo" } }, { "name": "

alfresco/layout/AlfSideBarContainer

", "config": { "initialSidebarWidth": "300" } } ]

Page Layout

• Hand Code the JSON • I showed an example of extending a Dojo widget last year • Programmatically Generate the JSON • Interesting examples in the Faceted Search page • Use the Page Creator Tool!

• Very functional – also configurable • Great start on a page • However, not all of the widgets are on the palette

Page Webscript Basics

• Define your JSON Any Way you Want • Once again, interesting examples in the Faceted Search page • Add in Any Additional Logic You Need • It’s a Javascript file!

• There are lots of built in Alfresco functions, Dojo function, etc.

• You can dynamically splice page components together – I will show an example

How Do I Get Data on the Page?

• Services are Exposed as Components • In the Page Builder Tool Palette • Look at the search code for examples • Also Special Data Widgets • Quick and Dirty Data Structures (QuaDDS) • Stores data structures you define as JSON in a folder in the repo in the Data Dictionary • 1001 uses!

Capturing Events

• Events Use Pub/Sub Paradigm • They will bubble up through the DOM • You can also limit the scope – Dojo stuff • Pub/Sub is by Name • Easy to generate or subscribe to an event • You can marshal a data package to go along with the event • I will show some examples

Fully Dynamic Page Creation Example “Eddie Would Go!”

Demonstration Scenario

• Acme Manufacturing Technical Support • Target: CSRs and Support Engineers • Handle Problem Reports (mostly from Wiley E. Coyote) • Extend the Basic Search Page • Searches on Acme Case Specific Types • Tune Searches for User Types • Needs to be done Yesterday!!!

That Sinking Feeling

Leveraging an Existing Page

What We Will Do

• Add a Toggle to the Search Page • Toggle between the default keyword search and a “special” search page • Add a Form on the Special Search Page • Define the form with the Page Creation Tool • Search for Particular Content Types • Provide Different Search Criteria by User

Infrastructure and Code Setup

Setup to Extend the Search Form

• Need to Define 2 Files: • A Module Extension Definition • The JavaScript for the Aikau Page/Form Extension • Packaging • Module extension in alfresco/site data/extensions • Code in alfresco/site-webscripts/… • Deliver as a jar (for development) or AMP (for production)

Maven 2.0 Package Setup

Define the Module Extension File

File: alfresco/site-data/extensions/acme-special-search-extension.xml

Acme Special Search true org.alfresco.share.pages.faceted-search com.acme.customizations.special-search

Adding the Search Toggle Button

Adding the Search Toggle Button

Dynamically Adding to the Search Page

• Identify the Code to Extend • Adding New Functionality to the Page • Identify the Form to Add to • Add Widget Definitions • Handle the Toggle Event • Using a Special Aikau Trick!

• Deploy and See What Happens!

Finding the Search Page Code

Finding the Search Page Code

Finding the Search Page Code

Search Page Code Files

Web Script: org/alfresco/share/pages/faceted-search/faceted-search.get

Script Properties Id: Short Name: org/alfresco/share/pages/faceted-search/faceted-search.get

Aikau Faceted Search Page […] File: org/alfresco/share/pages/faceted-search/faceted-search.get.desc.xml

Aikau Faceted Search Page Prototype Aikau based Faceted Search Page Aikau /faceted-search File: org/alfresco/share/pages/faceted-search/faceted-search.get.html.ftl

<@processJsonModel group="share"/> File: org/alfresco/share/pages/faceted-search/faceted-search.get.properties

Locating the ID to Hook Widgets To

File: org/alfresco/share/pages/faceted-search/faceted-search.get.js

var scopeSelection = { id: "FCTSRCH_TOP_MENU_BAR", name: "alfresco/layout/LeftAndRight", config: { widgets: [ { name: "alfresco/html/Label", config: { label: msg.get("faceted-search.scope.label") } }, { name: "alfresco/menus/AlfMenuBar", config: { widgets: [ { id: "FCTSRCH_SCOPE_SELECTION_MENU", name: "alfresco/menus/AlfMenuBarSelect", <….>

Building the Page Toggle

File: alfresco/site-webscripts/com/acme/customizations/special-search/facted-search.get.js

// Create links for hiding and showing the advanced search...

var showAdvancedSearchLink = { name: "alfresco/renderers/PropertyLink", align: "right", config: { visibilityConfig: hideRegularSearch, currentItem: { label: "Display Acme Case Mgt Search" }, propertyToRender: "label", renderSize: "small", useCurrentItemAsPayload: false, publishTopic: "ALF_SHOW_ADVANCED_SEARCH", publishPayloadType: "CONFIGURED", publishPayload: { show: true } } };

Building the Page Toggle

File: alfresco/site-webscripts/com/acme/customizations/special-search/facted-search.get.js

var hideAdvancedSearchLink = { name: "alfresco/renderers/PropertyLink", align: "right", config: { visibilityConfig: showAdvancedSearch, currentItem: { label: "Display Regular Search" }, propertyToRender: "label", renderSize: "small", useCurrentItemAsPayload: false, publishTopic: "ALF_SHOW_ADVANCED_SEARCH", publishPayloadType: "CONFIGURED", publishPayload: { show: false } } }; // Add the new widgets to the main stack...

var topMenuBar = widgetUtils.findObject(model.jsonModel.widgets, "id", "FCTSRCH_TOP_MENU_BAR"); if (topMenuBar && topMenuBar.config && topMenuBar.config.widgets) { topMenuBar.config.widgets.splice(3, 0, showAdvancedSearchLink, hideAdvancedSearchLink); }

Dynamic Form Extension!

Special Aikau Visibility Trick

File: alfresco/site-webscripts/com/acme/customizations/special-search/facted-search.get.js

// Set up some config for showing and hiding the advanced search features...

var showAdvancedSearch = { initialValue: false, rules: [ { topic: "ALF_SHOW_ADVANCED_SEARCH", attribute: "show", is: [true], isNot: [false] } ] }; var hideRegularSearch = { initialValue: true, rules: [ { topic: "ALF_SHOW_ADVANCED_SEARCH", attribute: "show", is: [false], isNot: [true] } ] };

What it Looks Like

What it Looks Like

Adding the Search Forms

Generating a New Search Interface

Generating a New Search Interface

Generating a New Search Interface

Generating a New Search Interface

Generating a New Search Interface

Page Creator Output

File: AcmeCSRForm.get.js

model.jsonModel = { publishOnReady: "", services: "", widgets: [ { name: "alfresco/forms/Form", config: { displayButtons: true, okButtonLabel: "OK", cancelButtonLabel: "Cancel", fieldId: "bff6e205-2af1-423b-81d7-79b2af7089a0", widgets: [ { name: "alfresco/forms/controls/DojoValidationTextBox", config: { label: "Case Id", name: "prop_acme_caseid", value: "", description: "Enter the case identifier" } },

Finding the Form to Append to

Finding the Search Code Hook

File: org/alfresco/share/pages/faceted-search/faceted-search.get.js

// Compose the search form model var searchForm = { id: "FCTSRCH_SEARCH_FORM", name: "alfresco/forms/SingleTextFieldForm", config: { useHash: true, okButtonLabel: msg.get("faceted-search.search-form.ok-button-label"), okButtonPublishTopic : "ALF_SET_SEARCH_TERM", okButtonPublishGlobal: true, okButtonIconClass: "alf-white-search-icon", okButtonClass: "call-to-action", textFieldName: "searchTerm", textBoxIconClass: "alf-search-icon", textBoxCssClasses: "long hiddenlabel", textBoxLabel: msg.get("faceted-search.search-form.search-field-label") } };

Grabbing the Form Hook

File: alfresco/site-webscripts/com/acme/customizations/special-search/facted-search.get.js

var searchForm = widgetUtils.findObject(model.jsonModel.widgets, "id", "FCTSRCH_SEARCH_FORM"); if (searchForm && searchForm.config) { searchForm.config.scopeFormControls = false; searchForm.config.visibilityConfig = hideRegularSearch; }

Copy and Paste the Form Definition

File: alfresco/site-webscripts/com/acme/customizations/special-search/facted-search.get.js

var advancedSearchOptions = [ { label: “CSR View", value: JSON.stringify([ // paste form fields for form 1 here ]) }, { label: “Management View", value: JSON.stringify([ // paste form fields for form 2 here ]) } ];

After the Paste…

File: alfresco/site-webscripts/com/acme/customizations/special-search/facted-search.get.js

// Define two new custom forms - one for a CSR and one for a manager. Add as an array so that // we can use a selector to display and select which form: var advancedSearchOptions = [ { label: “CSR View", value: JSON.stringify([ // pasted form fields for form 1 here: { name: "alfresco/forms/controls/DojoValidationTextBox", config: { label: "Case Id", name: "prop_acme_caseid", value: "", description: "Enter the case identifier" } }, { name: "alfresco/forms/controls/DojoValidationTextBox", config: { label: "Case Customer", name: "prop_case_customer", value: "", description: "Enter the customer name" } }, { name: "alfresco/forms/controls/DojoValidationTextBox", config: { label: "Case Description", name: "prop_acme_casedescr", value: "", description: "Enter all or part of the case description" } }, { name: "alfresco/forms/controls/DojoSelect", config: { label: "Case Status", name: "prop_acme_casestatus", value: "", unitsLabel: "", description: "Select the case status", optionsConfig: { fixed: [ { label: "Pending Customer", value: "PendCustomer" }, { label: "Pending Support", }, { value: "PendSupport" }, label: "Pending Research", value: "PendReseach" { label: "Pending Engineering", value: "PendEngineering" }, { label: "Stale / Timed Out", value: "PendStale" } ] } } }, { name: "alfresco/forms/controls/DojoRadioButtons", config: { label: "Case Disposition", name: "prop_acme_casedisposition", value: "", description: "Select the case disposition", optionsConfig: { fixed: [ { label: "Case Open", value: "Open" }, { label: "Case Closed", value: "Closed" }, { label: "Case Deferred", value: "Deferred" } ] } } } ]) }, { label: “Management View", value: JSON.stringify([ // pasted form fields for form 2 here { name: "alfresco/forms/controls/DojoValidationTextBox", config: { label: "Case Id", name: "prop_acme_caseid", value: "", description: "Enter the case identifier" } }, { name: "alfresco/forms/controls/DojoValidationTextBox", config: { label: "Case Customer", name: "prop_case_customer", value: "", description: "Enter the customer name" } }, { name: "alfresco/forms/controls/DojoRadioButtons", config: { label: "Case Overdue", name: "prop_acme_caseoverdue", value: "", optionsConfig: { fixed: [ { label: "Yes", value: "true" }, { label: "No", value: "false" } ] } } } ]) } ];

Add the Container for the Forms

File: alfresco/site-webscripts/com/acme/customizations/special-search/facted-search.get.js

// Create a form for selecting the advanced form...

var advancedSearchFormSelect = { name: "alfresco/forms/Form", config: { showCancelButton: false, showOkButton: false, scopeFormControls: false, widgets: [ { name: "alfresco/forms/controls/DojoSelect", config: { fieldId: "ADVANCED_SEARCH_OPTION", label: "Acme Case Data Search", visibilityConfig: showAdvancedSearch, optionsConfig: { fixed: advancedSearchOptions } } } ] } };

Add the Container for the Forms

File: alfresco/site-webscripts/com/acme/customizations/special-search/facted-search.get.js

// Create a form for selecting the advanced form...

var advancedSearchFormSelect = { name: "alfresco/forms/Form", config: { showCancelButton: false, showOkButton: false, scopeFormControls: false, widgets: [ { name: "alfresco/forms/controls/DojoSelect", config: { fieldId: "ADVANCED_SEARCH_OPTION", label: "Acme Case Data Search", visibilityConfig: showAdvancedSearch, optionsConfig: { fixed: advancedSearchOptions } } } ] } };

Add the Code to Post the Form

File: alfresco/site-webscripts/com/acme/customizations/special-search/facted-search.get.js

// Create a dynamic form for displaying each advanced form...

var dynamicForm = { name: "alfresco/forms/DynamicForm", config: { visibilityConfig: showAdvancedSearch, scopeFormControls: false, subscriptionTopic: "_valueChangeOf_ADVANCED_SEARCH_OPTION", okButtonLabel: msg.get("faceted-search.search-form.ok-button-label"), okButtonPublishTopic: "ALF_ADVANCED_SEARCH", okButtonPublishGlobal: true, showCancelButton: false } }; // Bind the new widgets to the main stack...

var mainVerticalStack = widgetUtils.findObject(model.jsonModel.widgets, "id", "FCTSRCH_MAIN_VERTICAL_STACK"); if (mainVerticalStack && mainVerticalStack.config && mainVerticalStack.config.widgets) { mainVerticalStack.config.widgets.splice(3, 0, advancedSearchFormSelect, dynamicForm); }

Finished Result Page

Finished Result Page

Project Complete!

Bonus: Adding a Custom Widget

It’s Easy and Fun!

Additional Scenario

• Add a Slider Widget to the Search Page • Allow a user to use a slide to search by file size • We will use a jQuery Slider, Since we like jQuery • We’ll need to set up the jQuery JavaScript and CSS libraries

Module Definition File

File: alfresco/site-data/extensions/slider-extension.xml

Custom Search Form true org.alfresco.share.pages.faceted-search com.alfresco.customizations.slider

JavaScript Customization

File: alfresco/site-webscripts/com/alfresco/customizations/slider/facted-search.get.js

var searchForm = widgetUtils.findObject(model.jsonModel.widgets, "id", "FCTSRCH_SEARCH_FORM"); if (searchForm) { searchForm.name = "alfresco/forms/Form"; searchForm.config = { okButtonLabel: "Custom Search", okButtonPublishTopic : "ALF_ADVANCED_SEARCH", okButtonPublishGlobal: true, widgets: [ { name: "summit/Slider", config: { name: "searchTerm", value: 50, label: "Slider", unitsLabel: "Gb" } } ] }; }

Finished Result Page

Q&A

Thank you for attending!

Finding the Search Code

File: org/alfresco/share/pages/faceted-search/faceted-search.get.js

// Put all components together var main = { id: "FCTSRCH_MAIN_VERTICAL_STACK", name: "alfresco/layout/VerticalWidgets", config: { baseClass: "side-margins", widgets: [ { name: "alfresco/html/Spacer", config: { height: "14px" } }, headingForSearchForm, searchForm, // more widget declarations

Finding the Search Code

File: org/alfresco/share/pages/faceted-search/faceted-search.get.js

main.config.widgets.splice(2, 0, scopeSelection); // Append services with those required for search services.push("alfresco/services/NavigationService", "alfresco/services/SearchService", "alfresco/services/ActionService", "alfresco/services/DocumentService", "alfresco/dialogs/AlfDialogService", "alfresco/services/PreferenceService", "alfresco/services/QuickShareService", "alfresco/services/RatingsService", "alfresco/services/CrudService", "alfresco/services/NotificationService", "alfresco/services/ContentService"); // Add in the search form and search doc lib...

widgets.unshift(accessMenu); widgets.push(main);