Actions and aliases

 CMF 1.5, on which Plone 2.1 depends, brings two related concepts together: Actions and method aliases. Actions are a way of defining links, such as the portal tabs or the document actions that define the "print" and "send to" icons on standard documents. In particular, actions in the 'object' and 'folder' categories will appear as green "content tabs" in Plone. The standard ones are 'view', 'edit', 'properties' and 'sharing'.
 
In Plone 2.0, defining a custom 'view' action was the standard way of defining the view template for a content type. However, as you have already seen in the section on "dynamic views":dynamic-views, this is better handled in Plone 2.1 using the 'default_view' and 'suppl_views' variables and letting CMFDynamicViewFTI do its magic.

The concept of 'method aliases' means that even for other actions such as 'edit', 'properties' or 'local_roles' (the 'sharing' tab), re-defining actions on a type is typically a bad idea. Method aliases are a way of standardising URLs via another level of indirection, so that if you navigate to '/path/to/object/edit' you will always get the correct template for editing the object.

ATContentTypes and 'BrowserDefaultMixin' from CMFDynamicViewFTI define the standard actions and aliases. You should rarely need to add additional actions, and even more rarely change the standard ones. You can view (and change) actions and aliases from the 'Actions' and 'Aliases' tabs of an FTI in 'portal_types', as well as define them on your type using the 'actions' and 'aliases' variables.

As defined in 'ATContentTypes/content/base.py', the standard actions and aliases are::

    actions = ({
        'id'          : 'view',
        'name'        : 'View',
        'action'      : 'string:${object_url}',
        'permissions' : (View,)
         },
        {
        'id'          : 'edit',
        'name'        : 'Edit',
        'action'      : 'string:${object_url}/edit',
        'permissions' : (ModifyPortalContent,),
         },
        {
        'id'          : 'metadata',
        'name'        : 'Properties',
        'action'      : 'string:${object_url}/properties',
        'permissions' : (ModifyPortalContent,),
         },
         
         ...
         
        {
        'id'          : 'local_roles',
        'name'        : 'Sharing',
        'action'      : 'string:${object_url}/sharing',
        'permissions' : (ManageProperties,),
         },
        )

    aliases = {
        '(Default)'  : '(dynamic view)',
        'view'       : '(selected layout)',
        'index.html' : '(dynamic view)',
        'edit'       : 'atct_edit',
        'properties' : 'base_metadata',
        'sharing'    : 'folder_localrole_form',
        'gethtml'    : '',
        'mkdir'      : '',
        }

Notice how actions resolve to URLs that are method aliases. Remember - actions are rendered as tabs. When you click a tab, the URL of that action is called. Hence, the 'edit' action points to 'string:${object_url}/edit', which means that if you are at '/path/to/object' and click 'edit', you will go to '/path/to/object/edit'. '/edit' then gets recognised as a method alias, which points to the page template 'atct_edit', causing Zope to render '/path/to/object/atct_edit'. 

Were you to define a custom edit form, you would do something like::

  aliases = updateAliases(ATDocument,
        {
        'edit' : 'my_edit',
        })
        
on your class. The utility method 'updateAliaes' is defined in 'ATContentTypes/content/base.py'.

Special targets

  CMFDynamicViewFTI adds two special method alias *targets*:
  
(selected layout) -- The method alias will resolve to the currently selected layout template, as chosen by the "display" menu. Note that this does not take into consideration any default-page content item chosen using the "Choose content item as default view" option in the 'display' menu.

(dynamic view) -- Is like '(selected layout)', except it *does* take into consideration the default-page if one is selected.

You will notice that the '(Default)' method alias uses '(dynamic view)' as its target. This alias (which must be defined and capitalised exactly like that) defines what happens when you navigate directly to a content object without specifying a page template[1]. Hence, when you go to the url '/path/to/object', it will look at the '(Default)' alias for the content type of 'object', and render the object with the chosen layout or default-page. This is also why the standard 'view' action points to 'string:${object_url}' only.

The 'view' alias points to '(selected layout)' by convention. This is so that you can go to '/path/to/folder/view' to make sure you get the view of that folder, regardless of any selected default-page.

A note about file content

 File content (files and images) need to dump their content to the browser when referenced directly. If you link to '/path/to/image.jpg', it must render the image, not the 'image_view' template for that object. This is achieved by pointing the '(Default)' alias to 'index_html', and defining a method 'index_html()' on the file content type that dumps the content. 
 
The '/view' alias can still be used to view the file within Plone. The 'view' *action* must point to this method alias ('string:${object_url}/view') instead of the more common '(Default)' alias ('string:${object_url}' only), or clicking the 'view' tab would cause the file to dump its contents. Hence 'ATFileContent' in 'ATContentTypes/content/base.py' sets::
 
    actions = updateActions(ATCTContent,
        ({
        'id'          : 'view',
        'name'        : 'View',
        'action'      : 'string:${object_url}/view',
        'permissions' : (View,)
         },
         {
        'id'          : 'download',
        'name'        : 'Download',
        'action'      : 'string:${object_url}/download',
        'permissions' : (View,),
        'condition'   : 'member', # don't show border for anon user
        'visible'     :  False,
         },
        )
    )

    aliases = updateAliases(ATCTMixin,
        {
        '(Default)' : 'index_html',
        'view'      : '(selected layout)',
        })

In certain places, it is important that Plone links to the '/view' alias to display the file inside Plone instead of dumping its contents. The property 'typesUseViewActionInListings' in 'portal_properties/site_properties' holds a list of these types. RichDocument's 'ImageAttachment' and 'FileAttachment' types, which simply extend 'ATImage' and 'ATFile', must be registered here::

    propsTool = getToolByName(context, 'portal_properties')
    siteProperties = getattr(propsTool, 'site_properties')
    typesUseViewActionInListings = list(siteProperties.getProperty('typesUseViewActionInListings'))
    if 'ImageAttachment' not in typesUseViewActionInListings:
        typesUseViewActionInListings.append('ImageAttachment')
    siteProperties.manage_changeProperties(typesUseViewActionInListings  = typesUseViewActionInListings)

..[1] In previous versions of Plone and CMF, this used to result in a call to '__call__()', which would look up the 'view' action and generally make your life a misery if you were dealing with folders