Pre-release
Edit me on GitHub

Management Views

A management view is a view configuration that applies only when the URL is prepended with the manage prefix. The manage prefix is usually /manage, unless you've changed it from its default by setting a custom substanced.manage_prefix in your application's .ini file.

This means that views declared as management views will never show up in your application's "retail" interface (the interface that normal unprivileged users see). They'll only show up when a user is using the SDI to manage content.

There are two ways to define management views:

  • Using the substanced.sdi.mgmt_view decorator on a function, method, or class.
  • Using the substanced.sdi.add_mgmt_view() Configurator (aka. config.add_mgmt_view) API.

The former is most convenient, but they are functionally equivalent. mgmt_view just calls into add_mgmt_view when found via a scan.

Declaring a management view is much the same as declaring a "normal" Pyramid view using pyramid.view.view_config with a route_name of substanced_manage. For example, each of the following view declarations will register a view that will show up when the /manage/foobar URL is visited:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from pyramid.view import view_config

@view_config(
    renderer='string',
    route_name='substanced_manage',
    name='foobar',
    permission='sdi.view',
    )
def foobar(request):
    return 'Foobar!'

The above is largely functionally the same as this:

1
2
3
4
5
from substanced.sdi import mgmt_view

@mgmt_view(renderer='string', name='foobar')
def foobar(request):
    return 'Foobar!'

Management views, in other words, are really just plain-old Pyramid views with a slightly shorter syntax for definition. Declaring a view a management view, however, does do some extra things that make it advisable to use rather than a plain Pyramid view registration:

  • It registers introspectable objects that the SDI interface uses to try to find management interface tabs (the row of actions at the top of every management view rendering).
  • It allows you to associate a tab title, a tab condition, and cross-site request forgery attributes with the view.
  • It uses the default permission sdi.view.

So if you want things to work right when developing management views, you'll use @mgmt_view instead of @view_config, and config.add_mgmt_view instead of config.add_view.

As you use management views in the SDI, you might notice that the URL includes @@ as "goggles". For example, http://0.0.0.0:6541/manage/@@contents is the URL for seeing the folder contents. The @@ is a way to ensure that you point at the URL for a view and not get some resource with the __name__ of contents. You can still get to the folder contents management view using http://0.0.0.0:6541/manage/contents...until that folder contains something named contents.

mgmt_view View Predicates

Since mgmt_view is an extension of Pyramid's view_config, it re-uses the same concept of view predicates as well as some of the same actual predicates:

  • request_type, request_method, request_param, containment, attr, renderer, wrapper, xhr, accept, header, path_info, context, name, custom_predicates, decorator, mapper, and http_cache are supported and behave the same.
  • permission is the same but defaults to sdi.view.

The following are new view predicates introduced for mgmt_view:

  • tab_title takes a string for the label placed on the tab.

  • tab_condition takes a callable that returns True or False, or True or False. If you state a callable, this callable is passed context and request. The boolean determines whether the tab is listed in a certain situation.

  • tab_before takes the view name of a mgmt_view that this mgmt_view should appear after (covered in detail in the next section.)

  • tab_after takes the view name of a mgmt_view that this mgmt_view should appear after. Also covered below.

  • tab_near takes a "sentinel" from substanced.sdi (or None) that makes a best effort at placement independent of another particular mgmt_view. Also covered below. The possible sentinel values are:

    substanced.sdi.LEFT
    substanced.sdi.MIDDLE
    substanced.sdi.RIGHT
    

Tab Ordering

If you register a management view, a tab will be added in the list of tabs. If no mgmt view specifies otherwise via its tab data, the tab order will use a default sorting: alphabetical order by the tab_title parameter of each tab (or the view name if no tab_title is provided.) The first tab in this tab listing acts as the "default" that is open when you visit a resource. Substance D does, though, give you some options to control tab ordering in larger systems with different software registering management views.

Perhaps a developer wants to ensure that one of her tabs appears first in the list and another appears last, no matter what other management views have been registered by Substance D or any add-on packages. @mgmt_view (or the imperative call) allow a keyword of tab_before or tab_after. Each take the string tab name of the management view to place before or after. If you don't care (or don't know) which view name to use as a tab_before or tab_after value, use tab_near, which can be any of the sentinel values MIDDLE, LEFT, or RIGHT, each of which specifies a target "zone" in the tab order. Substance D will make a best effort to do something sane with tab_near.

As in many cases, an illustration is helpful:

from substanced.sdi import LEFT, RIGHT

@mgmt_view(
    name='tab_1',
    tab_title='Tab 1',
    renderer='templates/tab.pt'
    )
def tab_1(context, request):
    return {}


@mgmt_view(
    name='tab_2',
    tab_title='Tab 2',
    renderer='templates/tab.pt',
    tab_before='tab_1'
    )
def tab_2(context, request):
    return {}


@mgmt_view(
    name='tab_3',
    tab_title='Tab 3',
    renderer='templates/tab.pt',
    tab_near=RIGHT
    )
def tab_3(context, request):
    return {}


@mgmt_view(
    name='tab_4',
    tab_title='Tab 4',
    renderer='templates/tab.pt',
    tab_near=LEFT
    )
def tab_4(context, request):
    return {}


@mgmt_view(
    name='tab_5',
    tab_title='Tab 5',
    renderer='templates/tab.pt',
    tab_near=LEFT
    )
def tab_5(context, request):
    return {}

This set of management views (combined with the built-in Substance D management views for Contents and Security) results in:

Tab 4 | Tab 5 | Contents | Security | Tab 2 | Tab 1 | Tab 3

These management view arguments apply to any content type that the view is registered for. What if you want to allow a content type to influence the tab ordering? As mentioned in the content type docs, the tab_order parameter overrides the mgmt_view tab settings, for a content type, with a sequence of view names that should be ordered (and everything not in the sequence, after.)

Filling Slots

Each management view that you write plugs into various parts of the SDI UI. This is done using normal ZPT fill-slot semantics:

  • page-title is the <title> in the <head>
  • head-more is a place to inject CSS and JS in the <head> after all the SDI elements
  • tail-more does the same, just before the </body>
  • main is the main content area

SDI API

All templates in the SDI share a common "layout". This layout needs information from the environment to render markup that is common to every screen, as well as the template used as the "main template."

This "template API" is known as the SDI API. It is an instance of the sdiapi class in substanced.sdi.__init__.py and is made available as request.sdiapi.

The template for your management view should start with a call to requests.sdiapi:

<div metal:use-macro="request.sdiapi.main_template">

The request.sdiapi object has other convenience features as well. See the Substance D interfaces documentation for more information.

Flash Messages

Often you perform an action on one view that needs a message displayed by another view on the next request. For example, if you delete a resource, the next request might confirm to the user "Deleted 1 resource." Pyramid supports this with "flash messages."

In Substance D, your applications can make a call to the sdiapi such as:

request.sdiapi.flash('ACE moved up')

...and the next request will process this flash message:

  • The message will be removed from the stack of messages
  • It will then be displayed in the appropriate styling based on the "queue"

The sdiapi provides another helper:

request.sdiapi.flash_with_undo('ACE moved up')

This displays a flash message as before, but also provides an Undo button to remove the previous transaction.

  • title, content, flash messages, head, tail