Welcome to the Admin Documentation!

This Packages tries to provide a Base to have basic CRUD Operations with a good User Interface and Experience with the least effort and code as Possible, because i believe, that, the DRY principle should be Used for the GUI of your application as much as Possible as Well. In Principle you only need to specify one Tag called @AdminAnnotationsActive in your Model’s class and this Admin package will take care of the rest. Every other Option is optional to optimize the Experience of the GUI. For Maximum Flexibility and Versatility a System of Fallback Mechanisms is in place to override the Default Template.

General

Basic Usage

Installation

If you don’t have a FLOW3 Project set up yet take a look at this: http://flow3.typo3.org/documentation/quickstart.html

Installing Admin:

cd %FLOW3-Project-Directory%
git clone git@github.com:mneuhaus/FLOW3-Admin.git Packages/Application/Admin
./flow3 package:activate Admin
./flow3 doctrine:migrate

Adding AdminDemo as well:

cd %FLOW3-Project-Directory%
git clone git@github.com:mneuhaus/FLOW3-AdminDemo.git Packages/Application/AdminDemo
./flow3 package:activate AdminDemo
./flow3 doctrine:migrate

Quick start

There are 2 Ways to Configure the Admin Interface:

  1. Settings.yaml

  2. Class Reflections inside the Models

    Note: The Settings.yaml overrules the Class Reflections in order to make it Possible to change the Behaviour of 3rd Party Packages without messing with external Code.

Settings.yaml:

Admin:
    Beings:
        \TYPO3\Blog\Domain\Model\Post:
            Active: true
            Properties:
                content:
                    Widget: TextArea

This Example Activates the Post model of the Blog Example (autoadmin:true) and Changes the Widget for the Content Property from a simple Textfield to a Textarea

Class Reflections:

use Admin\Annotations as Admin;
/**
 * A blog post
 * ...
 * @Admin\Active
 */
class Post {
    /**
     * @var string
     * @Admin\Widget("TextArea")
     */
    protected $content;
}

This Example Does the exact same thing as the Settings.yaml Example but this time inside the Post.php file with the Tag @AdminActive and @AdminWidget(“TextArea”)

Additional Features

MagicModel

You can extend your Models from this Model to get Magic Getters, Setters and some other features. Be aware of the fact that you should implement your Getters and Setters sooner or later to gain some Performance. But for development stage it just keeps the FLOW when you don’t need to bother about all those repetative getters and setters all the time. > Note: This Administration interface works completely without this MagicModel. You just need to make sure, that you have all the getter and setter functions properly defined in your models. Additionally it is strongly suggested to implement the __toString funtion for your Models to return a sensible String representation of the Model.

getPropertyName()
tries to get the property
setPropertyName($value)
tries to set the property
addPropertyName($item)
add an item to an collection
hasPropertyName($item)
checks if the collection contains that item
removePropertyName($item)
removes the item from the collection
__toString()
returns an smart string representation of the Model
toArray()
dumps the models properties to an array
fromArray($values)
sets the models properties based on the supplied values

Access Control

Through the Access annotation you have the ability to protect your ControllerActions with the Admin UserAuthorization.

All you need to do is to add this Annotation to the Actions you wish to protect:

/**
 * @Admin\Annotations\Access()
 */
public function indexAction(){}

When you don’t specifiy any parameters it will just check for a valid user and redirect to the login it no user is logged in.

Parameters
admin
set this to true in order to require an admin for this action
role
set this to a specific role to require the user to be in this role. (Admin overrules this!)

Configuration

Model Configuration

Active

Enable the Admin Interface for a Model

Class Reflection:

use Admin\Annotations as Admin;
/** A Blog post
  * ...
  * @Admin\Active
  */
class Post {...

YAML:

TYPO3\Blog\Domain\Model\Post:
    Active: true

Group

Specifiy a Group in which the Model will be Listed in the Menu. By Default the Models will be Sorted in Categories based on the Package name.

Class Reflection:

use Admin\Annotations as Admin;
/** A Blog post
  * ...
  * @Admin\Active
  * @Admin\Group("MyBlog")
  */
class Post {...

YAML:

TYPO3\Blog\Domain\Model\Post:
    Active: true
    Group: MyBlog

Label

Specifiy a Label for the Model to be used in the Menu.

Class Reflection:

use Admin\Annotations as Admin;
/** A Blog post
  * ...
  * @Admin\Active
  * @Admin\Label("Blog Posts")
  */
class Post {...

YAML:

TYPO3\Blog\Domain\Model\Post:
    Active: true
    Label: Blog Posts

Set

By Default all [Properties](property) will be in a General Fieldset called General in the Order in which they are listed in the Models class. You can override this by specifiying specific Sets of fields.

Class Reflection:

use Admin\Annotations as Admin;
/** A Blog post
  * ...
  * @Admin\Active
  * @Admin\Annotations\Set(title="Main", properties="title,content")
  * @Admin\Annotations\Set(title="Extended Informations", properties="linkTitle,date,author,image")
  */
class Post {...

YAML:

TYPO3\Blog\Domain\Model\Post:
    Active: true
    Set:
        -
            Title: Main
            Properties: title, content
        -
            Title: Extended Informations
            Properties: linkTitle, date, author, image

Variant

Variants are different Templates for actions. There are 3 Variants for the List Action included:

List
The regular Pagniated Table
Panes
Variant with 2 Panes like a E-Mail View
Calendar
Very basic implementation for a calendar view

Class Reflection:

use Admin\Annotations as Admin;
/** A Blog post
  * ...
  * @Admin\Active
  * @Admin\Variant(variant="Calendar", options="Calendar, List")
  */
class Event {...

YAML:

Admin\Domain\Model\Event:
    Active: true
    Variant:
        variant: Calendar
        options: Calendar, List
Options
variant
Name of the Variant
options
List of Variants that should be selectable

VariantMappings

VariantMappings are used in conjunction with Variants to tell the specific variant which property of the entity can be used for what

Panes
image, title, subtitle, content
Calendar
title, start, end

Class Reflection:

use Admin\Annotations as Admin;
/** A Blog post
  * ...
  * @Admin\Active
  * @Admin\Variant(variant="Calendar", options="Calendar, List")
  * @Admin\VariantMapping(title="title", start="startdate", end="enddate")
  */
class Event {...

YAML:

Admin\Domain\Model\Event:
    Active: true
    Variant:
        variant: Calendar
        options: Calendar, List
    VariantMapping:
        title: title
        start: startdate
        end: enddate

Property Configurations

Filter

By Tagging a Property with this Tag the Admin Interface will try to provide a Selectbox to Filter the List View by the Possible Values of this Property

Class Reflection:

/**
 * @var string
 * @Admin\Annotations\Filter
 */
protected $author;

YAML:

TYPO3\Blog\Domain\Model\Blog:
    Properties:
        Author:
            Filter: true

Label

With this Tag you can set the Label for a Property which is by Default a CamelCased version of the propertyname

Class Reflection:

/**
 * @var string
 * @Admin\Annotations\Label("Post Title")
 */
 protected $title;

YAML:

TYPO3\Blog\Domain\Model\Post:
    Properties:
        Title:
            Label: Post Title

Ignore

There are a number of Properties which have no use to be Administrated through a GUI. With the ignore Tag you can control the Visibility of the Property to the Admin Interface.

Ignore the Property Completly

Class Reflection:

/**
 * @var string
 * @Admin\Annotations\Ignore
 */
protected $id;

YAML:

TYPO3\Blog\Domain\Model\Post:
    Properties:
        Id:
            Ignore: true
Ignore the Property in specific Views

Class Reflection:

/**
 * @var string
 * @Admin\Annotations\Ignore list,view */
protected $content;

YAML:

TYPO3\Blog\Domain\Model\Post:
    Properties:
        Content:
            Ignore: list,view

Infotext

For more Information about a Property aside from the Title you can provide an Infotext that will be shown beside/below the Input Widgets in the Create and Update Views

Class Reflection:

/**
 * @var string
 * @Admin\Annotations\InfoText("Please tell us who you are")
 */
protexted $author;

YAML:

TYPO3\Blog\Domain\Model\Post:
    Properties:
        Author:
            Infotext: Please tell us who you are

Inline

Through this annotation you can make the Child Entities of relations editable directly on the editing page of the parent entity There are 2 different variants included for this: Default and Tabular. See Variants for more informations. Make sure to add “cascade={“all”}” to your ORM relation, because otherwise you’ll get an error when trying to save

Class Reflection:

/**
 * @var \AdminDemo\Domain\Model\Address
 * @ORM\ManyToOne(cascade={"all"})
 * @Admin\Inline()
 */
protected $address;

YAML:

TYPO3\Blog\Domain\Model\Post:
    Properties:
        Address:
            Inline: True

OptionsProvider

Class Reflection:

/**
 * @var \Doctrine\Common\Collections\ArrayCollection<\Admin\Security\Policy>
 * @Admin\Annotations\OptionsProvider("\Admin\OptionsProvider\PolicyOptionsProvider")
 */
protected $grant;

YAML:

TYPO3\Blog\Domain\Model\Blog:
    Properties:
        Grant:
            OptionsProvider: \Admin\OptionsProvider\PolicyOptionsProvider

Representation

Through this option you can set options for the Representation of an property. Currently it is only used for the datetimeFormat

Class Reflection:

/**
 * @var \Datetime
  * @Admin\Representation(datetimeFormat="Y-m-d")
 */
protected $date;

YAML:

Admin\Domain\Model\Widgets:
    Properties:
        date:
            Repesentation:
                datetimeFormat: Y-m-d

Title

This option only works if you use the MagicModel! You can Specify any Property that can be Converted to a String as a Title to be used as a simple String Repesentation which is for example used in the Single and Multiple Relation Widget to Identify an Model Item

The MagicModel will try the following things to determine a title:

  1. Does the Model Provide a __toString() Method
  2. Does one or more @title Tags exist
  3. Are there Properties Tagged as @identity which can be converted to String
  4. Is there a Property with the name “title” or “name”

Class Reflection:

/**
 * @var string
 * @Admin\Annotations\Title
 */
protected $title;

YAML:

TYPO3\Blog\Domain\Model\Post:
    Properties:
        Title:
            Title: true

First thing that matches will be used in the Order specified

Validate

Please Check the FLOW3 Documentation for the Validation rule: http://flow3.typo3.org/documentation/guide/partii/validation.html

Variant

Variants are different Variations for a similar use-case. Included are Variants for the InlineEditing:

Default
The default Stacked Form-View
Tabular
In this variant the inputs are aligned in a table to take up less space.

Class Reflection:

/**
 * @var \AdminDemo\Domain\Model\Address
 * @ORM\ManyToOne(cascade={"all"})
 * @Admin\Inline()
 * @Admin\Variant("Tabular")
 */
protected $address;

YAML:

TYPO3\Blog\Domain\Model\Post:
    Properties:
        Address:
            Inline: True
            Variant: Tabular

Widget

Instead of the automatically Assigned Widget you can use this Tag to specify a specific Widget.

Class Reflection:

/**
 * @var string
 * @Admin\Annotations\Widget("TextArea")
 */
 protected $content;

YAML:

TYPO3\Blog\Domain\Model\Post:
    Properties:
        Content:
            Widget: TextArea

Templates

Since the Admin Interface would be not of much use if you could only use the Templates that the Admin Package ships with by default there is a Fallback System in place to automatically choose the most specific Template possible to Render the Admin Interface. The Fallbacks are Configured in the Settings.yaml and can be customized if needed.

Views

The Admin Interface will check for the existence of each of this Fallbacks until a Template is found and then Render accordingly:

resource://@package/Private/Templates/@being/@action/@variant.html
resource://@package/Private/Templates/Admin/@action/@variant.html
resource://@package/Private/Templates/@being/@action.html
resource://@package/Private/Templates/Admin/@action.html
resource://Admin/Private/Templates/Standard/@action/@variant.html
resource://Admin/Private/Templates/Standard/@action.html
@package
Name of the Package which Contains the Model to be Rendered
@being
Short name of the Model (TYPO3BlogDomainModelPost -> Post)
@action
Action to Render (List, Create, Confirm, View, ...)
@variant
Variant to Render (Tabular, Block)

Partials

Partials are Subparts which can be Reused in more than one View (Form, Table, Toolbar,...):

resource://@package/Private/Partials/@being/@action/@partial/@variant.html
resource://@package/Private/Partials/@being/@action/@partial.html
resource://@package/Private/Partials/@being/@partial/@variant.html
resource://@package/Private/Partials/@being/@partial.html
resource://@package/Private/Partials/@action/@partial/@variant.html
resource://@package/Private/Partials/@action/@partial.html
resource://@package/Private/Partials/@partial/@variant.html
resource://@package/Private/Partials/@partial.html
resource://Admin/Private/Partials/@action/@partial.html
resource://Admin/Private/Partials/@action/@partial/@variant.html
resource://Admin/Private/Partials/@partial/@variant.html
resource://Admin/Private/Partials/@partial.html
resource://Admin/Private/Partials/@partial/Default.html
@package
Name of the Package which Contains the Model to be Rendered
@being
Short name of the Model (TYPO3BlogDomainModelPost -> Post)
@partial
Name of the Partial (Form, Table, Toolbar,...)

Widgets

resource://@package/Private/Partials/@being/Widgets/@partial.html
resource://@package/Private/Partials/Widgets/@partial.html
resource://Admin/Private/Partials/Widgets/@partial.html
@package
Name of the Package which Contains the Model to be Rendered
@being
Short name of the Model (TYPO3BlogDomainModelPost -> Post)
@partial
Name of the Partial (TextField, Boolean, DateTime,...)

DashboardWidgets

resource://@package/Private/Partials/@being/DashboardWidgets/@partial.html
resource://@package/Private/Partials/DashboardWidgets/@partial.html
resource://Admin/Private/Partials/DashboardWidgets/@partial.html
@package
Name of the Package which Contains the Model to be Rendered
@being
Short name of the Model (TYPO3BlogDomainModelPost -> Post)
@partial
Name of the Partial (TextField, Boolean, DateTime,...)

Extending

Actions

Actions for the Admin need to implement the following interaface:

namespace Admin\Core\Actions;
interface ActionInterface {

        /**
         * Function to Check if this Requested Action is supported
         * @author Marc Neuhaus <mneuhaus@famelo.com>
         * */
        public function canHandle($being, $action = null, $id = false);

        /**
         * The Name of this Action
         * @author Marc Neuhaus <mneuhaus@famelo.com>
         * */
        public function __toString();

        /**
         * @param string $being
         * @param array $ids
         * @author Marc Neuhaus <mneuhaus@famelo.com>
         * */
        public function execute($being, $ids = null);

}

Description of the functions

canHandle($being, $action = null, $id = false)
This function receives 3 arguments, based on which you need to decide if this action can handle the current use case.
Parameters:
  • $being – represents the current class
  • $action – name of current action (list, view, create, update, bulk,...)
  • $id – specifies if this action will receive ids as well
__toString()
This functions returns a Name for this action that will be used for the Buttons and such
execute($being, $ids = null)
This function handles the execution of the action.
Parameters:
  • $being – represents the current class
  • $ids – an array of ids to act upon

Examples

The Delete action needs $ids to delete, so it returns true if there are ids to receive:

class DeleteAction extends \Admin\Core\Actions\AbstractAction {
    public function canHandle($being, $action = null, $id = false) {
        return $id;
    }
}

The Update action needs $ids to update, but can’t handle bulk actions:

class UpdateAction extends \Admin\Core\Actions\AbstractAction {
    public function canHandle($being, $action = null, $id = false) {
        switch($action) {
            case "bulk":
                return false;
            default:
                return $id;
        }
    }
}

Rendering a view for the action

The function execute behaves exactly like a regular controllerAction. The following variables are defined in the ActionClass:

$this->request
the regular controllerRequest
$this->view
the view to be rendered
$this->adapter
the current adapter to handle objects
$this->controller
the responsible controller

Widgets

Available Properties in a Widget Template

Inside the Widget Partial there is one essential Variable available called {property}. This Variable Provides the following values:

property.adapter
Classname of the current Adapter
property.widget
Name of the Widget
property.value
Unprocessed value of the Property, this might be almost anything depending on the Data in the Object. Handle this with care, because it might cause Rendering errors. In most cases you should simply use the {property.string} option to get an String representation of the Value.
property.infotext
Informational Text for the Property
property.label
Label for the Property
property.string
String representation for the property’s value
property.inputName
Appropriate name for an input including the proppert prefix (item[propertyname])
property.type
DataType of the property
property.name
Name of the Property

Replace the default Widget for a specific datatype

Widgets are assigned to datatypes by a fallback system configured in Settings.yaml:

Doctrine:
    Widgets:
        Mapping:
            string:   Textfield
            readonly:   TextfieldReadonly
            integer:  Spinner
            float:    Textfield
            boolean:  Boolean
            \TYPO3\FLOW3\Resource\Resource: Upload
            \DateTime: DateTime
            ^\[A-Za-z]+\Domain\Model\[A-Za-z]+: SingleRelation
            ^\[A-Za-z]+\Security\[A-Za-z]+: SingleRelation
            ^\Doctrine\Common\Collections\Collection\<\*\[A-Za-z]+\Domain\Model\[A-za-z]+>: MultipleRelation
            ^\Doctrine\Common\Collections\Collection\<\*\[A-Za-z]+\Security\[A-za-z]+>: MultipleRelation

On The Left side you have your Classes or Names of the DataTypes and on the Right Side is the repsonsible Widget to use. You can override any of these Widgets in your Production/Development Settings.yaml. Aside from Classes or DataType Names you can specify an Regular Expression to Match more Complex things, like in this Case Entity Models.

Dashboard Widgets

DashboardWidgets are shown on the Admin main page and need to implement the function “initializeWidget”.

Example

LogWidget.php:

/**
 *
 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License, version 3 or later
 */
class LogWidget extends \Admin\Core\DashboardWidgets\AbstractDashboardWidget {
    public function initializeWidget() {
        $query = $this->objectManager->get("\Admin\Domain\Repository\LogRepository")->createQuery();
        $query->setOrderings(array(
            "datetime" => 'ASC'
        ));
        $query->setLimit("10");
        $logs = $query->execute();
        $this->view->assign("logs", $logs);
    }
}

Private/Partials/DashboardWidgets/Log.html:

<h5 class="well-header">Recent Activity</h5>
<f:if condition="{logs.count} > 0" >
    <f:then>
        <table class="zebra-striped condensed-table cozy">
            <thead>
            <tr>
                <th>Type</th>
                <th>Action</th>
                <th>User</th>
            </tr>
            </thead>
            <f:for each="{logs}" as="log">
                <tr>
                    <td>{log.being}</td>
                    <td>
                        <span class="label">{log.action}</span>
                    </td>
                    <td>{log.user}</td>
                </tr>
            </f:for>
        </table>
    </f:then>
    <f:else>
        No actions yet.
    </f:else>
</f:if>

OptionsProviders

An Options Provider creates the List of Options for the SingleRelation and MultipleRelation Widgets. Currently there is the Default implementation which creates the Options Using the ID and String Representation of the Object and one that load Options from an simple source. But for Example the PolicyOptionsProvider ensures that there are all needed Options as Policy available when the Roles Object is loaded

RelationOptionsProvider

This Optionsprovider gives available options based on the entity’s relation

ArrayOptionsProvider

This Optionsprovider gives available options based on the entity’s relation

Reflection:

/**
 * @var string
 * @Admin\Widget("Dropdown")
 * @Admin\OptionsProvider(name="Array", property="options")
 */
protected $optionsProvider;
public $options = array(
    "Hello" => "World",
    "Hell" => "Yea"
);

YAML:

TYPO3\Party\Domain\Model\ElectronicAddress:
  Properties:
    type:
      OptionsProvider:
        name: Array
        options:
          aim: Aim
          email: Email
          gizmo: Gizmo
          icq: Icq
          jabber: Jabber
          msn: Msn
          sip: Sip
          skype: Skype
          url: Url
          yahoo: Yahoo

PolicyOptionsProvider

Similar to the RelationOptionsProvider with the difference, that it populates the Policy table with policies based on available entities and actions

ViewHelpers

ApiViewHelper

This ViewHelper provides access to the AdminCoreAPI:: functions.

get
specifies the variable or function to trigger on the API
as
specifies the variable which will contain the result

BeingViewHelper

This ViewHelper converts an regular Object to a so called Being with all Annotations, properties etc

class
class to construct a being from
object
object to construct a being from

DashboardWidgets

This ViewHelper renders the currently active Widgets.

FilterViewHelper

This ViewHelper can be used to filter objects

objects
the objects that should be sorted
as
variable for the filtered objects. By Default: filteredObjects
filtersAs
variable for the filters. By Default: filters

Example:

<a:query.filter objects="{ objects}">
    <f:for each="{ filteredObjects}" as="object">
        ...
    </f:for>
    <a:render partial="Filters/Right" fallbacks="Partials"/>
</a:query.paginate>

Form.FieldViewHelper

This ViewHelper renders a form field with error handling, label infotext, etc

property
the beings property to render

Example:

<f:form method="post" action="form" fieldNamePrefix="form">
    <a:being className="AdminDemo\Domain\Model\Address" as="being">
        <a:form.field being="{ being.street}" />
        <a:form.field being="{ being.housenumber}" />
        <a:form.field being="{ being.city}" />
    </a:being>
</f:form>

Form.FieldsViewHelper

This ViewHelper renders a form for a being. This ViewHelper doesn’t render the form tag itself!

being
being to render the form for

Example:

<f:form method="post" action="form" fieldNamePrefix="form">
    <a:being className="AdminDemo\Domain\Model\Address" as="being">
        <a:form.fields being="{ being}" />
    </a:being>
</f:form>

LayoutViewHelper

This ViewHelper extends the regular LayoutViewHelper with the ability to specifiy an package to search for the layout

name
name of the layout
package
name of the package to look for the layout

Example:

<a:layout name="Bootstrap" package="Admin"/>

PaginationViewHelper

This is a simple pagination ViewHelper to limit and paginate objects

objects
the objects that should be paginated
as
variable for the paginated objects. By Default: paginatedObjects
limitsAs
variable for the limits. By Default: limits
paginationAs
variable for the pagination. By Default: pagination

Example:

<a:query.paginate objects="{ objects}">
    <f:for each="{ paginatedObjects}" as="object">
        ...
    </f:for>

    <div class="pagination pull-left">
        <a:render partial="Limits" fallbacks="Partials"/>
    </div>

    <div class="pagination pull-right">
        <a:render partial="Pagination" fallbacks="Partials"/>
    </div>
</a:query.paginate>

RenderViewHelper

This ViewHelper extends the regular RenderViewHelper with these features:

optional
you can set the optional parameter to true in conjunction with the section attribute. In contrast to the regular RenderViewHelper this one renders it’s childs if the section isn’t overidden instead of an empty string
fallbacks
with this function you can specify an fallback path from the settings to search for the partial in conjunction with the vars parameter

Examples(Partial):

<a:render partial="Pagination" fallbacks="Partials"/>

Examples(Section):

<a:render section='container' optional="true">
    Content to be rendered when this section isn't overidden
</a:render>

SettingsViewHelper

This ViewHelper gives you access to global Settings from the view

path
specifies the path to the setting

SortViewHelper

This ViewHelper can be used to sort objects

objects
the objects that should be sorted
as
variable for the sorted objects. By Default: sortedObjects
sortingAs
variable for the sorting. By Default: sorting

Example:

<a:query.sort objects="{ objects}">
    <f:link.action addQueryString="true" arguments="{sort: 'title', direction: sorting.oppositeDirection">
        Sort by title
    </f:link.action>
    <f:for each="{ sortedObjects}" as="object">
        ...
    </f:for>
</a:query.paginate>

UserViewHelper

This ViewHelper gives you access to the current user

as
specifies the variable which will contain the user