View
2.910
Download
2
Category
Preview:
DESCRIPTION
This is a presentation I gave at Alfresco DevCon 2010 in the Best Practices track. It covers patterns of Alfresco customization, compares Spring Surf to agile application development frameworks like Django, and provides best practices and advice around developing Share customizations.
Citation preview
Alfresco from an agile framework perspectiveJeff Potts
jpotts@metaversant.com
© Copyright 2010, Metaversant Group, Inc. | http://www.metaversant.com
Agenda
• Patterns of Alfresco Customization• A Tale of Two Frameworks: Surf vs. Django• Heavy Share Customization: A real-world example• Conclusions & Advice
PATTERNS OF ALFRESCO CUSTOMIZATION
© Copyright 2010, Metaversant Group, Inc. | http://www.metaversant.com
Custom Alfresco Patterns
• Non-Alfresco framework on top of Alfresco
• Surf on top of Alfresco• Light Share Customizations• Heavy Share Customizations
• Patterns we aren’t going to talk about:– Explorer client customizations– Portal integration– Embedded Alfresco
Source: thomas hawk
© Copyright 2010, Metaversant Group, Inc. | http://www.metaversant.com
Non-Alfresco Framework
Source: Optaros
+
© Copyright 2010, Metaversant Group, Inc. | http://www.metaversant.com
Surf on Alfresco
Source: Optaros
© Copyright 2010, Metaversant Group, Inc. | http://www.metaversant.com
Light Share Customizations
© Copyright 2010, Metaversant Group, Inc. | http://www.metaversant.com
Heavy Share Customizations
A TALE OF TWO FRAMEWORKS
© Copyright 2010, Metaversant Group, Inc. | http://www.metaversant.com
Surf or Something Else?
• Share is a popular, extensible client with a great UI
• When Share is too much or too far from your business requirements…– Which framework on top of
Alfresco?– An experiment…
Source: irargerich
© Copyright 2010, Metaversant Group, Inc. | http://www.metaversant.com
A word on agile frameworks
• Agile frameworks and scripting languages are very popular
• Examples: Rails, Grails, Django, Wicket, Symfony, Cake, etc.
• Productive, fast dev cycles• Built-in Bootstrapping, ORM, MVC,
tests/fixtures, administrative UI’s• Hundreds available across every
language imaginable• Can be trendy, like frozen yogurt
Source: mswine
© Copyright 2010, Metaversant Group, Inc. | http://www.metaversant.com
Common requirements
• Let content authors create "chunks" and upload files
• Chunks and files get tagged and categorized• Not all objects have files--the UI can't freak out
when it comes across "content-less" objects• Front-end web site needs to be able to query for
chunks, files, and content-less objects• The front-end web site cannot look like Share
– Our users aren't teams– They don't care about “document libraries”
© Copyright 2010, Metaversant Group, Inc. | http://www.metaversant.com
A simple example: To Do
• Basic requirements– End users create/manage to do list items– To do list items are tagged– End users can upload documents related to To Do’s
• Extended requirements– Certain categories of To Do Lists have associated
content chunks that need to be displayed.• Example: To do list that is categorized as "Writing"
should display with content chunks that give advice on writing.
– Groupings of To Do lists• Friends/Co-workers• Projects, etc.
– RSS feeds
© Copyright 2010, Metaversant Group, Inc. | http://www.metaversant.com
Approaches
• To Do’s, Users, and Files are objects.• I’ll map URLs to various views on those
objects.• I’ll probably use a relational database to
persist everything except the files, which I’ll let my framework handle.
• Files are documents. That’s easy.• To Do’s are “content-less” objects.• I need to figure out a folder for all of this
stuff to live in and how I want to relate To Do’s to files.
• I’ll map URLs to various views which will request data from the repository via REST.
© Copyright 2010, Metaversant Group, Inc. | http://www.metaversant.com
Five-minute look at Django
• Creating a new Django app• Defining a content model• Creating a template• Model View Controller• Using the admin site to edit
object instances
Source: William Gottlieb
Fun Django Facts:•Started as an internal project in 2003 at the Journal-World newspaper (Lawrence, KS)•Named after jazz guitarist, Django Reinhardt•The Onion recently migrated to Django from Drupal
Fun Django Facts:•Started as an internal project in 2003 at the Journal-World newspaper (Lawrence, KS)•Named after jazz guitarist, Django Reinhardt•The Onion recently migrated to Django from Drupal
© Copyright 2010, Metaversant Group, Inc. | http://www.metaversant.com
Model
from django.db import modelsfrom django.contrib.auth.models import Userfrom datetime import date, datetime
class ToDoItem(models.Model): title = models.CharField(max_length=200) dueDate = models.DateField(default=date.today()) priority = models.IntegerField(default=3) status = models.TextField() notes = models.TextField() createdDate = models.DateTimeField(default=datetime.today()) creator = models.ForeignKey(User, related_name='todo_creator') assignee = models.ForeignKey(User, null=True, blank=True, related_name='todo_assignee') #attachments = Document # Array of CMIS documents def __unicode__(self): return self.title
class Tag(models.Model): name = models.CharField(max_length=64, unique=True) toDoItems = models.ManyToManyField(ToDoItem) def __unicode__(self): return self.name
from django.db import modelsfrom django.contrib.auth.models import Userfrom datetime import date, datetime
class ToDoItem(models.Model): title = models.CharField(max_length=200) dueDate = models.DateField(default=date.today()) priority = models.IntegerField(default=3) status = models.TextField() notes = models.TextField() createdDate = models.DateTimeField(default=datetime.today()) creator = models.ForeignKey(User, related_name='todo_creator') assignee = models.ForeignKey(User, null=True, blank=True, related_name='todo_assignee') #attachments = Document # Array of CMIS documents def __unicode__(self): return self.title
class Tag(models.Model): name = models.CharField(max_length=64, unique=True) toDoItems = models.ManyToManyField(ToDoItem) def __unicode__(self): return self.name
models.py
© Copyright 2010, Metaversant Group, Inc. | http://www.metaversant.com
URLs map to Viewsurlpatterns = patterns('', (r'^admin/', include(admin.site.urls)), (r'^$', main_page), (r'^user/(\w+)/$', user_page), (r'^login/$', 'django.contrib.auth.views.login'), (r'^logout/$', logout_page), (r'^register/$', register_page), (r'^register/success/$', direct_to_template, {'template': 'registration/register_success.html'}), (r'^create/$', todo_create_page), (r'^save/(\d+)/$', todo_save_page), (r'^site_media/(?P<path>.*)$', 'django.views.static.serve', {'document_root': site_media}), (r'^tag/([^\s]+)/$', tag_page), (r'^tag/$', tag_cloud_page),)
urlpatterns = patterns('', (r'^admin/', include(admin.site.urls)), (r'^$', main_page), (r'^user/(\w+)/$', user_page), (r'^login/$', 'django.contrib.auth.views.login'), (r'^logout/$', logout_page), (r'^register/$', register_page), (r'^register/success/$', direct_to_template, {'template': 'registration/register_success.html'}), (r'^create/$', todo_create_page), (r'^save/(\d+)/$', todo_save_page), (r'^site_media/(?P<path>.*)$', 'django.views.static.serve', {'document_root': site_media}), (r'^tag/([^\s]+)/$', tag_page), (r'^tag/$', tag_cloud_page),)
settings.py
def user_page(request, username): user = get_object_or_404(User, username=username) todos = user.todo_assignee.order_by('-id') variables = RequestContext(request, { 'username': username, 'todos': todos, 'show_tags': True, 'show_assignee': False, 'show_creator': True, 'show_edit': username == request.user.username, }) return render_to_response( 'user_page.html', variables )
def user_page(request, username): user = get_object_or_404(User, username=username) todos = user.todo_assignee.order_by('-id') variables = RequestContext(request, { 'username': username, 'todos': todos, 'show_tags': True, 'show_assignee': False, 'show_creator': True, 'show_edit': username == request.user.username, }) return render_to_response( 'user_page.html', variables )
views.py
© Copyright 2010, Metaversant Group, Inc. | http://www.metaversant.com
<html> <head> <title>Django To Do's | {% block title %}{% endblock %}</title> <link rel="stylesheet" href="/site_media/style.css" type="text/css" /> </head> <body> <div id="nav"> <a href="/">home</a> {% if user.is_authenticated %} welcome, <a href="/user/{{ user.username }}/">{{ user.username }}</a>! | <a href="/create/">new to do</a> | <a href="/logout">logout</a> {% else %} <a href="/login/">login</a> <a href="/register/">register</a> {% endif %} </div> <h1>{% block head %}{% endblock %}</h1> {% block content %}{% endblock %} </body></html>
<html> <head> <title>Django To Do's | {% block title %}{% endblock %}</title> <link rel="stylesheet" href="/site_media/style.css" type="text/css" /> </head> <body> <div id="nav"> <a href="/">home</a> {% if user.is_authenticated %} welcome, <a href="/user/{{ user.username }}/">{{ user.username }}</a>! | <a href="/create/">new to do</a> | <a href="/logout">logout</a> {% else %} <a href="/login/">login</a> <a href="/register/">register</a> {% endif %} </div> <h1>{% block head %}{% endblock %}</h1> {% block content %}{% endblock %} </body></html>
base.html
{% extends "base.html" %}{% block title %}{{ username }}{% endblock %}{% block head %}To Do's for {{ username }}{% endblock %}{% block content %} {% include "todo_list.html" %}{% endblock %}
{% extends "base.html" %}{% block title %}{{ username }}{% endblock %}{% block head %}To Do's for {{ username }}{% endblock %}{% block content %} {% include "todo_list.html" %}{% endblock %}
user_page.html {% if todos %} <ul class="todos"> {% for todo in todos %} <li> <a href="/todo/{{ todo.id }}" class="title">{{ todo.title }}</a> {% if show_edit %} …
{% if todos %} <ul class="todos"> {% for todo in todos %} <li> <a href="/todo/{{ todo.id }}" class="title">{{ todo.title }}</a> {% if show_edit %} …
todo_list.html
Template Inheritance
© Copyright 2010, Metaversant Group, Inc. | http://www.metaversant.com
Alfresco approach
• Content Consumer UI– Custom Surf pages/templates/components for the "front-end"
user interface• Administrative UI
– Lightly-customized Alfresco Share• Data Persistence
– Custom content model for properties, associations (To Do data list happens to already exist)
– Document library for files and content chunks– Data List objects for To Do items– Rule on a folder to add taggable and classifiable aspects to
new objects
© Copyright 2010, Metaversant Group, Inc. | http://www.metaversant.com
Alfresco approach
• Business Logic– Share web scripts to generate UI and handle form
posts– Repo web scripts to handle JSON POSTs that
create new data (file upload, new to do)– Repo web scripts to handle GETs that retrieve
existing data (chunks for a given category, to do list info)
– JavaScript for all web-tier and repo-tier web script controllers (fast dev, cuts down on restarts)
© Copyright 2010, Metaversant Group, Inc. | http://www.metaversant.com
Demo: A tale of two frameworks
• Share site• Data list content model• Surf pages & web
scripts (XML, FreeMarker, JavaScript)
• Repository web scripts (minimal)
• Surf config– Alfresco user factory
• Share config (minimal)
• RDB back-end– Schema managed by
Django• Python classes
– Model– Controllers (“Views”)– Forms
• URL mapping• Admin UI
© Copyright 2010, Metaversant Group, Inc. | http://www.metaversant.com
Work Remaining
• Add file upload to both Django and Alfresco To Do’s– Django has a File type– Django supports custom File Managers (Hello,
CMIS!)– Refactor django-alfresco to use CMIS; e.g.,
CmisDocument type• Add “categorizable chunk” to both• Search• Friends list
Source: jphilipg
© Copyright 2010, Metaversant Group, Inc. | http://www.metaversant.com
Comparison
• Both have decent tooling– pydev for Django– Eclipse/STS, Roo, Maven, Ant
for Alfresco• Model, forms, query much easier
in Django• “Learning where stuff goes”
– Much faster in Django• Surf documentation is “still
evolving”
Source: TheBusyBrain
To Do Demo App
Alfresco Django
Number of files 75 23Alfresco # of Files by Type
Django # of Files by Type
© Copyright 2010, Metaversant Group, Inc. | http://www.metaversant.com
Comparison (cont’d)
• Gotchas– Lack of query-able associations
in DM repo was painful– Add user to Share site on user
registration post– Create a rule on the data list
folder to set owner– Keep track of assignee
add/remove
• Attempt to simplify actually made Surf site harder– Forms without JavaScript– No pickers– Not fully leveraging Alfresco’s
form service
Source: automania
To Do Demo Alfresco Django
Lines of Code 1,578 674
Alfresco LOC by Type
Django LOC by Type
HEAVY SHARE CUSTOMIZATION
© Copyright 2010, Metaversant Group, Inc. | http://www.metaversant.com
A real-world example
• SaaS platform wants a community site with resources their customers need to leverage the platform better
• Content chunks & files• Discussion threads, blogs• Everything tagged against multiple taxonomies• Consumer UI• Content Management / Admin UI
© Copyright 2010, Metaversant Group, Inc. | http://www.metaversant.com
Architecture
Lightly customized Share
Admin UI Consumer UIHeavily
customized Share
•Content Model•Behaviors•Rules•Web Scripts
•Theme•YUI•Form Service•Web Scripts
Content Managers Community Users
© Copyright 2010, Metaversant Group, Inc. | http://www.metaversant.com
Data Model
Global Share Site
Client Share Sites
Project Share Sites
Users & Groups
Client ID on cm:user
One group per client
Client Data List
Snippets & Resources
Categories
Categories for products, topics, processes
Project Data List
Client Logos
Process State Data List
User-uploaded Content
Share Site HierarchyAdmin Data
Team Data List
© Copyright 2010, Metaversant Group, Inc. | http://www.metaversant.com
Content Consumer UI
© Copyright 2010, Metaversant Group, Inc. | http://www.metaversant.com
Content Chunks
© Copyright 2010, Metaversant Group, Inc. | http://www.metaversant.com
File Resources
© Copyright 2010, Metaversant Group, Inc. | http://www.metaversant.com
© Copyright 2010, Metaversant Group, Inc. | http://www.metaversant.com
Administrative UI
© Copyright 2010, Metaversant Group, Inc. | http://www.metaversant.com
Customization by the Numbers
t = 17,153 lines t = 6,552 lines
t = 192,925 lines t = 118,586 lines
Share LOC Share JS
CustomizationLOC
CustomizationJS
© Copyright 2010, Metaversant Group, Inc. | http://www.metaversant.com
Conclusions & Advice
• Use JavaScript controllers• Turn on Share client debug• Develop on two Tomcats• Don’t deploy to Share as a JAR• Incorporate minification into your build up-
front• Learn something about YUI, JavaScript
closures• Extend, override, augment. Replace as last
resort• Actions, Behaviors, & Form Filters are your
friends
Source: hiram_college
Helpful tools:•curl•Firebug•Tamper Data•www.jsonlint.com•Rhino command-line
Helpful tools:•curl•Firebug•Tamper Data•www.jsonlint.com•Rhino command-line
© Copyright 2010, Metaversant Group, Inc. | http://www.metaversant.com
Get Involved!
• Apache Chemistry - http://incubator.apache.org/chemistry/
– OpenCMIS– cmislib– PHP CMIS Client
• Django Framework– http://www.djangoproject.com/– Django 1.0 Web Site Development (Packt), Ayman Hourieh
• django-alfresco on Google Code (non-CMIS)– http://code.google.com/p/django-alfresco/
• Drupal CMIS Module– http://drupal.org/project/cmis
• Spring Surf– http://www.springsurf.org
THANK YOU!Tweet me with questions/feedback @jeffpotts01
Get up to speed and stay informed with Alfresco news, tips, & tutorials!
Recommended