This product is in active development and has been released under the MIT license.

Adds a service manager to Grav.

The service manager is inspired by the OSGi service pattern. Services are registered by name, along with a list of properties. Clients can listen for service registrations, and also query the registry for services using service names and/or LDAP filters. Registrations can use factory methods to provide lazy instantiation.

The service manager singleton can be accessed from any point in the lifecycle.

$manager = \Grav\Plugin\Admin\ServiceManager::getInstance();

Services are registered using:

  • service name
  • an array, object, or primitive that implements or fullfills the service requirements
  • an optional properties array
$manager->registerService("has-label-service", ['label'=>'A sample label']);
$manager->registerService("has-label-service", ['label'=>'Another sample label'], ['scope'=>'private', 'type'=>'testing']);

A service also can be registered using a factory by providing an function closure that returns the service. The function will only be called if a client requests the service implementation. This provides a lazy service instantiation. The services properties can be used to published and queries do not require that the service is instantiated.

$manager->registerService("has-label-service", function() {
    // take some time...
    return ['label'=>'A lazy label'];
});

You can obtain a list of services by using the registered service name.

$services = $manager->getServices('has-label-service');
foreach($services as $service) {
    $theLabel = $service['label'];
}

You can also get a service by its ID. The ID is returned when the service is first registered.

$service = $manager->getService($serviceId);

You can obtain services and ServiceInfo objects using an LDAP filter to query the service properties.

In this example, only has-label-service with properties scope set to public and type starting with testing will be returned.

$services = $manager->find('has-label-service', '(&(scope=public)(type=testing*))');

More info on LDAP filters is available here and here.

Several twig functions have been added to inject content into a document.

The service implementations matching the given parameters.

    /**
     * @param string $serviceName The service name.
     * @param string $scope The scope to filter.  null or 'any' to match all scopes.
     * @param string $order The order.  null or 'any' to match all items.
     * @param null $context A context used when filtering the items
     * @return array The services matching the given parameters
     */
    function service_list($serviceName, $scope = 'any', $order = 'any', $context = null)

The service implementations matching the given parameters.

    /**
     * @param string $serviceName The service name.
     * @param string $scope The scope to filter.  null or 'any' to match all scopes.
     * @param string $order The order.  null or 'any' to match all items.
     * @param null $context A context used when filtering the items
     * @return array The ServiceInfo objects matching the given parameters
     */
    function service_info_list($serviceName, $scope = 'any', $order = 'any', $context = null)

The service implementations matching the given LDAP filter.

    /**
     * @param string $ldapFilter A LDAP filter
     * @param null $context A context used when filtering the items.
     * @return array The services matching the given parameters.
     * @throws \Exception
     */
    function service_list_filter($ldapFilter, $context = null)

The concatinated result of calling render() or rendering an action based on scope.

    /**
     * 
     * Renders "renderer" and "action" services
     * 
     * @param $serviceName The service name.
     * @param string $scope The scope to filter.  null or 'any' to match all scopes.
     * @param string $order The order.  null or 'any' to match all items.
     * @param null $context
     * @return string
     */
    public function service_render($serviceName, $scope = null, $order = null, $context = null)

A concatenated string list of calling 'items' on each service.

    /**
     * @param string $serviceName The service name.
     * @param string $scope The scope to filter.  null or 'any' to match all scopes.
     * @param string $order The order.  null or 'any' to match all items.
     * @return array Concatenated list of calling 'items' on each service.
     */
    function service_items($serviceName, $scope = 'any', $order = 'any')

The Service Manager allows adding listeners for service registrations.

$manager = ServiceManager::getInstance();

$manager->registerServiceListener('asset', function (&$serviceInfo) {
  log("Asset registered: " . $serviceInfo['implementation']['url']);
});
    /**
     * @param string $serviceName
     * @param callable string,array $listener
     * @param bool|null $fireOnExisting If true, the listener will be invoked for existing items.
     */
    public function registerServiceListener($serviceName, $listener, $fireOnExisting = false)

Most core services have the following fields:

Scope
array of string
The scope in which the service applies.

^ Known scopes: page | pages | configuration | dashboard | admin:sidebar | page:more | page:add ^

Order
string: first | last | before:parent | after:parent
The order of the item.
todo: Add order before-xxx, after-xxx
todo: Add order integer weight
isEnabled
function: truthy
By default all services are enabled. If a service declares a function called isEnabled($context) that returns a falsy value, the service will be considered disabled.
isVisible
function: truthy
By default all services are visible. If a service declares a function called isVisible($context) that returns a falsy value, the service will be considered hidden.
isSelected
function: truthy
By default all services unselected. If a service declares a function called isSelected($context) that returns a truthy value, the service will be considered selected.
group
A target group within the scope
todo: implement group

These are the current services implemented in the Grav Admin.

A general interface for rendering content.

Common Fields
scope, order
isVisible(context): Boolean
Returns boolean. true if the system should display the item.
render(context) : String
Returns html. The context parameter is dependent on the registered scope.

A general interface for adding assets to a page. Assets can be added at any time, even before and after the onAssetsInitialized event is fired by the Grav system. You can now also register link assets (for our web component fans).

Common Fields
scope, order, isEnabled
Assets that return false for isEnabled() will not be added to the document.
type
String: The asset type.
Allowed values: css | javascript | js | twig | link
url
String: The url of the asset.
content
String: The content to inject.
todo: implement content

A general interface for declaring an action.

Actions can be rendered by the system as menu items, buttons, links, callable methods, etc. based on their target scope. Actions can also provided their own rendered content.

An action callback can be executed on the client, the server, or both. The server callback can be asynchronous (AJAX) or synchronous (POST). The client callback can manually invoke the server callback if needed for validation, confirmations, etc. Action callbacks receive a context from the scope in which they are called.

Actions can specify the forms that opened when they are triggered. They can optionally submit a grav task when the forms are submitted.

Common Fields
scope, order, isVisible, isEnabled, isSelected

caption
String,optional: The caption to display
icon
String,optional: The icon to display
render
function(context): String, optional: A callback function that returns an HTML string. The context provided is dependant on the scope.

clientCallback
String: The Javascript to be executed on the browser.
serverCallbackId
String: A unique ID used to identify the callback when the client posts the action to the server.
serverCallbackContext
String | Array The context sent to the server callback.
serverAsyncCallback
Function: The PHP to be executed on the server. This is posted AJAX. The client will not be refreshed.
serverCallback
Function: The PHP to be executed on the server. This is posted using a standard form POST. The client will be refreshed.
serverHandler
Function: Same as ServerCallback, but must be manually invoked from client-side code.
_asyncActionCallback(actionId, scopeContext);
_syncActionCallback(actionId, scopeContext);

confirmationMessage
String: A message to display before callbacks are executed. If the user chooses to cancel, neither the clientCallback or serverCallback will be invoked.
form_id
The ID to use for the the form.
form_blueprint
The form blueprint .
form_data
A callback function the returns the data for the form. ^function($context) { return [ ]; }^

menu
The dropdown menu to add the action to.

A service to register page routes and generate pages
scope
String: site | admin
priority
Integer: The priority of the the route.
rxroute
String: The route, starting with '/' and containing a regular expression to match.
getPage
function getPage(route:String) : Page
todo: Implement priority when multiple routes are matched

Adds a report to the reports list.

scope
String: The scope of the report. Valid values are site | admin
caption
The report caption
generate
function generate(): String

The plugin also includes built-in features for the following items.

Synchronous and asynchronous posts to the server.

Simple modal dialogs for confirmations and alerts.

A class for checking runtime plugin dependencies has been added.