Author: jmorliaguet Date: Thu Jun 15 15:47:51 2006 New Revision: 3416 Added: cpsskins/branches/paris-sprint-2006/doc/portlets.txt (contents, props changed) Removed: cpsskins/branches/paris-sprint-2006/doc/architecture.txt cpsskins/branches/paris-sprint-2006/doc/canvas-rendering.txt cpsskins/branches/paris-sprint-2006/doc/draft-rendering-pipelines.txt cpsskins/branches/paris-sprint-2006/doc/draft-styles.txt cpsskins/branches/paris-sprint-2006/doc/layout-model.png cpsskins/branches/paris-sprint-2006/doc/local-portlets.txt cpsskins/branches/paris-sprint-2006/doc/mockup-style-editor.png cpsskins/branches/paris-sprint-2006/doc/portlet-modes.txt cpsskins/branches/paris-sprint-2006/doc/portlet-ordering.txt cpsskins/branches/paris-sprint-2006/doc/portlet-rendering.png cpsskins/branches/paris-sprint-2006/doc/portlet-rendering.txt cpsskins/branches/paris-sprint-2006/doc/setup.txt cpsskins/branches/paris-sprint-2006/doc/site-designer.txt cpsskins/branches/paris-sprint-2006/doc/theme-layout-model.png cpsskins/branches/paris-sprint-2006/doc/ui-portlet-edit-form.png Modified: cpsskins/branches/paris-sprint-2006/doc/theme-structure.txt cpsskins/branches/paris-sprint-2006/ftests/test_portlet.py cpsskins/branches/paris-sprint-2006/tests/test_renderers.py
Log: - removed old cruft (drafts, outdated documentation, mockups ) - moved all portlet documentation and doctests to doc/portlets.txt Added: cpsskins/branches/paris-sprint-2006/doc/portlets.txt ============================================================================== --- (empty file) +++ cpsskins/branches/paris-sprint-2006/doc/portlets.txt Thu Jun 15 15:47:51 2006 @@ -0,0 +1,475 @@ +================= +PORTLET RENDERING +================= + +This document describes how portlets are rendered by the rendering engine. + +We first set up a test environment: + + >>> from pprint import pprint + >>> from zope.app.testing import ztapi + >>> from zope.component import getMultiAdapter + + >>> from zope.publisher.browser import TestRequest + >>> from zope.publisher.interfaces import IRequest + >>> request = TestRequest() + >>> root = getRootFolder() + +Portlets use a *context info* structure to transmit data through the +rendering chain. This structure is created by the page renderer itself for +every object that needs to be rendered (such as portlets) but since +there is no page renderer in this example, we create a fake info structure: + + >>> from cpsskins.browser.rendering.context import ContextInfo + >>> info = ContextInfo({'data': None, 'metadata': None}) + >>> info.globals = ContextInfo({'request': request, 'location': root}) + + +Portlets +======== + +Portlets are always the first elements in the rendering chain. For that matter +they are *content providers* with regard to the rendering engine. + +They can provide both *unstructured content* (e.g. HTML markup, plain text) and +*structured content* (i.e. data structures implementing specific data schemas). + +Unstructured content +-------------------- + +Unstructured content consists of unicode strings (HTML markup, plain text) + +The Dummy Portlet is an example implementation of a portlet which provides +HTML markup: + + >>> from cpsskins.standard.portlets.dummy.portlet import Dummy \ + ... as DummyPortlet + >>> portlet = DummyPortlet(title=u'Example portlet', + ... text=u'<span>Dummy text.</span>') + + +To get the HTML output from it, we simply call the portlet by passing the info +structure as a parameter: + + >>> portlet(info) + u'<span>Dummy text.</span>' + +In the case of the Dummy Portlet the data structure is left unchanged: + + >>> info.data, info.metadata = None, None + +It is the job of the rendering engine to call the portlet, but since we are +detailing every step, we call the portlet directly to see what it does. + + +Structured content +------------------ + +Structured content consists of data structures that implement specific +schemas. + +The Breadcrumbs Portlet provides structured data in the form of menu items. + + >>> from cpsskins.standard.ds.menuitems import MenuItems + >>> from cpsskins.standard.ds.menuitem import MenuItem + +Let us instanciate a Breadcrumbs Portlet: + + >>> from cpsskins.standard.portlets.breadcrumbs.portlet import Breadcrumbs + >>> portlet = Breadcrumbs() + +The portlet returns no markup: + + >>> portlet(info) is None + True + +but the data structure got updated: + + >>> info.data + <Items> + + >>> isinstance(info.data, MenuItems) + True + +let us see what the MenuItems structure now contains: + + >>> pprint(info.data()) + [<Menu item ''>] + + >>> pprint([item() for item in info.data]) + [{'description': u'', + 'icon': u'', + 'selected': False, + 'title': '', + 'url': 'http://127.0.0.1'}] + +as we see, the Breadcrumbs Portlet provides a list of menu items such as: + + >>> item = MenuItem(title=u'Item 1', url=u'@@url.html', icon=u'') + >>> pprint(item()) + {'description': u'', + 'icon': u'', + 'selected': False, + 'title': u'Item 1', + 'url': u'@@url.html'} + + >>> content = MenuItems([item]) + >>> content() + [<Menu item 'Item 1'>] + + +But structured content cannot be rendered directly in a browser. + +We will see how structured data gets tranformed into HTML with help of +*widgets*. + + +Metadata +-------- + +The 'metadata' present in the context info structure contains data *about* the +portlet. It implements the IMetaData schema which specifies: + + - the portlet's title + + - the portlet's description + + - a URL associated with the portlet + +None of the information above needs to be displayed on the page, however it +can be used to provide information on the portlet. + + +Displaying portlets +=================== + +The data provided by portlets is displayed in *Display Elements* (e.g. a Box), +which represents the fragment of the page in which the portlet will appear. + + >>> from zope.interface.verify import verifyClass + >>> from cpsskins.standard.displays.box import Box + >>> from cpsskins.elements.interfaces import IDisplay + >>> verifyClass(IDisplay, Box) + True + +A same portlet can be displayed in different Display Elements depending on the +context. + +Provided that the portlet is associated with a given Box, the Box is in turn +associated with various Format Element (style, effects, ...) whose +responsibility in the rendering chain is to format elements. + +The type of Format that interests us here is the *widget* Format. + + >>> from cpsskins.standard.formats.widget import Widget + >>> from cpsskins.elements.interfaces import IFormat + >>> verifyClass(IFormat, Widget) + True + +The job of the widget is to convert structured data into HTML, or to format the +HTML markup that it receives from the portlet. + +A list of widget names can be specified, i.e. if the first widget cannot +render the data, the next widget will be used, etc. until a suitable widget +is found. + + >>> widget = Widget([u'standard.special_widget', u'standard.plain_html']) + +As a last resort the *basic widget view* registered by default for the given +data type will be used. It will provide a very minimal formatting but it will +be able to convert the data structure into HTML. + +Note that the widget format described here does not directly produce any HTML +output. It only specifies which widget view to use. Also it is the object that +the user can interact with. + +The portlet is rendered with a widget view (called a 'filter' in the rendering +chain). The widget filter attempts to find a named view for the data +structure, by looking up the different names specified by the widget format. + +We create a fake filter view in order to test the various widgets: + + >>> from cpsskins.standard.filters.widget import WidgetFilter + >>> from cpsskins.standard.formats.widget import IWidget + >>> from cpsskins.browser.rendering.interfaces import IFilterView + + >>> from zope.component import getGlobalSiteManager + >>> gsm = getGlobalSiteManager() + >>> gsm.registerAdapter(WidgetFilter, (IWidget,), IFilterView) + + >>> def test_render(widget, rendered, info): + ... widget_filter = IFilterView(widget) + ... return widget_filter(rendered, info) + + >>> rendered = u'<h1>Test of HTML</h1>' + >>> info.data = None + + >>> test_render(widget, rendered, info) + u'<div class="standardPlainHtml"><h1>Test of HTML</h1></div>' + +Available widgets: +================== + +Widgets displaying menu items +----------------------------- + + >>> itemA = MenuItem(title=u'Item A', url=u'@@urlA.html', icon=u'') + >>> itemB = MenuItem(title=u'Item B', url=u'@@urlB.html', icon=u'') + >>> info.data = MenuItems([itemA, itemB]) + + a) vertical menu + ---------------- + >>> widget = Widget([u'standard.vertical_menu']) + >>> test_render(widget, rendered, info) + u'<ul class="standardVerticalMenu"><li><a href="@@urlA.html">Item A</a></li><li><a href="@@urlB.html">Item B</a></li></ul>' + + b) horizontal tabs + ------------------ + >>> widget = Widget([u'standard.horizontal_tabs']) + >>> test_render(widget, rendered, info) + u'<ul class="standardHorizontalTabs"><li><a href="@@urlA.html">Item A</a></li><li><a href="@@urlB.html">Item B</a></li></ul>' + + c) Menu bar + ------------------- + >>> widget = Widget([u'standard.menu_bar']) + >>> test_render(widget, rendered, info) + u'<div class="standardMenuBar"><a href="@@urlA.html">Item A</a><a href="@@urlB.html">Item B</a></div>' + + d) Horizontal trail + ------------------- + >>> widget = Widget([u'standard.horizontal_trail']) + >>> test_render(widget, rendered, info) + u'<div class="standardHorizontalTrail"><a href="@@urlA.html">Item A</a><span class="sep">></span><a href="@@urlB.html">Item B</a><span class="sep">></span></div>' + + e) Drop-down list + ----------------- + >>> widget = Widget([u'standard.dropdown_list']) + >>> test_render(widget, rendered, info) + u'<form action="@@cpsskins_redirect" method="post" class="standardDropdownList"><select onchange="submit()" name="url"><option value="./"></option><option value="@@urlA.html">Item A</option><option value="@@urlB.html">Item B</option></select></form>' + + +Widgets displaying HTML markup +------------------------------ + + >>> info.data = None + >>> rendered = '<h1>Test of HTML</h1>' + + a) frame box + ------------ + >>> from cpsskins.standard.ds.metadata import MetaData + >>> info.metadata = MetaData(title=u'Example frame box') + >>> widget = Widget([u'standard.frame_box']) + >>> test_render(widget, rendered, info) + u'<fieldset class="standardFrameBox"><legend>Example frame box</legend><div><h1>Test of HTML</h1></div></fieldset>' + + b) horizontal scrollbar + ----------------------- + >>> widget = Widget([u'standard.horizontal_scrollbar']) + >>> test_render(widget, rendered, info) + u'<div style="overflow-x:scroll;" class="standardHorizontalScrollbar"><h1>Test of HTML</h1></div>' + + c) pageblock frame + ------------------ + >>> widget = Widget([u'standard.pageblock_frame']) + >>> test_render(widget, rendered, info) + u'<table summary="" class="standardPageblockFrame"><tr><h1>Test of HTML</h1></tr></table>' + + d) cell frame + ------------- + >>> widget = Widget([u'standard.cell_frame']) + >>> test_render(widget, rendered, info) + u'<td style="vertical-align:top;" class="standardCellFrame"><h1>Test of HTML</h1></td>' + + e) pageblock frame (tableless rendering) + ------------------------------------ + >>> widget = Widget([u'tableless-standard.pageblock_frame']) + >>> test_render(widget, rendered, info) + u'<div class="tablelessStandardPageblockFrame"><h1>Test of HTML</h1><br style="clear:left"/></div><div style="clear:left"></div>' + + f) cell frame (tableless rendering) + --------------------------------- + >>> widget = Widget([u'tableless-standard.cell_frame']) + >>> test_render(widget, rendered, info) + u'<div style="float:left;overflow:hidden" class="tablelessStandardCellFrame"><h1>Test of HTML</h1></div>' + + +Widgets displaying images +------------------------- + + >>> from cpsskins.standard.ds.image import Image + >>> info.data = Image(path=u'/some/image.png') + + a) image + -------- + >>> widget = Widget([u'standard.image']) + >>> test_render(widget, rendered, info) + u'<img src="/some/image.png" alt="" style="border: none" class="standardImage" />' + +[to finish] + + +PORTLET ORDERING +================ + +The portlets displayed in slots need to be ordered. The order information is +stored in the slot's display. + +We set up some test environment: + + >>> from cpsskins.tests.setup import makeSite + >>> from cpsskins.tests.setup import addThemeManager, addThemeSkeleton + >>> root = getRootFolder() + >>> tmutil = addThemeManager(root, makeSite(root)) + +Let us create a theme with two slots ('slot_A', 'slot_B'): + + >>> from cpsskins.elements.slot import Slot + >>> theme = addThemeSkeleton(tmutil) + + >>> slot_A = Slot(slot=u'A') + >>> slot_B = Slot(slot=u'B') + + >>> theme[u'page'][u'block'][u'cell'][u'slot_a'] = slot_A + >>> theme[u'page'][u'block'][u'cell'][u'slot_b'] = slot_B + +The slots A and B have their own display + + >>> from cpsskins.elements.interfaces import IDisplayable + >>> from cpsskins.standard.displays.boxgroup import BoxGroup + + >>> display_slot_A = IDisplayable(slot_A).getDisplay() + >>> display_slot_B = IDisplayable(slot_B).getDisplay() + + >>> isinstance(display_slot_A, BoxGroup) + True + + >>> isinstance(display_slot_B, BoxGroup) + True + + >>> display_slot_A is not display_slot_B + True + + +LOCAL PORTLETS +============== + + >>> from cpsskins.elements.slot import Slot + >>> from cpsskins.elements.portlet import TestPortlet as Portlet + >>> from cpsskins.perspectives import Perspective + +Local portlets are displayed in *slots* + + >>> portlet = Portlet('Some portlet') + >>> portlet.identifier = 1 + +Let us create a slot: + + >>> slot = Slot(slot=u'Right') + >>> slot.identifier = 2 + +And a perspective: + + >>> perspective = Perspective('Some perspective') + >>> perspective.identifier = 3 + +Let us create a relation to connect the slots, the portlet and the perspective: + + >>> from cpsskins.ontology import hasPortletFromPerspective + >>> from cpsskins.relations import TriadicRelation as Triad + + >>> relation = Triad( + ... predicate=hasPortletFromPerspective, + ... first=slot, + ... second=portlet, + ... third=perspective + ... ) + +and add them to the storage: + + >>> from cpsskins.storage.relations import RelationStorage + >>> storage = RelationStorage() + + >>> storage[u'some_id'] = relation + +We can now find the portlets present in the slot from the given perspective: + + >>> storage.getSeconds( + ... predicate=hasPortletFromPerspective, + ... first=slot, + ... third=perspective + ... ) + [Portlet('Some portlet')] + +The portlet will not be visible from another perspective: + + >>> other_perspective = Perspective('Some other perspective') + >>> other_perspective.identifier = 4 + + >>> storage.getSeconds( + ... predicate=hasPortletFromPerspective, + ... first=slot, + ... third=other_perspective + ... ) + [] + +We clear the storage: + + >>> storage.clear() + +A slot has: + +- a unique identifier that is used internally + + >>> slot.identifier + 2 + +- a slot name that is exposed to the user. + + >>> str(slot) + 'Right' + + +If two *different* slots have the *same slot name*, they will display the same +portlets: + + >>> slotA = Slot(slot=u'Some slot') + >>> slotA.identifier = 5 + >>> slotB = Slot(slot=u'Some slot') + >>> slotB.identifier = 6 + + >>> relationA = Triad( + ... predicate=hasPortletFromPerspective, + ... first=slotA, + ... second=portlet, + ... third=perspective + ... ) + + >>> relationB = Triad( + ... predicate=hasPortletFromPerspective, + ... first=slotB, + ... second=portlet, + ... third=perspective + ... ) + + >>> storage[u'A'] = relationA + >>> storage[u'B'] = relationB + + >>> storage.getSeconds( + ... predicate=hasPortletFromPerspective, + ... first=slotA, + ... third=perspective + ... ) + [Portlet('Some portlet')] + + >>> storage.getSeconds( + ... predicate=hasPortletFromPerspective, + ... first=slotB, + ... third=perspective + ... ) + [Portlet('Some portlet')] + +Finally we clear the storage: + + >>> storage.clear() + + Modified: cpsskins/branches/paris-sprint-2006/doc/theme-structure.txt ============================================================================== --- cpsskins/branches/paris-sprint-2006/doc/theme-structure.txt (original) +++ cpsskins/branches/paris-sprint-2006/doc/theme-structure.txt Thu Jun 15 15:47:51 2006 @@ -1,26 +1,34 @@ -Theme structure ---------------- +======================= +THEME MANAGEMENT FOLDER +======================= :: + themes ( .../++etc++site/defaut/themes ) | - +----- Theme A + +--- Theme A + | | + | + page a | - +----+ Theme B - | - + Pages + page a - | | - | + page b - | - + Displays (areas, boxes) - | - + Formats (styles, layouts,...) - | - + Portlets (global) - | - + Images (backgrounds, images, ...) + | + +--+ Theme B + | | + | + page a + | | + | + page b + | + | + + Portlets (local) + | + + Displays (areas, boxes, ...) + | + + Formats (styles, layouts, ...) + | + + Images (backgrounds, icons, ...) + | + + Presets + | + + Snapshots -- images, portlets, formats and displays are shared between pages Modified: cpsskins/branches/paris-sprint-2006/ftests/test_portlet.py ============================================================================== --- cpsskins/branches/paris-sprint-2006/ftests/test_portlet.py (original) +++ cpsskins/branches/paris-sprint-2006/ftests/test_portlet.py Thu Jun 15 15:47:51 2006 @@ -25,9 +25,7 @@ def test_suite(): return unittest.TestSuite(( - FunctionalDocFileSuite('../doc/local-portlets.txt', setUp=setUp), - FunctionalDocFileSuite('../doc/portlet-rendering.txt', setUp=setUp), - FunctionalDocFileSuite('../doc/portlet-ordering.txt', setUp=setUp), + FunctionalDocFileSuite('../doc/portlets.txt', setUp=setUp), )) if __name__ == '__main__': Modified: cpsskins/branches/paris-sprint-2006/tests/test_renderers.py ============================================================================== --- cpsskins/branches/paris-sprint-2006/tests/test_renderers.py (original) +++ cpsskins/branches/paris-sprint-2006/tests/test_renderers.py Thu Jun 15 15:47:51 2006 @@ -26,8 +26,6 @@ DocTestSuite('cpsskins.browser.rendering.renderer'), DocTestSuite('cpsskins.browser.rendering.context'), DocTestSuite('cpsskins.browser.rendering.viewer'), - DocFileSuite('../doc/canvas-rendering.txt'), - DocFileSuite('../doc/draft-rendering-pipelines.txt'), )) if __name__ == '__main__': -- http://lists.nuxeo.com/mailman/listinfo/z3lab-checkins