================ Management Views ================ A :term:`management view` is a :term:`view configuration` that applies only when the URL is prepended with the :term:`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 :term:`SDI` to manage content. There are two ways to define management views: - Using the :class:`substanced.sdi.mgmt_view` decorator on a function, method, or class. - Using the :func:`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 :term:`scan`. Declaring a management view is much the same as declaring a "normal" Pyramid view using :class:`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: .. code-block:: python :linenos: 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: .. code-block:: python :linenos: 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 :attr:`~substanced.sdi.MIDDLE`, :attr:`~substanced.sdi.LEFT`, or :attr:`~substanced.sdi.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: .. code-block:: python 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 :doc:`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 ```` 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``: .. code-block:: xml <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