SlideShare une entreprise Scribd logo
1  sur  44
Télécharger pour lire hors ligne
Views and viewlets for
  Plone 3.0 Product
    Development
        Tom Lazar
Who are you?

• Not a veteran Zope Developer
• A Plone integrator with Zope/Plone 2.x
  experience (CMF based skins)
• Entirely new to Plone (Congratulations!)
• Sceptical and / or confused about this stuff
Who am I?
• Self-employed Developer, Sysad and
  Consultant, based in Berlin, Germany
• Started working with Plone in 2003
• Contributor since April 2006 (Archipelago
  Sprint, where development of 3.0 started)
• Started Plone 3.0 based product in March
  2007, following trunk
History I
            CMF skins
• A skin is a collection of templates and
  ressources (CSS, JS, images, scripts, macros)
• CMF skins have ordered layers
• customization = copy to a higher layer
• Acquisition based lookup (“Which template
  is producing this markup?!”)
History II
              Macros

• re-use sub-page templates (navigation, etc.)
• Aquisition based lookup (“Which macro is
  really being called?!”)
• Clumsy parameter handling
History III
              Slots
• “Macros on steroids”
• i.e. add CSS links to header from body
  template, advanced loops
• extremely powerful
• only one macro per slot, though
History III
        Python scripts

• security limitations (can’t use
  unrestrictedSearchResults, can’t
  import all packages etc.)
• cumbersome to debug (enablesettrace)
• Clumsy parameter handling (pyflakes)
History III
        Python scripts

• No subclassing (let alone interfaces)
• Acquisition based lookup (“Which script is
  being called?!”)
• hard (impossible?) to test
History IV
            The Good
• very straightforward: copy, edit, reload
• very flexible: easy to override defaults
• instant gratification (TTW support)
• large factor in Zope 2’s success
History V
             The Bad
• “implicit magic”: WTF is going on here?!
• tendency to overload templates with
  application logic (‘dummy’ calls, anyone?)
• “maze of python scripts”
• in summary: “messy”
History VI
  The Ugly
“Acquisition is a
jealous mistress”
                   Martin Aspeli



     ‘Nuff said!
Enter Zope3
• Rewritten from the ground up
• “Component Architecture”: Interfaces,
  Adapters... and Views
• pluggable via configuration a.k.a.“XML sit-
  ups” (Grok is changing that, though)
• lots of other Goodies (buildout, eggs,
  events)
Benefits of the CA

• makes it easy to write small, specific, easy
  to understand code
• re-usability
• explicit (it never guesses! no magic!)
portalstatusmessages
Interface                                            Adapter
from zope.interface import Interface, Attribute
                                                     from base64 import encodestring, decodestring
                                                     from pickle import dumps, loads
                                                     import sys

                                                     from zope.annotation.interfaces import IAnnotations
                                                     from zope.i18n import translate
                                                     from zope.interface import implements

class IMessage(Interface):                           from Products.statusmessages import STATUSMESSAGEKEY
                                                     from Products.statusmessages.message import Message

    quot;quot;quot;A single status message.quot;quot;quot;                   from Products.statusmessages.interfaces import IStatusMessage

                                                     import logging
                                                     logger = logging.getLogger('statusmessages')

                                                     class StatusMessage(object):


    message = Attribute('The text of this message.
                                                         quot;quot;quot;Adapter for the BrowserRequest to handle status messages.

                                                         Let's make sure that this implementation actually fulfills the


Usally a Message object.')
                                                         'IStatusMessage' API.

                                                           >>> from zope.interface.verify import verifyClass
                                                           >>> verifyClass(IStatusMessage, StatusMessage)
                                                           True
                                                         quot;quot;quot;
                                                         implements(IStatusMessage)

    type = Attribute('The type of this message.')        def __init__(self, context):
                                                             self.context = context # the context must be the request

                                                         def addStatusMessage(self, text, type=''):
                                                             quot;quot;quot;Add a status message.
                                                             quot;quot;quot;
                                                             text = translate(text, context=self.context)
                                                             annotations = IAnnotations(self.context)


class IStatusMessage(Interface):                             old = annotations.get(STATUSMESSAGEKEY, self.context.cookies.get(STATUSMESSAGEKEY))
                                                             value = _encodeCookieValue(text, type, old=old)


    quot;quot;quot;An adapter for the BrowserRequest to handle
                                                             self.context.RESPONSE.setCookie(STATUSMESSAGEKEY, value, path='/')
                                                             annotations[STATUSMESSAGEKEY] = value

                                                         def showStatusMessages(self):

status messages.quot;quot;quot;                                          quot;quot;quot;Removes all status messages and returns them for display.
                                                             quot;quot;quot;
                                                             annotations = IAnnotations(self.context)
                                                             value = annotations.get(STATUSMESSAGEKEY, self.context.cookies.get(STATUSMESSAGEKEY))
                                                             if value is None:
                                                                 return []

    def addStatusMessage(text, type=''):                     value = _decodeCookieValue(value)
                                                             # clear the existing cookie entries
                                                             self.context.cookies[STATUSMESSAGEKEY] = None

        quot;quot;quot;Add a status message.quot;quot;quot;                          self.context.RESPONSE.expireCookie(STATUSMESSAGEKEY, path='/')
                                                             annotations[STATUSMESSAGEKEY] = None
                                                             return value

                                                     def _encodeCookieValue(text, type, old=None):
                                                         quot;quot;quot;Encodes text and type to a list of Messages. If there is already some old


    def showStatusMessages():
                                                             existing list, add the new Message at the end but don't add duplicate
                                                             messages.
                                                         quot;quot;quot;
                                                         results = []

        quot;quot;quot;Removes all status messages and returns       message = Message(text, type=type)

                                                         if old is not None:

them for display.                                            results = _decodeCookieValue(old)
                                                         if not message in results:
                                                             results.append(message)

        quot;quot;quot;                                              # we have to remove any newlines or the cookie value will be invalid
                                                         return encodestring(dumps(results)).replace('n','')

                                                     def _decodeCookieValue(string):
                                                         quot;quot;quot;Decode a cookie value to a list of Messages.
                                                             The value has to be a base64 encoded pickle of a list of Messages. If it
                                                             contains anything else, it will be ignored for security reasons.
                                                         quot;quot;quot;
                                                         results = []
                                                         # Return nothing if the cookie is marked as deleted
                                                         if string == 'deleted':
                                                              return results
                                                         # Try to decode the cookie value
                                                         try:
                                                              values = loads(decodestring(string))
                                                         except: # If there's anything unexpected in the string ignore it
                                                              logger.log(logging.ERROR, '%s n%s',
                                                                         'Unexpected value in statusmessages cookie',
                                                                         sys.exc_value
                                                                         )
                                                              return []
                                                         if isinstance(values, list): # simple security check
                                                              for value in values:
                                                                  if isinstance(value, Message): # and another simple check
                                                                      results.append(value)
                                                         return results
Views + Templates
        Adapters
    (Application Logic)



           View



        Template
Views + Templates


 Let’s dumb this down further ;-)
Views + Templates

• Think “View = Dictionary”
• Views collect data from the application into
  a dictionary
• Templates take the data from the dictionary
  and insert it into the markup
Views + Templates

• Templates insert, replace and loop
  (conditionally) – keep them ‘dumb’
• use methods (with @property, @memoize
  at your discretion)

• use kw dictionary for simple attributes:
  kw.update(‘foo’, bar)
Views + Templates

• Templates insert, replace and loop
  (conditionally) – keep them ‘dumb’
• use methods (with @property, @memoize
  at your discretion)

• use kw dictionary for simple attributes:
  kw.update(‘foo’, bar)
Views + Templates


       Demo
Views + Templates

• avoid using self.foo for attributes (for
  consistency’s sake)
• python expressions in templates are not
  inherently evil! (only to abuse them is)
• python expressions are ca. 3x faster than
  path expressions!
On to the
practical stuff

    Questions?
A Simple View
from Products.Five.browser import BrowserView

class MyView(BrowserView):

    def __call__(self, *args, **kw):

        kw.update({‘foo’ : self.bar()})

        return super(MyView, self).__call__(*args, **kw)

    @property

    def some_value(self):

        return something_complicated()
A Simple Viewlet
from Products.Five.browser import BrowserView

class MyView(BrowserView):

    def __call__(self, *args, **kw):

        kw.update({‘foo’ : self.bar()})

        return super(MyView, self).__call__(*args, **kw)

    @property

    def some_value(self):

        return something_complicated()
Debugging Views
from Products.Five.browser import BrowserView

class MyView(BrowserView):

    def __call__(self, *args, **kw):

        import pdb ; pdb.set_trace()

        return super(MyView, self).__call__(*args, **kw)
Context
     from Acquisition import aq_inner

     from Products.Five.browser import BrowserView

     class MyView(BrowserView):

          def some_view_function(self):

               context = aq_inner(self.context)



 “If you forget the aq_inner() it will probably still work 9 times out of 10,
but the 10th time you're screwed and wondering why you're getting insane
         errors about user folders and attributes not being found.”
                                                                     Martin Aspeli
View Interfaces
   • a contract between developer and designer
from zope.interface import Interface
from zope.interface import implements
from Products.Five.browser import BrowserView

class ISummaryPage(Interface):
  def summary():
    pass
  def body():
    pass

class IntroPageView(BrowserView):
    implements(ISummaryPage)

 def summary(self):
   return foo()
Dos and Don’ts

• don’t use __init__()
• use __call__()
• don’t use context/foo (expensive!)
• use kw + options/foo and/or Interfaces
Hack No. 1
    View + skin template
    For extra flexibility you can hook up a Five
         view with a skin based template:

from Acquisition import aq_acquire
from Products.Five.browser import BrowserView

class IntroPageView(BrowserView):
    def __call__(self, *args, **kw):
        kw.update({'some_name' : nifty_method()})
        return aq_acquire(self.context, 'intro-page')(**kw)
Skin switching
• Use a different skin depending on URL
• Useful for editing sites with heavily customized
  public skin
• For example: use Plone’s default skin when
  accessed via HTTPS
• Old method with External Method and ‘Set
    Access Rule’ no longer works (plone.theme
    marks request with default skin before rule is
    processed)
•   New method is actually easier and less code
Skin switching
 The Code
def setskin(site, event):
    if event.request.URL.startswith('https'):
        site.changeSkin(“Plone Default”, event.request)



 The Glue
<subscriber
    for=quot;Products.CMFPlone.interfaces.IPloneSiteRoot
         zope.app.publication.interfaces.IBeforeTraverseEventquot;
    handler=quot;.skinswitcher.setskinquot; />
Caching

  Two basic approaches:
• cache results of view methods: memoize
• cache entire view/viewlet: lovely.viewcache
Caching
from plone.memoize.instance import memoize
from Products.Five.browser import BrowserView
class MyView(BrowserView):

   @memoize

   def expensiveMethod(self):

    return foo
lovely.viewcache
from lovely.viewcache.view import
cachedView

class View(BrowserView):
    pass

CachedView = cachedView(View)

Now use CachedView instead of View in your .zcml
       Works just the same for Viewlets
lovely.viewcache

• Entire page usually not cacheable
• But caching viewlets is particularly useful
• By using views and viewlets you futureproof
  your product for later scalability!
subclass BrowserView

• common methods for all your views
• group and share similar behaviour
• hierarchy easier to debug than Acquisition!
Hack No. 2: custom
           viewlet per page
     Usecase: custom logo viewlet for frontpage only.
     Didn’t want to add extra logic to view class and
                  template just for that.
        Solution: custom template for frontpage
<browser:viewlet
    name=quot;plone.logoquot;
    manager=quot;plone.app.layout.viewlets.interfaces.IPortalHeaderquot;
    class=quot;.viewlets.LogoViewletquot;
    permission=quot;zope2.Viewquot;
    layer=quot;.interfaces.IMySkinSpecificquot;
    />
Hack No. 2: custom
           viewlet per page
from Products.CMFPlone.interfaces import IPloneSiteRoot
from plone.app.layout.viewlets.common import LogoViewlet as
LogoViewletBase
class LogoViewlet(LogoViewletBase):

    _template = ViewPageTemplateFile('logo.pt')
    _frontpage_template = ViewPageTemplateFile('logo-frontpage.pt')

    def render(self):
        if IPloneSiteRoot.providedBy(self.context):
            return self._frontpage_template()
        else:
            return self._template()
Hack No. 2: custom
           viewlet per page
               That’s stupid, don’t do that!
     Viewlet accepts view attribute, problem solved!

<browser:viewlet
    name=quot;plone.logoquot;
    manager=quot;plone.app.layout.viewlets.interfaces.IPortalHeaderquot;
    class=quot;.viewlets.FrontpageLogoViewletquot;
    permission=quot;zope2.Viewquot;
    layer=quot;.interfaces.IMySkinSpecificquot;
    view=”IFrontpageView”
    />
Fun with Views

• Remember:Views are just multi-adapters
  that return (in most cases) HTML
• ZPT is just a default behavior
• Let’s use... Genshi
Genshi
from zope.publisher.browser import BrowserPage
class HelloGenshi(BrowserPage):
    def __call__(self):
        tmpl = loader.load('helloworld.html')
        stream = tmpl.generate(
            request=self.request,
            who=self.request.principal.id
        )
        return stream.render('html',
            doctype='html')
Genshi
<div xmlns:py=quot;http://genshi.edgewall.org/quot;>
    <p>Hello $who</p>
    <p>Hello ${request.principal.id}</p>
    <table>
      <tr py:for=quot;key in requestquot;
           py:if=quot;key.startswith('HTTP')quot;>
         <td>$key</td>
         <td>${request[key]}</td>
      </tr>
    </table>
</div>
Genshi

• everything you love about ZPT
• (hardly) anything you hate about ZPT
• also works with non-markup
• still valid XHTML
• less verbose
Thank you.

Contenu connexe

Tendances

Ruby/Rails
Ruby/RailsRuby/Rails
Ruby/Railsrstankov
 
«Objective-C Runtime в примерах» — Алексей Сторожев, e-Legion
«Objective-C Runtime в примерах» — Алексей Сторожев, e-Legion«Objective-C Runtime в примерах» — Алексей Сторожев, e-Legion
«Objective-C Runtime в примерах» — Алексей Сторожев, e-Legione-Legion
 
Actionsscript Cheat Sheet Letter
Actionsscript Cheat Sheet LetterActionsscript Cheat Sheet Letter
Actionsscript Cheat Sheet Letterguest2a6b08
 
Actionsscript cheat sheet_letter
Actionsscript cheat sheet_letterActionsscript cheat sheet_letter
Actionsscript cheat sheet_letterRadik Setagalih
 
Backbone js
Backbone jsBackbone js
Backbone jsrstankov
 
jQuery1.2.cheatsheet.v1.0
jQuery1.2.cheatsheet.v1.0jQuery1.2.cheatsheet.v1.0
jQuery1.2.cheatsheet.v1.0guest644d1d
 
JQuery New Evolution
JQuery New EvolutionJQuery New Evolution
JQuery New EvolutionAllan Huang
 
Single page webapps & javascript-testing
Single page webapps & javascript-testingSingle page webapps & javascript-testing
Single page webapps & javascript-testingsmontanari
 
Data20161007
Data20161007Data20161007
Data20161007capegmail
 

Tendances (11)

Ruby/Rails
Ruby/RailsRuby/Rails
Ruby/Rails
 
«Objective-C Runtime в примерах» — Алексей Сторожев, e-Legion
«Objective-C Runtime в примерах» — Алексей Сторожев, e-Legion«Objective-C Runtime в примерах» — Алексей Сторожев, e-Legion
«Objective-C Runtime в примерах» — Алексей Сторожев, e-Legion
 
Actionsscript Cheat Sheet Letter
Actionsscript Cheat Sheet LetterActionsscript Cheat Sheet Letter
Actionsscript Cheat Sheet Letter
 
Actionsscript cheat sheet_letter
Actionsscript cheat sheet_letterActionsscript cheat sheet_letter
Actionsscript cheat sheet_letter
 
Backbone js
Backbone jsBackbone js
Backbone js
 
Prototype Framework
Prototype FrameworkPrototype Framework
Prototype Framework
 
jQuery1.2.cheatsheet.v1.0
jQuery1.2.cheatsheet.v1.0jQuery1.2.cheatsheet.v1.0
jQuery1.2.cheatsheet.v1.0
 
JQuery New Evolution
JQuery New EvolutionJQuery New Evolution
JQuery New Evolution
 
Single page webapps & javascript-testing
Single page webapps & javascript-testingSingle page webapps & javascript-testing
Single page webapps & javascript-testing
 
Data20161007
Data20161007Data20161007
Data20161007
 
Understanding redux
Understanding reduxUnderstanding redux
Understanding redux
 

En vedette

Plone3 Generateur d'applications et gestion de contenu 2.0
Plone3 Generateur d'applications et gestion de contenu 2.0Plone3 Generateur d'applications et gestion de contenu 2.0
Plone3 Generateur d'applications et gestion de contenu 2.0Paris, France
 
zc.buildout para desarrolladores Plone
zc.buildout para desarrolladores Plonezc.buildout para desarrolladores Plone
zc.buildout para desarrolladores PloneRoberto Allende
 
New in Plone 3.3. What to expect from Plone 4
New in Plone 3.3. What to expect from Plone 4New in Plone 3.3. What to expect from Plone 4
New in Plone 3.3. What to expect from Plone 4Quintagroup
 
Ruby On Rails Tutorial
Ruby On Rails TutorialRuby On Rails Tutorial
Ruby On Rails Tutorialsunniboy
 
PECL Picks - Extensions to make your life better
PECL Picks - Extensions to make your life betterPECL Picks - Extensions to make your life better
PECL Picks - Extensions to make your life betterZendCon
 
Plone 4 and 5, plans and progress
Plone 4 and 5, plans and progressPlone 4 and 5, plans and progress
Plone 4 and 5, plans and progressGeir Bækholt
 

En vedette (10)

Plone3 Generateur d'applications et gestion de contenu 2.0
Plone3 Generateur d'applications et gestion de contenu 2.0Plone3 Generateur d'applications et gestion de contenu 2.0
Plone3 Generateur d'applications et gestion de contenu 2.0
 
zc.buildout para desarrolladores Plone
zc.buildout para desarrolladores Plonezc.buildout para desarrolladores Plone
zc.buildout para desarrolladores Plone
 
Plone@tigem
Plone@tigemPlone@tigem
Plone@tigem
 
New in Plone 3.3. What to expect from Plone 4
New in Plone 3.3. What to expect from Plone 4New in Plone 3.3. What to expect from Plone 4
New in Plone 3.3. What to expect from Plone 4
 
Struts N E W
Struts N E WStruts N E W
Struts N E W
 
Ruby On Rails Tutorial
Ruby On Rails TutorialRuby On Rails Tutorial
Ruby On Rails Tutorial
 
PECL Picks - Extensions to make your life better
PECL Picks - Extensions to make your life betterPECL Picks - Extensions to make your life better
PECL Picks - Extensions to make your life better
 
Informix and PHP
Informix and PHPInformix and PHP
Informix and PHP
 
Plone 4 and 5, plans and progress
Plone 4 and 5, plans and progressPlone 4 and 5, plans and progress
Plone 4 and 5, plans and progress
 
Show cooking 1
Show cooking 1Show cooking 1
Show cooking 1
 

Similaire à Tom Lazar Using Zope3 Views And Viewlets For Plone 3.0 Product Development

Testing your javascript code with jasmine
Testing your javascript code with jasmineTesting your javascript code with jasmine
Testing your javascript code with jasmineRubyc Slides
 
Jython: Python para la plataforma Java (EL2009)
Jython: Python para la plataforma Java (EL2009)Jython: Python para la plataforma Java (EL2009)
Jython: Python para la plataforma Java (EL2009)Leonardo Soto
 
Vbscript reference
Vbscript referenceVbscript reference
Vbscript referenceRahul Ranjan
 
Jython: Python para la plataforma Java (JRSL 09)
Jython: Python para la plataforma Java (JRSL 09)Jython: Python para la plataforma Java (JRSL 09)
Jython: Python para la plataforma Java (JRSL 09)Leonardo Soto
 
Frank Rodenbaugh Portfolio
Frank Rodenbaugh PortfolioFrank Rodenbaugh Portfolio
Frank Rodenbaugh PortfolioFrankRodenbaugh
 
Unit test candidate solutions
Unit test candidate solutionsUnit test candidate solutions
Unit test candidate solutionsbenewu
 
Hidden Gems in Swift
Hidden Gems in SwiftHidden Gems in Swift
Hidden Gems in SwiftNetguru
 
The Scalactic Way
The Scalactic WayThe Scalactic Way
The Scalactic Waybvenners
 
Backbone.js: Run your Application Inside The Browser
Backbone.js: Run your Application Inside The BrowserBackbone.js: Run your Application Inside The Browser
Backbone.js: Run your Application Inside The BrowserHoward Lewis Ship
 
Barcamp Auckland Rails3 presentation
Barcamp Auckland Rails3 presentationBarcamp Auckland Rails3 presentation
Barcamp Auckland Rails3 presentationSociable
 
Learning Java 4 – Swing, SQL, and Security API
Learning Java 4 – Swing, SQL, and Security APILearning Java 4 – Swing, SQL, and Security API
Learning Java 4 – Swing, SQL, and Security APIcaswenson
 
Practical Experience Building JavaFX Rich Clients
Practical Experience Building JavaFX Rich ClientsPractical Experience Building JavaFX Rich Clients
Practical Experience Building JavaFX Rich ClientsRichard Bair
 
descriptive programming
descriptive programmingdescriptive programming
descriptive programmingAnand Dhana
 
比XML更好用的Java Annotation
比XML更好用的Java Annotation比XML更好用的Java Annotation
比XML更好用的Java Annotationjavatwo2011
 
Swift で JavaScript 始めませんか? #iOSDC
Swift で JavaScript 始めませんか? #iOSDCSwift で JavaScript 始めませんか? #iOSDC
Swift で JavaScript 始めませんか? #iOSDCTomohiro Kumagai
 
Scripting as a Second Language
Scripting as a Second LanguageScripting as a Second Language
Scripting as a Second LanguageRob Dunn
 
Fabric.js @ Falsy Values
Fabric.js @ Falsy ValuesFabric.js @ Falsy Values
Fabric.js @ Falsy ValuesJuriy Zaytsev
 
前端MVC 豆瓣说
前端MVC 豆瓣说前端MVC 豆瓣说
前端MVC 豆瓣说Ting Lv
 

Similaire à Tom Lazar Using Zope3 Views And Viewlets For Plone 3.0 Product Development (20)

Testing your javascript code with jasmine
Testing your javascript code with jasmineTesting your javascript code with jasmine
Testing your javascript code with jasmine
 
Jython: Python para la plataforma Java (EL2009)
Jython: Python para la plataforma Java (EL2009)Jython: Python para la plataforma Java (EL2009)
Jython: Python para la plataforma Java (EL2009)
 
Vbscript reference
Vbscript referenceVbscript reference
Vbscript reference
 
Jython: Python para la plataforma Java (JRSL 09)
Jython: Python para la plataforma Java (JRSL 09)Jython: Python para la plataforma Java (JRSL 09)
Jython: Python para la plataforma Java (JRSL 09)
 
Frank Rodenbaugh Portfolio
Frank Rodenbaugh PortfolioFrank Rodenbaugh Portfolio
Frank Rodenbaugh Portfolio
 
DataMapper
DataMapperDataMapper
DataMapper
 
Unit test candidate solutions
Unit test candidate solutionsUnit test candidate solutions
Unit test candidate solutions
 
Hidden Gems in Swift
Hidden Gems in SwiftHidden Gems in Swift
Hidden Gems in Swift
 
The Scalactic Way
The Scalactic WayThe Scalactic Way
The Scalactic Way
 
Backbone.js: Run your Application Inside The Browser
Backbone.js: Run your Application Inside The BrowserBackbone.js: Run your Application Inside The Browser
Backbone.js: Run your Application Inside The Browser
 
Barcamp Auckland Rails3 presentation
Barcamp Auckland Rails3 presentationBarcamp Auckland Rails3 presentation
Barcamp Auckland Rails3 presentation
 
Qtp test
Qtp testQtp test
Qtp test
 
Learning Java 4 – Swing, SQL, and Security API
Learning Java 4 – Swing, SQL, and Security APILearning Java 4 – Swing, SQL, and Security API
Learning Java 4 – Swing, SQL, and Security API
 
Practical Experience Building JavaFX Rich Clients
Practical Experience Building JavaFX Rich ClientsPractical Experience Building JavaFX Rich Clients
Practical Experience Building JavaFX Rich Clients
 
descriptive programming
descriptive programmingdescriptive programming
descriptive programming
 
比XML更好用的Java Annotation
比XML更好用的Java Annotation比XML更好用的Java Annotation
比XML更好用的Java Annotation
 
Swift で JavaScript 始めませんか? #iOSDC
Swift で JavaScript 始めませんか? #iOSDCSwift で JavaScript 始めませんか? #iOSDC
Swift で JavaScript 始めませんか? #iOSDC
 
Scripting as a Second Language
Scripting as a Second LanguageScripting as a Second Language
Scripting as a Second Language
 
Fabric.js @ Falsy Values
Fabric.js @ Falsy ValuesFabric.js @ Falsy Values
Fabric.js @ Falsy Values
 
前端MVC 豆瓣说
前端MVC 豆瓣说前端MVC 豆瓣说
前端MVC 豆瓣说
 

Plus de Vincenzo Barone

Sally Kleinfeldt - Plone Application Development Patterns
Sally Kleinfeldt - Plone Application Development PatternsSally Kleinfeldt - Plone Application Development Patterns
Sally Kleinfeldt - Plone Application Development PatternsVincenzo Barone
 
Where's the source, Luke? : How to find and debug the code behind Plone
Where's the source, Luke? : How to find and debug the code behind PloneWhere's the source, Luke? : How to find and debug the code behind Plone
Where's the source, Luke? : How to find and debug the code behind PloneVincenzo Barone
 
ItalianSkin: an improvement in the accessibility of the Plone interface in or...
ItalianSkin: an improvement in the accessibility of the Plone interface in or...ItalianSkin: an improvement in the accessibility of the Plone interface in or...
ItalianSkin: an improvement in the accessibility of the Plone interface in or...Vincenzo Barone
 
How to market Plone the Web2.0 way
How to market Plone the Web2.0 wayHow to market Plone the Web2.0 way
How to market Plone the Web2.0 wayVincenzo Barone
 
Lennart Regebro What Zope Did Wrong (And What To Do Instead)
Lennart Regebro   What Zope Did Wrong (And What To Do Instead)Lennart Regebro   What Zope Did Wrong (And What To Do Instead)
Lennart Regebro What Zope Did Wrong (And What To Do Instead)Vincenzo Barone
 
Wichert Akkerman Plone Deployment Practices The Plone.Org Setup
Wichert Akkerman   Plone Deployment Practices   The Plone.Org SetupWichert Akkerman   Plone Deployment Practices   The Plone.Org Setup
Wichert Akkerman Plone Deployment Practices The Plone.Org SetupVincenzo Barone
 
Philipp Von Weitershausen Untested Code Is Broken Code
Philipp Von Weitershausen   Untested Code Is Broken CodePhilipp Von Weitershausen   Untested Code Is Broken Code
Philipp Von Weitershausen Untested Code Is Broken CodeVincenzo Barone
 
Duco Dokter - Plone for the enterprise market: technical musing on caching, C...
Duco Dokter - Plone for the enterprise market: technical musing on caching, C...Duco Dokter - Plone for the enterprise market: technical musing on caching, C...
Duco Dokter - Plone for the enterprise market: technical musing on caching, C...Vincenzo Barone
 
Rocky Burt Subtyping Unleashed
Rocky Burt   Subtyping UnleashedRocky Burt   Subtyping Unleashed
Rocky Burt Subtyping UnleashedVincenzo Barone
 
Alec Mitchell Relationship Building Defining And Querying Complex Relatio...
Alec Mitchell   Relationship Building   Defining And Querying Complex Relatio...Alec Mitchell   Relationship Building   Defining And Querying Complex Relatio...
Alec Mitchell Relationship Building Defining And Querying Complex Relatio...Vincenzo Barone
 
Wageindicator Foundation: a Case Study
Wageindicator Foundation: a Case StudyWageindicator Foundation: a Case Study
Wageindicator Foundation: a Case StudyVincenzo Barone
 
Xavier Heymans Plone Gov Plone In The Public Sector. Panel Presenting The...
Xavier Heymans   Plone Gov   Plone In The Public Sector. Panel Presenting The...Xavier Heymans   Plone Gov   Plone In The Public Sector. Panel Presenting The...
Xavier Heymans Plone Gov Plone In The Public Sector. Panel Presenting The...Vincenzo Barone
 
Brent Lambert Plone In Education A Case Study Of The Use Of Plone And Educa...
Brent Lambert   Plone In Education A Case Study Of The Use Of Plone And Educa...Brent Lambert   Plone In Education A Case Study Of The Use Of Plone And Educa...
Brent Lambert Plone In Education A Case Study Of The Use Of Plone And Educa...Vincenzo Barone
 
Wichert Akkerman - Plone.Org Infrastructure
Wichert Akkerman - Plone.Org InfrastructureWichert Akkerman - Plone.Org Infrastructure
Wichert Akkerman - Plone.Org InfrastructureVincenzo Barone
 
Philipp Von Weitershausen Plone Age Mammoths, Sabers And Caveen Cant The...
Philipp Von Weitershausen   Plone Age  Mammoths, Sabers And Caveen   Cant The...Philipp Von Weitershausen   Plone Age  Mammoths, Sabers And Caveen   Cant The...
Philipp Von Weitershausen Plone Age Mammoths, Sabers And Caveen Cant The...Vincenzo Barone
 
Denis Mishunov Making Plone Theme 10 Most Wanted Tips
Denis Mishunov   Making Plone Theme   10 Most Wanted Tips Denis Mishunov   Making Plone Theme   10 Most Wanted Tips
Denis Mishunov Making Plone Theme 10 Most Wanted Tips Vincenzo Barone
 
Duncan Booth Kupu, Past Present And Future
Duncan Booth   Kupu, Past Present And FutureDuncan Booth   Kupu, Past Present And Future
Duncan Booth Kupu, Past Present And FutureVincenzo Barone
 
Jeroen Vloothuis Bend Kss To Your Will
Jeroen Vloothuis   Bend Kss To Your WillJeroen Vloothuis   Bend Kss To Your Will
Jeroen Vloothuis Bend Kss To Your WillVincenzo Barone
 
Jared Whitlock Open Source In The Enterprise Plone @ Novell
Jared Whitlock   Open Source In The Enterprise    Plone @ NovellJared Whitlock   Open Source In The Enterprise    Plone @ Novell
Jared Whitlock Open Source In The Enterprise Plone @ NovellVincenzo Barone
 
Paul Everitt Community And Foundation Plones Past, Present, Future
Paul Everitt   Community And Foundation   Plones Past, Present, Future Paul Everitt   Community And Foundation   Plones Past, Present, Future
Paul Everitt Community And Foundation Plones Past, Present, Future Vincenzo Barone
 

Plus de Vincenzo Barone (20)

Sally Kleinfeldt - Plone Application Development Patterns
Sally Kleinfeldt - Plone Application Development PatternsSally Kleinfeldt - Plone Application Development Patterns
Sally Kleinfeldt - Plone Application Development Patterns
 
Where's the source, Luke? : How to find and debug the code behind Plone
Where's the source, Luke? : How to find and debug the code behind PloneWhere's the source, Luke? : How to find and debug the code behind Plone
Where's the source, Luke? : How to find and debug the code behind Plone
 
ItalianSkin: an improvement in the accessibility of the Plone interface in or...
ItalianSkin: an improvement in the accessibility of the Plone interface in or...ItalianSkin: an improvement in the accessibility of the Plone interface in or...
ItalianSkin: an improvement in the accessibility of the Plone interface in or...
 
How to market Plone the Web2.0 way
How to market Plone the Web2.0 wayHow to market Plone the Web2.0 way
How to market Plone the Web2.0 way
 
Lennart Regebro What Zope Did Wrong (And What To Do Instead)
Lennart Regebro   What Zope Did Wrong (And What To Do Instead)Lennart Regebro   What Zope Did Wrong (And What To Do Instead)
Lennart Regebro What Zope Did Wrong (And What To Do Instead)
 
Wichert Akkerman Plone Deployment Practices The Plone.Org Setup
Wichert Akkerman   Plone Deployment Practices   The Plone.Org SetupWichert Akkerman   Plone Deployment Practices   The Plone.Org Setup
Wichert Akkerman Plone Deployment Practices The Plone.Org Setup
 
Philipp Von Weitershausen Untested Code Is Broken Code
Philipp Von Weitershausen   Untested Code Is Broken CodePhilipp Von Weitershausen   Untested Code Is Broken Code
Philipp Von Weitershausen Untested Code Is Broken Code
 
Duco Dokter - Plone for the enterprise market: technical musing on caching, C...
Duco Dokter - Plone for the enterprise market: technical musing on caching, C...Duco Dokter - Plone for the enterprise market: technical musing on caching, C...
Duco Dokter - Plone for the enterprise market: technical musing on caching, C...
 
Rocky Burt Subtyping Unleashed
Rocky Burt   Subtyping UnleashedRocky Burt   Subtyping Unleashed
Rocky Burt Subtyping Unleashed
 
Alec Mitchell Relationship Building Defining And Querying Complex Relatio...
Alec Mitchell   Relationship Building   Defining And Querying Complex Relatio...Alec Mitchell   Relationship Building   Defining And Querying Complex Relatio...
Alec Mitchell Relationship Building Defining And Querying Complex Relatio...
 
Wageindicator Foundation: a Case Study
Wageindicator Foundation: a Case StudyWageindicator Foundation: a Case Study
Wageindicator Foundation: a Case Study
 
Xavier Heymans Plone Gov Plone In The Public Sector. Panel Presenting The...
Xavier Heymans   Plone Gov   Plone In The Public Sector. Panel Presenting The...Xavier Heymans   Plone Gov   Plone In The Public Sector. Panel Presenting The...
Xavier Heymans Plone Gov Plone In The Public Sector. Panel Presenting The...
 
Brent Lambert Plone In Education A Case Study Of The Use Of Plone And Educa...
Brent Lambert   Plone In Education A Case Study Of The Use Of Plone And Educa...Brent Lambert   Plone In Education A Case Study Of The Use Of Plone And Educa...
Brent Lambert Plone In Education A Case Study Of The Use Of Plone And Educa...
 
Wichert Akkerman - Plone.Org Infrastructure
Wichert Akkerman - Plone.Org InfrastructureWichert Akkerman - Plone.Org Infrastructure
Wichert Akkerman - Plone.Org Infrastructure
 
Philipp Von Weitershausen Plone Age Mammoths, Sabers And Caveen Cant The...
Philipp Von Weitershausen   Plone Age  Mammoths, Sabers And Caveen   Cant The...Philipp Von Weitershausen   Plone Age  Mammoths, Sabers And Caveen   Cant The...
Philipp Von Weitershausen Plone Age Mammoths, Sabers And Caveen Cant The...
 
Denis Mishunov Making Plone Theme 10 Most Wanted Tips
Denis Mishunov   Making Plone Theme   10 Most Wanted Tips Denis Mishunov   Making Plone Theme   10 Most Wanted Tips
Denis Mishunov Making Plone Theme 10 Most Wanted Tips
 
Duncan Booth Kupu, Past Present And Future
Duncan Booth   Kupu, Past Present And FutureDuncan Booth   Kupu, Past Present And Future
Duncan Booth Kupu, Past Present And Future
 
Jeroen Vloothuis Bend Kss To Your Will
Jeroen Vloothuis   Bend Kss To Your WillJeroen Vloothuis   Bend Kss To Your Will
Jeroen Vloothuis Bend Kss To Your Will
 
Jared Whitlock Open Source In The Enterprise Plone @ Novell
Jared Whitlock   Open Source In The Enterprise    Plone @ NovellJared Whitlock   Open Source In The Enterprise    Plone @ Novell
Jared Whitlock Open Source In The Enterprise Plone @ Novell
 
Paul Everitt Community And Foundation Plones Past, Present, Future
Paul Everitt   Community And Foundation   Plones Past, Present, Future Paul Everitt   Community And Foundation   Plones Past, Present, Future
Paul Everitt Community And Foundation Plones Past, Present, Future
 

Dernier

BAGALUR CALL GIRL IN 98274*61493 ❤CALL GIRLS IN ESCORT SERVICE❤CALL GIRL
BAGALUR CALL GIRL IN 98274*61493 ❤CALL GIRLS IN ESCORT SERVICE❤CALL GIRLBAGALUR CALL GIRL IN 98274*61493 ❤CALL GIRLS IN ESCORT SERVICE❤CALL GIRL
BAGALUR CALL GIRL IN 98274*61493 ❤CALL GIRLS IN ESCORT SERVICE❤CALL GIRLkapoorjyoti4444
 
Quick Doctor In Kuwait +2773`7758`557 Kuwait Doha Qatar Dubai Abu Dhabi Sharj...
Quick Doctor In Kuwait +2773`7758`557 Kuwait Doha Qatar Dubai Abu Dhabi Sharj...Quick Doctor In Kuwait +2773`7758`557 Kuwait Doha Qatar Dubai Abu Dhabi Sharj...
Quick Doctor In Kuwait +2773`7758`557 Kuwait Doha Qatar Dubai Abu Dhabi Sharj...daisycvs
 
Enhancing and Restoring Safety & Quality Cultures - Dave Litwiller - May 2024...
Enhancing and Restoring Safety & Quality Cultures - Dave Litwiller - May 2024...Enhancing and Restoring Safety & Quality Cultures - Dave Litwiller - May 2024...
Enhancing and Restoring Safety & Quality Cultures - Dave Litwiller - May 2024...Dave Litwiller
 
Call Girls Jp Nagar Just Call 👗 7737669865 👗 Top Class Call Girl Service Bang...
Call Girls Jp Nagar Just Call 👗 7737669865 👗 Top Class Call Girl Service Bang...Call Girls Jp Nagar Just Call 👗 7737669865 👗 Top Class Call Girl Service Bang...
Call Girls Jp Nagar Just Call 👗 7737669865 👗 Top Class Call Girl Service Bang...amitlee9823
 
Insurers' journeys to build a mastery in the IoT usage
Insurers' journeys to build a mastery in the IoT usageInsurers' journeys to build a mastery in the IoT usage
Insurers' journeys to build a mastery in the IoT usageMatteo Carbone
 
Falcon's Invoice Discounting: Your Path to Prosperity
Falcon's Invoice Discounting: Your Path to ProsperityFalcon's Invoice Discounting: Your Path to Prosperity
Falcon's Invoice Discounting: Your Path to Prosperityhemanthkumar470700
 
Call Girls In DLf Gurgaon ➥99902@11544 ( Best price)100% Genuine Escort In 24...
Call Girls In DLf Gurgaon ➥99902@11544 ( Best price)100% Genuine Escort In 24...Call Girls In DLf Gurgaon ➥99902@11544 ( Best price)100% Genuine Escort In 24...
Call Girls In DLf Gurgaon ➥99902@11544 ( Best price)100% Genuine Escort In 24...lizamodels9
 
MONA 98765-12871 CALL GIRLS IN LUDHIANA LUDHIANA CALL GIRL
MONA 98765-12871 CALL GIRLS IN LUDHIANA LUDHIANA CALL GIRLMONA 98765-12871 CALL GIRLS IN LUDHIANA LUDHIANA CALL GIRL
MONA 98765-12871 CALL GIRLS IN LUDHIANA LUDHIANA CALL GIRLSeo
 
John Halpern sued for sexual assault.pdf
John Halpern sued for sexual assault.pdfJohn Halpern sued for sexual assault.pdf
John Halpern sued for sexual assault.pdfAmzadHosen3
 
Call Girls From Pari Chowk Greater Noida ❤️8448577510 ⊹Best Escorts Service I...
Call Girls From Pari Chowk Greater Noida ❤️8448577510 ⊹Best Escorts Service I...Call Girls From Pari Chowk Greater Noida ❤️8448577510 ⊹Best Escorts Service I...
Call Girls From Pari Chowk Greater Noida ❤️8448577510 ⊹Best Escorts Service I...lizamodels9
 
0183760ssssssssssssssssssssssssssss00101011 (27).pdf
0183760ssssssssssssssssssssssssssss00101011 (27).pdf0183760ssssssssssssssssssssssssssss00101011 (27).pdf
0183760ssssssssssssssssssssssssssss00101011 (27).pdfRenandantas16
 
How to Get Started in Social Media for Art League City
How to Get Started in Social Media for Art League CityHow to Get Started in Social Media for Art League City
How to Get Started in Social Media for Art League CityEric T. Tung
 
A DAY IN THE LIFE OF A SALESMAN / WOMAN
A DAY IN THE LIFE OF A  SALESMAN / WOMANA DAY IN THE LIFE OF A  SALESMAN / WOMAN
A DAY IN THE LIFE OF A SALESMAN / WOMANIlamathiKannappan
 
Call Girls In Noida 959961⊹3876 Independent Escort Service Noida
Call Girls In Noida 959961⊹3876 Independent Escort Service NoidaCall Girls In Noida 959961⊹3876 Independent Escort Service Noida
Call Girls In Noida 959961⊹3876 Independent Escort Service Noidadlhescort
 
Russian Call Girls In Gurgaon ❤️8448577510 ⊹Best Escorts Service In 24/7 Delh...
Russian Call Girls In Gurgaon ❤️8448577510 ⊹Best Escorts Service In 24/7 Delh...Russian Call Girls In Gurgaon ❤️8448577510 ⊹Best Escorts Service In 24/7 Delh...
Russian Call Girls In Gurgaon ❤️8448577510 ⊹Best Escorts Service In 24/7 Delh...lizamodels9
 
Famous Olympic Siblings from the 21st Century
Famous Olympic Siblings from the 21st CenturyFamous Olympic Siblings from the 21st Century
Famous Olympic Siblings from the 21st Centuryrwgiffor
 
Uneak White's Personal Brand Exploration Presentation
Uneak White's Personal Brand Exploration PresentationUneak White's Personal Brand Exploration Presentation
Uneak White's Personal Brand Exploration Presentationuneakwhite
 
Cracking the Cultural Competence Code.pptx
Cracking the Cultural Competence Code.pptxCracking the Cultural Competence Code.pptx
Cracking the Cultural Competence Code.pptxWorkforce Group
 
Dr. Admir Softic_ presentation_Green Club_ENG.pdf
Dr. Admir Softic_ presentation_Green Club_ENG.pdfDr. Admir Softic_ presentation_Green Club_ENG.pdf
Dr. Admir Softic_ presentation_Green Club_ENG.pdfAdmir Softic
 

Dernier (20)

BAGALUR CALL GIRL IN 98274*61493 ❤CALL GIRLS IN ESCORT SERVICE❤CALL GIRL
BAGALUR CALL GIRL IN 98274*61493 ❤CALL GIRLS IN ESCORT SERVICE❤CALL GIRLBAGALUR CALL GIRL IN 98274*61493 ❤CALL GIRLS IN ESCORT SERVICE❤CALL GIRL
BAGALUR CALL GIRL IN 98274*61493 ❤CALL GIRLS IN ESCORT SERVICE❤CALL GIRL
 
Quick Doctor In Kuwait +2773`7758`557 Kuwait Doha Qatar Dubai Abu Dhabi Sharj...
Quick Doctor In Kuwait +2773`7758`557 Kuwait Doha Qatar Dubai Abu Dhabi Sharj...Quick Doctor In Kuwait +2773`7758`557 Kuwait Doha Qatar Dubai Abu Dhabi Sharj...
Quick Doctor In Kuwait +2773`7758`557 Kuwait Doha Qatar Dubai Abu Dhabi Sharj...
 
Enhancing and Restoring Safety & Quality Cultures - Dave Litwiller - May 2024...
Enhancing and Restoring Safety & Quality Cultures - Dave Litwiller - May 2024...Enhancing and Restoring Safety & Quality Cultures - Dave Litwiller - May 2024...
Enhancing and Restoring Safety & Quality Cultures - Dave Litwiller - May 2024...
 
Call Girls Jp Nagar Just Call 👗 7737669865 👗 Top Class Call Girl Service Bang...
Call Girls Jp Nagar Just Call 👗 7737669865 👗 Top Class Call Girl Service Bang...Call Girls Jp Nagar Just Call 👗 7737669865 👗 Top Class Call Girl Service Bang...
Call Girls Jp Nagar Just Call 👗 7737669865 👗 Top Class Call Girl Service Bang...
 
Insurers' journeys to build a mastery in the IoT usage
Insurers' journeys to build a mastery in the IoT usageInsurers' journeys to build a mastery in the IoT usage
Insurers' journeys to build a mastery in the IoT usage
 
Falcon's Invoice Discounting: Your Path to Prosperity
Falcon's Invoice Discounting: Your Path to ProsperityFalcon's Invoice Discounting: Your Path to Prosperity
Falcon's Invoice Discounting: Your Path to Prosperity
 
Call Girls In DLf Gurgaon ➥99902@11544 ( Best price)100% Genuine Escort In 24...
Call Girls In DLf Gurgaon ➥99902@11544 ( Best price)100% Genuine Escort In 24...Call Girls In DLf Gurgaon ➥99902@11544 ( Best price)100% Genuine Escort In 24...
Call Girls In DLf Gurgaon ➥99902@11544 ( Best price)100% Genuine Escort In 24...
 
MONA 98765-12871 CALL GIRLS IN LUDHIANA LUDHIANA CALL GIRL
MONA 98765-12871 CALL GIRLS IN LUDHIANA LUDHIANA CALL GIRLMONA 98765-12871 CALL GIRLS IN LUDHIANA LUDHIANA CALL GIRL
MONA 98765-12871 CALL GIRLS IN LUDHIANA LUDHIANA CALL GIRL
 
Forklift Operations: Safety through Cartoons
Forklift Operations: Safety through CartoonsForklift Operations: Safety through Cartoons
Forklift Operations: Safety through Cartoons
 
John Halpern sued for sexual assault.pdf
John Halpern sued for sexual assault.pdfJohn Halpern sued for sexual assault.pdf
John Halpern sued for sexual assault.pdf
 
Call Girls From Pari Chowk Greater Noida ❤️8448577510 ⊹Best Escorts Service I...
Call Girls From Pari Chowk Greater Noida ❤️8448577510 ⊹Best Escorts Service I...Call Girls From Pari Chowk Greater Noida ❤️8448577510 ⊹Best Escorts Service I...
Call Girls From Pari Chowk Greater Noida ❤️8448577510 ⊹Best Escorts Service I...
 
0183760ssssssssssssssssssssssssssss00101011 (27).pdf
0183760ssssssssssssssssssssssssssss00101011 (27).pdf0183760ssssssssssssssssssssssssssss00101011 (27).pdf
0183760ssssssssssssssssssssssssssss00101011 (27).pdf
 
How to Get Started in Social Media for Art League City
How to Get Started in Social Media for Art League CityHow to Get Started in Social Media for Art League City
How to Get Started in Social Media for Art League City
 
A DAY IN THE LIFE OF A SALESMAN / WOMAN
A DAY IN THE LIFE OF A  SALESMAN / WOMANA DAY IN THE LIFE OF A  SALESMAN / WOMAN
A DAY IN THE LIFE OF A SALESMAN / WOMAN
 
Call Girls In Noida 959961⊹3876 Independent Escort Service Noida
Call Girls In Noida 959961⊹3876 Independent Escort Service NoidaCall Girls In Noida 959961⊹3876 Independent Escort Service Noida
Call Girls In Noida 959961⊹3876 Independent Escort Service Noida
 
Russian Call Girls In Gurgaon ❤️8448577510 ⊹Best Escorts Service In 24/7 Delh...
Russian Call Girls In Gurgaon ❤️8448577510 ⊹Best Escorts Service In 24/7 Delh...Russian Call Girls In Gurgaon ❤️8448577510 ⊹Best Escorts Service In 24/7 Delh...
Russian Call Girls In Gurgaon ❤️8448577510 ⊹Best Escorts Service In 24/7 Delh...
 
Famous Olympic Siblings from the 21st Century
Famous Olympic Siblings from the 21st CenturyFamous Olympic Siblings from the 21st Century
Famous Olympic Siblings from the 21st Century
 
Uneak White's Personal Brand Exploration Presentation
Uneak White's Personal Brand Exploration PresentationUneak White's Personal Brand Exploration Presentation
Uneak White's Personal Brand Exploration Presentation
 
Cracking the Cultural Competence Code.pptx
Cracking the Cultural Competence Code.pptxCracking the Cultural Competence Code.pptx
Cracking the Cultural Competence Code.pptx
 
Dr. Admir Softic_ presentation_Green Club_ENG.pdf
Dr. Admir Softic_ presentation_Green Club_ENG.pdfDr. Admir Softic_ presentation_Green Club_ENG.pdf
Dr. Admir Softic_ presentation_Green Club_ENG.pdf
 

Tom Lazar Using Zope3 Views And Viewlets For Plone 3.0 Product Development

  • 1. Views and viewlets for Plone 3.0 Product Development Tom Lazar
  • 2. Who are you? • Not a veteran Zope Developer • A Plone integrator with Zope/Plone 2.x experience (CMF based skins) • Entirely new to Plone (Congratulations!) • Sceptical and / or confused about this stuff
  • 3. Who am I? • Self-employed Developer, Sysad and Consultant, based in Berlin, Germany • Started working with Plone in 2003 • Contributor since April 2006 (Archipelago Sprint, where development of 3.0 started) • Started Plone 3.0 based product in March 2007, following trunk
  • 4. History I CMF skins • A skin is a collection of templates and ressources (CSS, JS, images, scripts, macros) • CMF skins have ordered layers • customization = copy to a higher layer • Acquisition based lookup (“Which template is producing this markup?!”)
  • 5. History II Macros • re-use sub-page templates (navigation, etc.) • Aquisition based lookup (“Which macro is really being called?!”) • Clumsy parameter handling
  • 6. History III Slots • “Macros on steroids” • i.e. add CSS links to header from body template, advanced loops • extremely powerful • only one macro per slot, though
  • 7. History III Python scripts • security limitations (can’t use unrestrictedSearchResults, can’t import all packages etc.) • cumbersome to debug (enablesettrace) • Clumsy parameter handling (pyflakes)
  • 8. History III Python scripts • No subclassing (let alone interfaces) • Acquisition based lookup (“Which script is being called?!”) • hard (impossible?) to test
  • 9. History IV The Good • very straightforward: copy, edit, reload • very flexible: easy to override defaults • instant gratification (TTW support) • large factor in Zope 2’s success
  • 10. History V The Bad • “implicit magic”: WTF is going on here?! • tendency to overload templates with application logic (‘dummy’ calls, anyone?) • “maze of python scripts” • in summary: “messy”
  • 11. History VI The Ugly “Acquisition is a jealous mistress” Martin Aspeli ‘Nuff said!
  • 12. Enter Zope3 • Rewritten from the ground up • “Component Architecture”: Interfaces, Adapters... and Views • pluggable via configuration a.k.a.“XML sit- ups” (Grok is changing that, though) • lots of other Goodies (buildout, eggs, events)
  • 13. Benefits of the CA • makes it easy to write small, specific, easy to understand code • re-usability • explicit (it never guesses! no magic!)
  • 14. portalstatusmessages Interface Adapter from zope.interface import Interface, Attribute from base64 import encodestring, decodestring from pickle import dumps, loads import sys from zope.annotation.interfaces import IAnnotations from zope.i18n import translate from zope.interface import implements class IMessage(Interface): from Products.statusmessages import STATUSMESSAGEKEY from Products.statusmessages.message import Message quot;quot;quot;A single status message.quot;quot;quot; from Products.statusmessages.interfaces import IStatusMessage import logging logger = logging.getLogger('statusmessages') class StatusMessage(object): message = Attribute('The text of this message. quot;quot;quot;Adapter for the BrowserRequest to handle status messages. Let's make sure that this implementation actually fulfills the Usally a Message object.') 'IStatusMessage' API. >>> from zope.interface.verify import verifyClass >>> verifyClass(IStatusMessage, StatusMessage) True quot;quot;quot; implements(IStatusMessage) type = Attribute('The type of this message.') def __init__(self, context): self.context = context # the context must be the request def addStatusMessage(self, text, type=''): quot;quot;quot;Add a status message. quot;quot;quot; text = translate(text, context=self.context) annotations = IAnnotations(self.context) class IStatusMessage(Interface): old = annotations.get(STATUSMESSAGEKEY, self.context.cookies.get(STATUSMESSAGEKEY)) value = _encodeCookieValue(text, type, old=old) quot;quot;quot;An adapter for the BrowserRequest to handle self.context.RESPONSE.setCookie(STATUSMESSAGEKEY, value, path='/') annotations[STATUSMESSAGEKEY] = value def showStatusMessages(self): status messages.quot;quot;quot; quot;quot;quot;Removes all status messages and returns them for display. quot;quot;quot; annotations = IAnnotations(self.context) value = annotations.get(STATUSMESSAGEKEY, self.context.cookies.get(STATUSMESSAGEKEY)) if value is None: return [] def addStatusMessage(text, type=''): value = _decodeCookieValue(value) # clear the existing cookie entries self.context.cookies[STATUSMESSAGEKEY] = None quot;quot;quot;Add a status message.quot;quot;quot; self.context.RESPONSE.expireCookie(STATUSMESSAGEKEY, path='/') annotations[STATUSMESSAGEKEY] = None return value def _encodeCookieValue(text, type, old=None): quot;quot;quot;Encodes text and type to a list of Messages. If there is already some old def showStatusMessages(): existing list, add the new Message at the end but don't add duplicate messages. quot;quot;quot; results = [] quot;quot;quot;Removes all status messages and returns message = Message(text, type=type) if old is not None: them for display. results = _decodeCookieValue(old) if not message in results: results.append(message) quot;quot;quot; # we have to remove any newlines or the cookie value will be invalid return encodestring(dumps(results)).replace('n','') def _decodeCookieValue(string): quot;quot;quot;Decode a cookie value to a list of Messages. The value has to be a base64 encoded pickle of a list of Messages. If it contains anything else, it will be ignored for security reasons. quot;quot;quot; results = [] # Return nothing if the cookie is marked as deleted if string == 'deleted': return results # Try to decode the cookie value try: values = loads(decodestring(string)) except: # If there's anything unexpected in the string ignore it logger.log(logging.ERROR, '%s n%s', 'Unexpected value in statusmessages cookie', sys.exc_value ) return [] if isinstance(values, list): # simple security check for value in values: if isinstance(value, Message): # and another simple check results.append(value) return results
  • 15. Views + Templates Adapters (Application Logic) View Template
  • 16. Views + Templates Let’s dumb this down further ;-)
  • 17. Views + Templates • Think “View = Dictionary” • Views collect data from the application into a dictionary • Templates take the data from the dictionary and insert it into the markup
  • 18. Views + Templates • Templates insert, replace and loop (conditionally) – keep them ‘dumb’ • use methods (with @property, @memoize at your discretion) • use kw dictionary for simple attributes: kw.update(‘foo’, bar)
  • 19. Views + Templates • Templates insert, replace and loop (conditionally) – keep them ‘dumb’ • use methods (with @property, @memoize at your discretion) • use kw dictionary for simple attributes: kw.update(‘foo’, bar)
  • 21. Views + Templates • avoid using self.foo for attributes (for consistency’s sake) • python expressions in templates are not inherently evil! (only to abuse them is) • python expressions are ca. 3x faster than path expressions!
  • 22. On to the practical stuff Questions?
  • 23. A Simple View from Products.Five.browser import BrowserView class MyView(BrowserView): def __call__(self, *args, **kw): kw.update({‘foo’ : self.bar()}) return super(MyView, self).__call__(*args, **kw) @property def some_value(self): return something_complicated()
  • 24. A Simple Viewlet from Products.Five.browser import BrowserView class MyView(BrowserView): def __call__(self, *args, **kw): kw.update({‘foo’ : self.bar()}) return super(MyView, self).__call__(*args, **kw) @property def some_value(self): return something_complicated()
  • 25. Debugging Views from Products.Five.browser import BrowserView class MyView(BrowserView): def __call__(self, *args, **kw): import pdb ; pdb.set_trace() return super(MyView, self).__call__(*args, **kw)
  • 26. Context from Acquisition import aq_inner from Products.Five.browser import BrowserView class MyView(BrowserView): def some_view_function(self): context = aq_inner(self.context) “If you forget the aq_inner() it will probably still work 9 times out of 10, but the 10th time you're screwed and wondering why you're getting insane errors about user folders and attributes not being found.” Martin Aspeli
  • 27. View Interfaces • a contract between developer and designer from zope.interface import Interface from zope.interface import implements from Products.Five.browser import BrowserView class ISummaryPage(Interface): def summary(): pass def body(): pass class IntroPageView(BrowserView): implements(ISummaryPage) def summary(self): return foo()
  • 28. Dos and Don’ts • don’t use __init__() • use __call__() • don’t use context/foo (expensive!) • use kw + options/foo and/or Interfaces
  • 29. Hack No. 1 View + skin template For extra flexibility you can hook up a Five view with a skin based template: from Acquisition import aq_acquire from Products.Five.browser import BrowserView class IntroPageView(BrowserView): def __call__(self, *args, **kw): kw.update({'some_name' : nifty_method()}) return aq_acquire(self.context, 'intro-page')(**kw)
  • 30. Skin switching • Use a different skin depending on URL • Useful for editing sites with heavily customized public skin • For example: use Plone’s default skin when accessed via HTTPS • Old method with External Method and ‘Set Access Rule’ no longer works (plone.theme marks request with default skin before rule is processed) • New method is actually easier and less code
  • 31. Skin switching The Code def setskin(site, event): if event.request.URL.startswith('https'): site.changeSkin(“Plone Default”, event.request) The Glue <subscriber for=quot;Products.CMFPlone.interfaces.IPloneSiteRoot zope.app.publication.interfaces.IBeforeTraverseEventquot; handler=quot;.skinswitcher.setskinquot; />
  • 32. Caching Two basic approaches: • cache results of view methods: memoize • cache entire view/viewlet: lovely.viewcache
  • 33. Caching from plone.memoize.instance import memoize from Products.Five.browser import BrowserView class MyView(BrowserView): @memoize def expensiveMethod(self): return foo
  • 34. lovely.viewcache from lovely.viewcache.view import cachedView class View(BrowserView): pass CachedView = cachedView(View) Now use CachedView instead of View in your .zcml Works just the same for Viewlets
  • 35. lovely.viewcache • Entire page usually not cacheable • But caching viewlets is particularly useful • By using views and viewlets you futureproof your product for later scalability!
  • 36. subclass BrowserView • common methods for all your views • group and share similar behaviour • hierarchy easier to debug than Acquisition!
  • 37. Hack No. 2: custom viewlet per page Usecase: custom logo viewlet for frontpage only. Didn’t want to add extra logic to view class and template just for that. Solution: custom template for frontpage <browser:viewlet name=quot;plone.logoquot; manager=quot;plone.app.layout.viewlets.interfaces.IPortalHeaderquot; class=quot;.viewlets.LogoViewletquot; permission=quot;zope2.Viewquot; layer=quot;.interfaces.IMySkinSpecificquot; />
  • 38. Hack No. 2: custom viewlet per page from Products.CMFPlone.interfaces import IPloneSiteRoot from plone.app.layout.viewlets.common import LogoViewlet as LogoViewletBase class LogoViewlet(LogoViewletBase): _template = ViewPageTemplateFile('logo.pt') _frontpage_template = ViewPageTemplateFile('logo-frontpage.pt') def render(self): if IPloneSiteRoot.providedBy(self.context): return self._frontpage_template() else: return self._template()
  • 39. Hack No. 2: custom viewlet per page That’s stupid, don’t do that! Viewlet accepts view attribute, problem solved! <browser:viewlet name=quot;plone.logoquot; manager=quot;plone.app.layout.viewlets.interfaces.IPortalHeaderquot; class=quot;.viewlets.FrontpageLogoViewletquot; permission=quot;zope2.Viewquot; layer=quot;.interfaces.IMySkinSpecificquot; view=”IFrontpageView” />
  • 40. Fun with Views • Remember:Views are just multi-adapters that return (in most cases) HTML • ZPT is just a default behavior • Let’s use... Genshi
  • 41. Genshi from zope.publisher.browser import BrowserPage class HelloGenshi(BrowserPage): def __call__(self): tmpl = loader.load('helloworld.html') stream = tmpl.generate( request=self.request, who=self.request.principal.id ) return stream.render('html', doctype='html')
  • 42. Genshi <div xmlns:py=quot;http://genshi.edgewall.org/quot;> <p>Hello $who</p> <p>Hello ${request.principal.id}</p> <table> <tr py:for=quot;key in requestquot; py:if=quot;key.startswith('HTTP')quot;> <td>$key</td> <td>${request[key]}</td> </tr> </table> </div>
  • 43. Genshi • everything you love about ZPT • (hardly) anything you hate about ZPT • also works with non-markup • still valid XHTML • less verbose