From 6b37fcf24f129baefacbd56dd25701fb77ae48ba Mon Sep 17 00:00:00 2001
From: Marius Hein <marius.hein@netways.de>
Date: Thu, 4 Jul 2013 14:48:30 +0200
Subject: [PATCH] Host detail view

Add commands to detail view

refs #4182
---
 .../views/helpers/MonitoringCommands.php      |  70 +-
 .../scripts/show/components/commands.phtml    |  17 -
 .../views/scripts/show/header.phtml           |   9 +-
 .../application/views/scripts/show/host.phtml |   9 +
 .../library/Monitoring/Command/Meta.php       | 677 +++++++++++++++---
 .../views/helpers/MonitoringCommandsTest.php  | 116 +++
 .../views/helpers/MonitoringFlagsTest.php     |   2 +-
 .../test/php/library/Command/MetaTest.php     | 185 +++++
 public/css/components/details.less            |  23 +
 test/php/phpunit.xml                          |   6 +-
 10 files changed, 978 insertions(+), 136 deletions(-)
 delete mode 100644 modules/monitoring/application/views/scripts/show/components/commands.phtml
 create mode 100644 modules/monitoring/test/php/application/views/helpers/MonitoringCommandsTest.php
 rename {test/php/modules/monitoring => modules/monitoring/test/php}/application/views/helpers/MonitoringFlagsTest.php (96%)
 create mode 100644 modules/monitoring/test/php/library/Command/MetaTest.php

diff --git a/modules/monitoring/application/views/helpers/MonitoringCommands.php b/modules/monitoring/application/views/helpers/MonitoringCommands.php
index 4b2733cfc..6a1a9b57b 100644
--- a/modules/monitoring/application/views/helpers/MonitoringCommands.php
+++ b/modules/monitoring/application/views/helpers/MonitoringCommands.php
@@ -27,36 +27,62 @@ use Icinga\Monitoring\Command\Meta;
 
 /**
  * Class MonitoringCommands
+ *
+ * Helper which produces a list of command buttons
+ * depending on object states
  */
 class Zend_View_Helper_MonitoringCommands extends Zend_View_Helper_Abstract
 {
     /**
-     * Type of small interface style
+     * @param stdClass $object host or service object or something other
+     * @param string $type small or full
+     * @return string html output
      */
-    const TYPE_SMALL = 'small';
-
-    /**
-     * Type of full featured interface style
-     */
-    const TYPE_FULL = 'full';
-    /**
-     * Returns the object type from object
-     * @param stdClass $object
-     * @return string
-     */
-    public function getObjectType(\stdClass $object)
-    {
-        return array_shift(explode('_', array_shift(array_keys(get_object_vars($object))), 2));
-    }
-
     public function monitoringCommands(\stdClass $object, $type)
     {
-        $type = $this->getObjectType($object);
-
         $commands = new Meta();
-        var_dump($commands->getCommandsForObject($object));
+        $definitions = $commands->getCommandForObject($object, $type);
+        $out = '<div>';
+        $i = 0;
 
+        foreach ($definitions as $definition) {
 
-        var_dump($type);
+            if ($i % 5 === 0) {
+                $out .= '</div><div class="command-section pull-left">';
+            }
+
+            if ($type === Meta::TYPE_FULL) {
+                $out .= '<div>';
+            }
+
+            $out .= sprintf(
+                '<button type="button" data-target="command"'
+                . ' data-command-id="%1$s" class="btn %5$s"'
+                . ' title="%3$s">'
+                . '<i class="%4$s"></i> %2$s'
+                . '</button>',
+                $definition->id,
+                $definition->shortDescription,
+                $definition->longDescription,
+                $definition->iconCls,
+                $definition->btnCls
+            );
+
+            if ($type === Meta::TYPE_FULL) {
+                $out .= '</div>';
+            }
+
+            $i++;
+        }
+
+        $out .= '</div>';
+
+        $out .= '<div class="clearfix"></div>';
+
+        if ($type === Meta::TYPE_FULL) {
+            return '<div class="command-container">'. $out. '</div>';
+        }
+
+        return $out;
     }
-}
\ No newline at end of file
+}
diff --git a/modules/monitoring/application/views/scripts/show/components/commands.phtml b/modules/monitoring/application/views/scripts/show/components/commands.phtml
deleted file mode 100644
index 771da4cb1..000000000
--- a/modules/monitoring/application/views/scripts/show/components/commands.phtml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php
-    $commands = $this->monitoringCommands(
-        $this->object,
-        (isset($this->type)) ? $this->type : 'full'
-    );
-    var_dump($commands);
-?>
-<div>
-    <button type="button" class="btn btn-primary">
-        <span>Check now </span>
-    </button>
-</div>
-<div class="container-spacer">
-    <button type="button" class="btn btn-danger">
-        <span>Acknowledge</span>
-    </button>
-</div>
\ No newline at end of file
diff --git a/modules/monitoring/application/views/scripts/show/header.phtml b/modules/monitoring/application/views/scripts/show/header.phtml
index c74d91235..cc691ab4f 100644
--- a/modules/monitoring/application/views/scripts/show/header.phtml
+++ b/modules/monitoring/application/views/scripts/show/header.phtml
@@ -19,12 +19,9 @@ if (!$this->compact) {
     <?php if ($inlineCommands === true) { ?>
     <div class="pull-right">
         <?=
-            $this->partial(
-                'show/components/commands.phtml',
-                array(
-                    'object' => ($showService === true) ? $this->service : $this->host,
-                    'type' => 'small'
-                )
+            $this->monitoringCommands(
+                ($showService === true) ? $this->service : $this->host,
+                'small'
             );
         ?>
     </div>
diff --git a/modules/monitoring/application/views/scripts/show/host.phtml b/modules/monitoring/application/views/scripts/show/host.phtml
index 3ae9fbe7d..e812a58c7 100644
--- a/modules/monitoring/application/views/scripts/show/host.phtml
+++ b/modules/monitoring/application/views/scripts/show/host.phtml
@@ -82,4 +82,13 @@
     <div>
         <?= $this->render('show/components/flags.phtml'); ?>
     </div>
+</div>
+
+<div class="information-container">
+    <div class="head">
+        <span>Commands</span>
+    </div>
+    <div>
+        <?= $this->monitoringCommands($this->host, 'full'); ?>
+    </div>
 </div>
\ No newline at end of file
diff --git a/modules/monitoring/library/Monitoring/Command/Meta.php b/modules/monitoring/library/Monitoring/Command/Meta.php
index 633d0a187..3644549d5 100644
--- a/modules/monitoring/library/Monitoring/Command/Meta.php
+++ b/modules/monitoring/library/Monitoring/Command/Meta.php
@@ -1,26 +1,26 @@
 <?php
 // {{{ICINGA_LICENSE_HEADER}}}
 /**
-* Icinga 2 Web - Head for multiple monitoring frontends
-* Copyright (C) 2013 Icinga Development Team
-*
-* This program is free software; you can redistribute it and/or
-* modify it under the terms of the GNU General Public License
-* as published by the Free Software Foundation; either version 2
-* of the License, or (at your option) any later version.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-* GNU General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with this program; if not, write to the Free Software
-* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-*
-* @copyright 2013 Icinga Development Team <info@icinga.org>
-* @author Icinga Development Team <info@icinga.org>
-*/
+ * Icinga 2 Web - Head for multiple monitoring frontends
+ * Copyright (C) 2013 Icinga Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * @copyright 2013 Icinga Development Team <info@icinga.org>
+ * @author Icinga Development Team <info@icinga.org>
+ */
 // {{{ICINGA_LICENSE_HEADER}}}
 
 namespace Icinga\Monitoring\Command;
@@ -35,29 +35,324 @@ use Icinga\Exception\ProgrammingError;
  */
 class Meta
 {
+    /**
+     * Category name which is useless
+     */
     const DROP_CATEGORY = 'none';
 
+    /**
+     * Interface type for small interfaces
+     *
+     * Only important commands
+     */
+    const TYPE_SMALL = 'small';
+
+    /**
+     * Interface type for full featured interface
+     *
+     * All commands are shown
+     */
+    const TYPE_FULL = 'full';
+
+    const CMD_DISABLE_ACTIVE_CHECKS = 1;
+    const CMD_ENABLE_ACTIVE_CHECKS = 2;
+    const CMD_RESCHEDULE_NEXT_CHECK = 3;
+    const CMD_SUBMIT_PASSIVE_CHECK_RESULT = 4;
+    const CMD_STOP_OBSESSING = 5;
+    const CMD_START_OBSESSING = 6;
+    const CMD_STOP_ACCEPTING_PASSIVE_CHECKS = 7;
+    const CMD_START_ACCEPTING_PASSIVE_CHECKS = 8;
+    const CMD_DISABLE_NOTIFICATIONS = 9;
+    const CMD_ENABLE_NOTIFICATIONS = 10;
+    const CMD_SEND_CUSTOM_NOTIFICATION = 11;
+    const CMD_SCHEDULE_DOWNTIME = 12;
+    const CMD_SCHEDULE_DOWNTIMES_TO_ALL = 13;
+    const CMD_REMOVE_DOWNTIMES_FROM_ALL = 14;
+    const CMD_DISABLE_NOTIFICATIONS_FOR_ALL = 15;
+    const CMD_ENABLE_NOTIFICATIONS_FOR_ALL = 16;
+    const CMD_RESCHEDULE_NEXT_CHECK_TO_ALL = 17;
+    const CMD_DISABLE_ACTIVE_CHECKS_FOR_ALL = 18;
+    const CMD_ENABLE_ACTIVE_CHECKS_FOR_ALL = 19;
+    const CMD_DISABLE_EVENT_HANDLER = 20;
+    const CMD_ENABLE_EVENT_HANDLER = 21;
+    const CMD_DISABLE_FLAP_DETECTION = 22;
+    const CMD_ENABLE_FLAP_DETECTION = 23;
+    const CMD_ADD_COMMENT = 24;
+    const CMD_RESET_ATTRIBUTES = 25;
+    const CMD_ACKNOWLEDGE_PROBLEM = 26;
+    const CMD_REMOVE_ACKNOWLEDGEMENT = 27;
+    const CMD_DELAY_NOTIFICATION = 28;
+
+    /**
+     * Filter array for array displayed in small interfaces
+     * @var int[]
+     */
+    private static $commandSmallFilter = array(
+        self::CMD_RESCHEDULE_NEXT_CHECK,
+        self::CMD_ACKNOWLEDGE_PROBLEM,
+        self::CMD_REMOVE_ACKNOWLEDGEMENT
+    );
+
+    /**
+     * Information about interface commands
+     *
+     * With following structure
+     * <pre>
+     * array(
+     *  self::CMD_CONSTANT_* => array(
+     *   '<LONG DESCRIPTION WHERE %s is the type, e.g. host or service>',
+     *   '<SHORT DESCRIPTION WHERE %s is the type, e.g. host or service>',
+     *   '[ICON CSS CLASS]',
+     *   '[BUTTON CSS CLASS]',
+     *
+     *    // Maybe any other options later on
+     *  )
+     * )
+     * </pre>
+     *
+     * @var array
+     */
+    private static $commandInformation = array(
+        self::CMD_DISABLE_ACTIVE_CHECKS => array(
+            'Disable active checks for this %s', // Long description (mandatory)
+            'Disable active checks', // Short description (mandatory)
+            '', // Icon anything (optional)
+            '' // Button css cls (optional)
+        ),
+        self::CMD_ENABLE_ACTIVE_CHECKS => array(
+            'Enable active checks for this %s',
+            'Enable active checks',
+            ''
+        ),
+        self::CMD_RESCHEDULE_NEXT_CHECK => array(
+            'Reschedule next service check',
+            'Recheck',
+            '',
+            'btn-success'
+        ),
+        self::CMD_SUBMIT_PASSIVE_CHECK_RESULT => array(
+            'Submit passive check result',
+            'Submit check result',
+            ''
+        ),
+        self::CMD_STOP_OBSESSING => array(
+            'Stop obsessing over this %s',
+            'Stop obsessing',
+            ''
+        ),
+        self::CMD_START_OBSESSING => array(
+            'Start obsessing over this %s',
+            'Start obsessing',
+            ''
+        ),
+        self::CMD_STOP_ACCEPTING_PASSIVE_CHECKS => array(
+            'Stop accepting passive check for this %s',
+            'Stop passive checks',
+            ''
+        ),
+        self::CMD_START_ACCEPTING_PASSIVE_CHECKS => array(
+            'Start accepting passive check for this %s',
+            'Start passive checks',
+            ''
+        ),
+        self::CMD_DISABLE_NOTIFICATIONS => array(
+            'Disable notifications for this %s',
+            'Disable notifications',
+            ''
+        ),
+        self::CMD_ENABLE_NOTIFICATIONS => array(
+            'Enable notifications for this %s',
+            'Enable notifications',
+            ''
+        ),
+        self::CMD_SEND_CUSTOM_NOTIFICATION => array(
+            'Send custom %s notification',
+            'Send notification',
+            ''
+        ),
+        self::CMD_SCHEDULE_DOWNTIME => array(
+            'Schedule downtime for this %s',
+            'Schedule downtime',
+            ''
+        ),
+        self::CMD_SCHEDULE_DOWNTIMES_TO_ALL => array(
+            'Schedule downtime for this %s and all services',
+            'Schedule services downtime',
+            ''
+        ),
+        self::CMD_REMOVE_DOWNTIMES_FROM_ALL => array(
+            'Remove downtime(s) for this %s and all services',
+            'Remove downtime(s)',
+            ''
+        ),
+        self::CMD_DISABLE_NOTIFICATIONS_FOR_ALL => array(
+            'Disable notification for all service on this %s',
+            'Disable service notifications',
+            ''
+        ),
+        self::CMD_ENABLE_NOTIFICATIONS_FOR_ALL => array(
+            'Enable notification for all service on this %s',
+            'Enable service notifications',
+            ''
+        ),
+        self::CMD_RESCHEDULE_NEXT_CHECK_TO_ALL => array(
+            'Schedule a check of all service on this %s',
+            'Recheck all services',
+            '',
+            'btn-success'
+        ),
+        self::CMD_DISABLE_ACTIVE_CHECKS_FOR_ALL => array(
+            'Disable checks for all services on this %s',
+            'Disable service checks',
+            ''
+        ),
+        self::CMD_ENABLE_ACTIVE_CHECKS_FOR_ALL => array(
+            'Enable checks for all services on this %s',
+            'Enable service checks',
+            ''
+        ),
+        self::CMD_DISABLE_EVENT_HANDLER => array(
+            'Disable event handler for this %s',
+            'Disable event handler',
+            ''
+        ),
+        self::CMD_ENABLE_EVENT_HANDLER => array(
+            'Enable event handler for this %s',
+            'Enable event handler',
+            ''
+        ),
+        self::CMD_DISABLE_FLAP_DETECTION => array(
+            'Disable flap detection for this %s',
+            'Disable flap detection',
+            ''
+        ),
+        self::CMD_ENABLE_FLAP_DETECTION => array(
+            'Enable flap detection for this %s',
+            'Enable flap detection',
+            ''
+        ),
+        self::CMD_ADD_COMMENT => array(
+            'Add new %s comment',
+            'Add comment',
+            ''
+        ),
+        self::CMD_RESET_ATTRIBUTES => array(
+            'Reset modified attributes',
+            'Reset attributes',
+            '',
+            'btn-danger'
+        ),
+        self::CMD_ACKNOWLEDGE_PROBLEM => array(
+            'Acknowledge %s problem',
+            'Acknowledge',
+            '',
+            'btn-warning'
+        ),
+        self::CMD_REMOVE_ACKNOWLEDGEMENT => array(
+            'Remove %s acknowledgement',
+            'Remove acknowledgement',
+            '',
+            'btn-warning'
+        ),
+        self::CMD_DELAY_NOTIFICATION => array(
+            'Delay next %s notification',
+            'Delay notification',
+            ''
+        ),
+    );
+
+    /**
+     * An mapping array which is valid for hosts and services
+     * @var array
+     */
+    private static $defaultObjectCommands = array(
+        self::CMD_DISABLE_ACTIVE_CHECKS,
+        self::CMD_ENABLE_ACTIVE_CHECKS,
+        self::CMD_RESCHEDULE_NEXT_CHECK,
+        self::CMD_SUBMIT_PASSIVE_CHECK_RESULT,
+        self::CMD_STOP_OBSESSING,
+        self::CMD_START_OBSESSING,
+        self::CMD_ACKNOWLEDGE_PROBLEM,
+        self::CMD_REMOVE_ACKNOWLEDGEMENT,
+        self::CMD_STOP_ACCEPTING_PASSIVE_CHECKS,
+        self::CMD_START_ACCEPTING_PASSIVE_CHECKS,
+        self::CMD_DISABLE_NOTIFICATIONS,
+        self::CMD_ENABLE_NOTIFICATIONS,
+        self::CMD_SEND_CUSTOM_NOTIFICATION,
+        self::CMD_SCHEDULE_DOWNTIME,
+        self::CMD_SCHEDULE_DOWNTIMES_TO_ALL,
+        self::CMD_REMOVE_DOWNTIMES_FROM_ALL,
+        self::CMD_DISABLE_NOTIFICATIONS_FOR_ALL,
+        self::CMD_ENABLE_NOTIFICATIONS_FOR_ALL,
+        self::CMD_RESCHEDULE_NEXT_CHECK_TO_ALL,
+        self::CMD_DISABLE_ACTIVE_CHECKS_FOR_ALL,
+        self::CMD_ENABLE_ACTIVE_CHECKS_FOR_ALL,
+        self::CMD_DISABLE_EVENT_HANDLER,
+        self::CMD_ENABLE_EVENT_HANDLER,
+        self::CMD_DISABLE_FLAP_DETECTION,
+        self::CMD_ENABLE_FLAP_DETECTION,
+        self::CMD_ADD_COMMENT,
+        self::CMD_RESET_ATTRIBUTES,
+        self::CMD_DELAY_NOTIFICATION
+    );
+
+    /**
+     * Command mapper
+     *
+     * Holds information about commands for object types
+     *
+     * Please note that host and service are implicit initialized!
+     * see $defaultObjectCommands for definition
+     *
+     * @var array
+     */
+    private static $objectCommands = array(
+        // 'host' => self::$defaultObjectCommands,
+        // 'service' => self::$defaultObjectCommands,
+        // -> this is done in self::initCommandStructure()
+        //
+        // Real other commands structures:
+        // NONE
+    );
+
+    /**
+     * Array of modifier methods in this class
+     *
+     * Maps object type to modifier method which rewrites
+     * visible commands depending on object states
+     *
+     * @var array
+     */
+    private static $commandProcessorMethods = array(
+        'host' => 'defaultCommandProcessor',
+        'service' => 'defaultCommandProcessor'
+    );
+
+    /**
+     * Init flag for lazy, static data preparation
+     * @var bool
+     */
+    private static $initialized = false;
+
     /**
      * Array of available categories
      * @var array
      */
-    private static $categories = array(
-
-    );
+    private static $rawCategories = array();
 
     /**
      * Categories to command names
      * @var array
      */
-    private static $categoriesToCommands = array();
+    private static $rawCommandCategories = array();
 
     /**
      * Command reference list
      * @var array
      */
-    private static $commands = array(
+    private static $rawCommands = array(
         'NONE' => 'none',
-        'ADD_HOST_COMMENT' => 'host,interface',
+        'ADD_HOST_COMMENT' => 'host',
         'DEL_HOST_COMMENT' => 'host',
         'ADD_SVC_COMMENT' => 'service',
         'DEL_SVC_COMMENT' => 'service',
@@ -72,14 +367,14 @@ class Meta
         'SHUTDOWN_PROCESS' => 'global',
         'ENABLE_HOST_SVC_CHECKS' => 'host',
         'DISABLE_HOST_SVC_CHECKS' => 'host',
-        'SCHEDULE_HOST_SVC_CHECKS' => 'host,interface',
+        'SCHEDULE_HOST_SVC_CHECKS' => 'host',
         'DELAY_HOST_SVC_NOTIFICATIONS' => 'host',
-        'DEL_ALL_HOST_COMMENTS' => 'host,',
+        'DEL_ALL_HOST_COMMENTS' => 'host',
         'DEL_ALL_SVC_COMMENTS' => 'service',
         'ENABLE_SVC_NOTIFICATIONS' => 'service',
         'DISABLE_SVC_NOTIFICATIONS' => 'service',
-        'ENABLE_HOST_NOTIFICATIONS' => 'host,interface',
-        'DISABLE_HOST_NOTIFICATIONS' => 'host,interface',
+        'ENABLE_HOST_NOTIFICATIONS' => 'host',
+        'DISABLE_HOST_NOTIFICATIONS' => 'host',
         'ENABLE_ALL_NOTIFICATIONS_BEYOND_HOST' => 'host',
         'DISABLE_ALL_NOTIFICATIONS_BEYOND_HOST' => 'host',
         'ENABLE_HOST_SVC_NOTIFICATIONS' => 'host',
@@ -87,7 +382,7 @@ class Meta
         'PROCESS_SERVICE_CHECK_RESULT' => 'service',
         'SAVE_STATE_INFORMATION' => 'global',
         'READ_STATE_INFORMATION' => 'global',
-        'ACKNOWLEDGE_HOST_PROBLEM' => 'host,interface',
+        'ACKNOWLEDGE_HOST_PROBLEM' => 'host',
         'ACKNOWLEDGE_SVC_PROBLEM' => 'service',
         'START_EXECUTING_SVC_CHECKS' => 'service',
         'STOP_EXECUTING_SVC_CHECKS' => 'service',
@@ -97,22 +392,22 @@ class Meta
         'DISABLE_PASSIVE_SVC_CHECKS' => 'service',
         'ENABLE_EVENT_HANDLERS' => 'global',
         'DISABLE_EVENT_HANDLERS' => 'global',
-        'ENABLE_HOST_EVENT_HANDLER' => 'host,interface',
-        'DISABLE_HOST_EVENT_HANDLER' => 'host,interface',
+        'ENABLE_HOST_EVENT_HANDLER' => 'host',
+        'DISABLE_HOST_EVENT_HANDLER' => 'host',
         'ENABLE_SVC_EVENT_HANDLER' => 'service',
         'DISABLE_SVC_EVENT_HANDLER' => 'service',
-        'ENABLE_HOST_CHECK' => 'host,interface',
-        'DISABLE_HOST_CHECK' => 'host,interface',
+        'ENABLE_HOST_CHECK' => 'host',
+        'DISABLE_HOST_CHECK' => 'host',
         'START_OBSESSING_OVER_SVC_CHECKS' => 'service',
         'STOP_OBSESSING_OVER_SVC_CHECKS' => 'service',
-        'REMOVE_HOST_ACKNOWLEDGEMENT' => 'host,interface',
+        'REMOVE_HOST_ACKNOWLEDGEMENT' => 'host',
         'REMOVE_SVC_ACKNOWLEDGEMENT' => 'service',
         'SCHEDULE_FORCED_HOST_SVC_CHECKS' => 'host',
         'SCHEDULE_FORCED_SVC_CHECK' => 'service',
-        'SCHEDULE_HOST_DOWNTIME' => 'host,interface',
+        'SCHEDULE_HOST_DOWNTIME' => 'host',
         'SCHEDULE_SVC_DOWNTIME' => 'service',
-        'ENABLE_HOST_FLAP_DETECTION' => 'host,interface',
-        'DISABLE_HOST_FLAP_DETECTION' => 'host,interface',
+        'ENABLE_HOST_FLAP_DETECTION' => 'host',
+        'DISABLE_HOST_FLAP_DETECTION' => 'host',
         'ENABLE_SVC_FLAP_DETECTION' => 'service',
         'DISABLE_SVC_FLAP_DETECTION' => 'service',
         'ENABLE_FLAP_DETECTION' => 'global',
@@ -141,17 +436,17 @@ class Meta
         'SCHEDULE_HOSTGROUP_HOST_DOWNTIME' => 'hostgroup',
         'SCHEDULE_HOSTGROUP_SVC_DOWNTIME' => 'hostgroup',
         'SCHEDULE_HOST_SVC_DOWNTIME' => 'host',
-        'PROCESS_HOST_CHECK_RESULT' => 'host,interface',
-        'START_EXECUTING_HOST_CHECKS' => 'host,interface',
-        'STOP_EXECUTING_HOST_CHECKS' => 'host,interface',
-        'START_ACCEPTING_PASSIVE_HOST_CHECKS' => 'host,interface',
-        'STOP_ACCEPTING_PASSIVE_HOST_CHECKS' => 'host,interface',
-        'ENABLE_PASSIVE_HOST_CHECKS' => 'host.interface',
-        'DISABLE_PASSIVE_HOST_CHECKS' => 'host,interface',
-        'START_OBSESSING_OVER_HOST_CHECKS' => 'host,interface',
-        'STOP_OBSESSING_OVER_HOST_CHECKS' => 'host,interface',
-        'SCHEDULE_HOST_CHECK' => 'host,interface',
-        'SCHEDULE_FORCED_HOST_CHECK' => 'host,interface',
+        'PROCESS_HOST_CHECK_RESULT' => 'host',
+        'START_EXECUTING_HOST_CHECKS' => 'host',
+        'STOP_EXECUTING_HOST_CHECKS' => 'host',
+        'START_ACCEPTING_PASSIVE_HOST_CHECKS' => 'host',
+        'STOP_ACCEPTING_PASSIVE_HOST_CHECKS' => 'host',
+        'ENABLE_PASSIVE_HOST_CHECKS' => 'host',
+        'DISABLE_PASSIVE_HOST_CHECKS' => 'host',
+        'START_OBSESSING_OVER_HOST_CHECKS' => 'host',
+        'STOP_OBSESSING_OVER_HOST_CHECKS' => 'host',
+        'SCHEDULE_HOST_CHECK' => 'host',
+        'SCHEDULE_FORCED_HOST_CHECK' => 'host',
         'START_OBSESSING_OVER_SVC' => 'global',
         'STOP_OBSESSING_OVER_SVC' => 'global',
         'START_OBSESSING_OVER_HOST' => 'global',
@@ -233,45 +528,45 @@ class Meta
         'CUSTOM_COMMAND' => 'none',
     );
 
-    /**
-     * Labels of commands
-     * @var array
-     */
-    private static $commandLabels = array();
-
     /**
      * Initialize command structures only once
      */
     private static function initCommandStructure()
     {
-        static $initialized = false;
-
-        if ($initialized === true) {
+        if (self::$initialized === true) {
             return;
         }
 
+        /*
+         * Build everything for raw commands
+         */
         $categories = array();
-        foreach (self::$commands as $commandName => $categoryName) {
-            $flags = explode(',', $categoryName);
-            $categoryName = array_shift($flags);
+        foreach (self::$rawCommands as $commandName => $categoryName) {
             // We do not want to see useless commands
             if ($categoryName === self::DROP_CATEGORY) {
-                unset(self::$commands[$commandName]);
+                unset(self::$rawCommands[$commandName]);
                 continue;
             }
 
             $categories[$categoryName] = null;
 
-            if (array_key_exists($categoryName, self::$categoriesToCommands) === false) {
-                self::$categoriesToCommands[$categoryName] = array();
+            if (array_key_exists($categoryName, self::$rawCommandCategories) === false) {
+                self::$rawCommandCategories[$categoryName] = array();
             }
 
-            self::$categoriesToCommands[$categoryName][$commandName] = $flags;
+            self::$rawCommandCategories[$categoryName][] = $commandName;
         }
 
-        self::$categories = $categories;
+        self::$rawCategories = array_keys($categories);
+        sort(self::$rawCategories);
 
-        $initialized = true;
+        /*
+         * Build everything for object commands
+         */
+        self::$objectCommands['host'] = self::$defaultObjectCommands;
+        self::$objectCommands['service'] = self::$defaultObjectCommands;
+
+        self::$initialized = true;
     }
 
     /**
@@ -286,12 +581,12 @@ class Meta
      * Return a full list of commands
      * @return string[]
      */
-    public function getCommands()
+    public function getRawCommands()
     {
         static $commands = null;
 
         if ($commands === null) {
-            $commands = array_keys(self::$commands);
+            $commands = array_keys(self::$rawCommands);
         }
 
         return $commands;
@@ -302,17 +597,10 @@ class Meta
      * @param string $categoryName
      * @return string[]
      */
-    public function getCommandsForCategory($categoryName)
+    public function getRawCommandsForCategory($categoryName)
     {
-        static $commands = null;
-
-        $this->assertCategoryExistence($categoryName);
-
-        if (!$commands === null) {
-            $commands = array_keys(self::$categoriesToCommands[$category]);
-        }
-
-        return $commands;
+        $this->assertRawCategoryExistence($categoryName);
+        return array_values(self::$rawCommandCategories[$categoryName]);
     }
 
     /**
@@ -320,10 +608,10 @@ class Meta
      * @param string $categoryName
      * @throws \Icinga\Exception\ProgrammingError
      */
-    private function assertCategoryExistence($categoryName)
+    private function assertRawCategoryExistence($categoryName)
     {
-        if (array_key_exists($categoryName, self::$categoriesToCommands) === false) {
-            throw new ProgrammingError('Category does not exists: '. $categoryName);
+        if (array_key_exists($categoryName, self::$rawCommandCategories) === false) {
+            throw new ProgrammingError('Category does not exists: ' . $categoryName);
         }
     }
 
@@ -331,14 +619,225 @@ class Meta
      * Return a list of all categories
      * @return string[]
      */
-    public function getCategories()
+    public function getRawCategories()
     {
-        return self::$categories;
+        return self::$rawCategories;
     }
 
-    public function getCommandsForObject(\stdClass $object, User $user = null)
+    /**
+     * Returns the type of object
+     *
+     * This is made by the first key of property
+     * e.g.
+     *      $object->host_state
+     *      Type is 'host'
+     *
+     * @param \stdClass $object
+     * @return mixed
+     */
+    private function getObjectType(\stdClass $object)
     {
-        $items = self::$categoriesToCommands['host'];
-        var_dump($items);
+        return array_shift(explode('_', array_shift(array_keys(get_object_vars($object))), 2));
     }
-}
\ No newline at end of file
+
+    /**
+     * Returns method name based on object type
+     * @param string $type
+     * @return string
+     * @throws \Icinga\Exception\ProgrammingError
+     */
+    private function getCommandProcessorMethod($type)
+    {
+        if (array_key_exists($type, self::$commandProcessorMethods)) {
+            $method = self::$commandProcessorMethods[$type];
+            if (is_callable(array(&$this, $method))) {
+                return $method;
+            }
+        }
+
+        throw new ProgrammingError('Type has no command processor: '. $type);
+    }
+
+    /**
+     * Return interface commands by object type
+     * @param string $type
+     * @return array
+     * @throws \Icinga\Exception\ProgrammingError
+     */
+    private function getCommandsByType($type)
+    {
+        if (array_key_exists($type, self::$objectCommands)) {
+            return self::$objectCommands[$type];
+        }
+
+        throw new ProgrammingError('Type has no commands defined: '. $type);
+    }
+
+    /**
+     * Modifies data objects to drop their object type
+     *
+     * - host_state will be state
+     * - service_state will be also state
+     * - And so on
+     *
+     * @param \stdClass $object
+     * @param $type
+     * @return object
+     */
+    private function dropTypeAttributes(\stdClass $object, $type)
+    {
+        $objectData = get_object_vars($object);
+        foreach ($objectData as $propertyName => $propertyValue) {
+            $newProperty = str_replace($type. '_', '', $propertyName);
+            $objectData[$newProperty] = $propertyValue;
+            unset($objectData[$propertyName]);
+        }
+        return (object)$objectData;
+    }
+
+    /**
+     * Default processor for host and service objects
+     *
+     * Drop commands from list based on states and object properties
+     *
+     * @param \stdClass $object
+     * @param array $commands
+     * @param string $type
+     * @return array
+     */
+    private function defaultCommandProcessor(\stdClass $object, array $commands, $type)
+    {
+        $object = $this->dropTypeAttributes($object, $type);
+
+        $commands = array_flip($commands);
+
+        if ($object->active_checks_enabled === '1') {
+            unset($commands[self::CMD_ENABLE_ACTIVE_CHECKS]);
+        } else {
+            unset($commands[self::CMD_DISABLE_ACTIVE_CHECKS]);
+        }
+
+        if ($object->passive_checks_enabled !== '1') {
+            unset($commands[self::CMD_SUBMIT_PASSIVE_CHECK_RESULT]);
+        }
+
+        if ($object->passive_checks_enabled === '1') {
+            unset($commands[self::CMD_STOP_ACCEPTING_PASSIVE_CHECKS]);
+        } else {
+            unset($commands[self::CMD_START_ACCEPTING_PASSIVE_CHECKS]);
+        }
+
+        if ($object->obsess_over_host === '1') {
+            unset($commands[self::CMD_START_OBSESSING]);
+        } else {
+            unset($commands[self::CMD_STOP_OBSESSING]);
+        }
+
+        if ($object->state !== '0') {
+            if ($object->acknowledged === '1') {
+                unset($commands[self::CMD_ACKNOWLEDGE_PROBLEM]);
+            } else {
+                unset($commands[self::CMD_REMOVE_ACKNOWLEDGEMENT]);
+            }
+        } else {
+            unset($commands[self::CMD_ACKNOWLEDGE_PROBLEM]);
+            unset($commands[self::CMD_REMOVE_ACKNOWLEDGEMENT]);
+        }
+
+        if ($object->notifications_enabled === '1') {
+            unset($commands[self::CMD_ENABLE_NOTIFICATIONS]);
+        } else {
+            unset($commands[self::CMD_DISABLE_NOTIFICATIONS]);
+        }
+
+        if ($object->event_handler_enabled === '1') {
+            unset($commands[self::CMD_ENABLE_EVENT_HANDLER]);
+        } else {
+            unset($commands[self::CMD_DISABLE_EVENT_HANDLER]);
+        }
+
+        if ($object->flap_detection_enabled === '1') {
+            unset($commands[self::CMD_ENABLE_FLAP_DETECTION]);
+        } else {
+            unset($commands[self::CMD_DISABLE_FLAP_DETECTION]);
+        }
+
+        return array_flip($commands);
+    }
+
+    /**
+     * Creates structure to work with in interfaces
+     *
+     * @param array $commands
+     * @param string $objectType
+     * @return array
+     */
+    private function buildInterfaceConfiguration(array $commands, $objectType)
+    {
+        $out = array();
+        foreach ($commands as $index => $commandId) {
+
+            $command = new \stdClass();
+            $command->id = $commandId;
+            if (array_key_exists($commandId, self::$commandInformation)) {
+                $command->shortDescription = sprintf(self::$commandInformation[$commandId][1], $objectType);
+                $command->longDescription = sprintf(self::$commandInformation[$commandId][0], $objectType);
+                $command->iconCls =
+                    (isset(self::$commandInformation[$commandId][2]))
+                    ? self::$commandInformation[$commandId][2]
+                    : '';
+                $command->btnCls =
+                    (isset(self::$commandInformation[$commandId][3]))
+                    ? self::$commandInformation[$commandId][3]
+                    : '';
+            }
+
+            $out[] = $command;
+        }
+
+        return $out;
+    }
+
+    /**
+     * Drop commands
+     * For small interfaces or bypass when full interfaces are needed
+     * @param array $commands
+     * @param string $type
+     * @return array
+     */
+    private function filterInterfaceType(array $commands, $type)
+    {
+        if ($type === self::TYPE_FULL) {
+            return $commands;
+        }
+
+        foreach ($commands as $arrayId => $commandId) {
+            if (in_array($commandId, self::$commandSmallFilter) === false) {
+                unset($commands[$arrayId]);
+            }
+        }
+
+        return $commands;
+    }
+
+    /**
+     * Get commands for an object
+     *
+     * Based on objects and interface type
+     *
+     * @param \stdClass $object
+     * @param $interfaceType
+     * @param User $user
+     * @return array
+     */
+    public function getCommandForObject(\stdClass $object, $interfaceType, User $user = null)
+    {
+        $objectType = $this->getObjectType($object);
+        $commands = $this->getCommandsByType($objectType);
+        $method = $this->getCommandProcessorMethod($objectType);
+        $commands = $this->$method($object, $commands, $objectType);
+        $commands = $this->filterInterfaceType($commands, $interfaceType);
+        $commands = $this->buildInterfaceConfiguration($commands, $objectType);
+        return $commands;
+    }
+}
diff --git a/modules/monitoring/test/php/application/views/helpers/MonitoringCommandsTest.php b/modules/monitoring/test/php/application/views/helpers/MonitoringCommandsTest.php
new file mode 100644
index 000000000..8d3060c85
--- /dev/null
+++ b/modules/monitoring/test/php/application/views/helpers/MonitoringCommandsTest.php
@@ -0,0 +1,116 @@
+<?php
+
+namespace Test\Modules\Monitoring\Application\Views\Helpers;
+
+use Icinga\Monitoring\Command\Meta;
+
+require_once 'Zend/View/Helper/Abstract.php';
+require_once 'Zend/View.php';
+require_once __DIR__. '/../../../../../application/views/helpers/MonitoringCommands.php';
+require_once __DIR__. '/../../../../../library/Monitoring/Command/Meta.php';
+require_once __DIR__. '/../../../../../../../library/Icinga/Exception/ProgrammingError.php';
+
+class HostStruct extends \stdClass
+{
+    public $host_name = 'localhost';
+    public $host_address = '127.0.0.1';
+    public $host_state = '1';
+    public $host_handled = '1';
+    public $host_in_downtime = '1';
+    public $host_acknowledged = '1';
+    public $host_check_command = 'check-host-alive';
+    public $host_last_state_change = '1372937083';
+    public $host_alias = 'localhost';
+    public $host_output = 'DDD';
+    public $host_long_output = '';
+    public $host_perfdata = '';
+    public $host_current_check_attempt = '1';
+    public $host_max_check_attempts = '10';
+    public $host_attempt = '1/10';
+    public $host_last_check = '2013-07-04 11:24:42';
+    public $host_next_check = '2013-07-04 11:29:43';
+    public $host_check_type = '1';
+    public $host_last_hard_state_change = '2013-07-04 11:24:43';
+    public $host_last_hard_state = '0';
+    public $host_last_time_up = '2013-07-04 11:20:23';
+    public $host_last_time_down = '2013-07-04 11:24:43';
+    public $host_last_time_unreachable = '0000-00-00 00:00:00';
+    public $host_state_type = '1';
+    public $host_last_notification = '0000-00-00 00:00:00';
+    public $host_next_notification = '0000-00-00 00:00:00';
+    public $host_no_more_notifications = '0';
+    public $host_notifications_enabled = '1';
+    public $host_problem_has_been_acknowledged = '1';
+    public $host_acknowledgement_type = '2';
+    public $host_current_notification_number = '0';
+    public $host_passive_checks_enabled = '1';
+    public $host_active_checks_enabled = '0';
+    public $host_event_handler_enabled = '0';
+    public $host_flap_detection_enabled = '1';
+    public $host_is_flapping = '0';
+    public $host_percent_state_change = '12.36842';
+    public $host_latency = '0.12041';
+    public $host_execution_time = '0';
+    public $host_scheduled_downtime_depth = '1';
+    public $host_failure_prediction_enabled = '1';
+    public $host_process_performance_data = '1';
+    public $host_obsess_over_host = '1';
+    public $host_modified_host_attributes = '14';
+    public $host_event_handler = '';
+    public $host_normal_check_interval = '5';
+    public $host_retry_check_interval = '1';
+    public $host_check_timeperiod_object_id = '27';
+}
+
+class MonitoringCommandsTest extends \PHPUnit_Framework_TestCase {
+
+    public function testOutput1()
+    {
+        $helper = new \Zend_View_Helper_MonitoringCommands();
+        $object = new HostStruct();
+
+        $html = $helper->monitoringCommands($object, Meta::TYPE_SMALL);
+
+        $this->assertContains('Remove acknowledgement', $html);
+        $this->assertContains('Recheck', $html);
+        $this->assertNotContains('Obsess', $html);
+        $this->assertNotContains('Enable', $html);
+        $this->assertNotContains('Disable', $html);
+    }
+
+    public function testOutput2()
+    {
+        $helper = new \Zend_View_Helper_MonitoringCommands();
+        $object = new HostStruct();
+
+        $html = $helper->monitoringCommands($object, Meta::TYPE_FULL);
+
+        $dom = new \DOMDocument('1.0', 'utf-8');
+        $dom->loadXML($html);
+
+        $xpath = new \DOMXPath($dom);
+        $nodes = $xpath->query('//div[@class=\'command-section pull-left\']');
+
+        $this->assertEquals(5, $nodes->length);
+
+        $nodes = $xpath->query('//button');
+        $this->assertEquals(21, $nodes->length);
+    }
+
+    public function testOutput3()
+    {
+        $helper = new \Zend_View_Helper_MonitoringCommands();
+        $object = new HostStruct();
+        $object->host_state = '0';
+
+        $html = $helper->monitoringCommands($object, Meta::TYPE_FULL);
+
+        $dom = new \DOMDocument('1.0', 'utf-8');
+        $dom->loadXML($html);
+
+        $xpath = new \DOMXPath($dom);
+
+        $nodes = $xpath->query('//button');
+        $this->assertEquals(20, $nodes->length);
+    }
+}
\ No newline at end of file
diff --git a/test/php/modules/monitoring/application/views/helpers/MonitoringFlagsTest.php b/modules/monitoring/test/php/application/views/helpers/MonitoringFlagsTest.php
similarity index 96%
rename from test/php/modules/monitoring/application/views/helpers/MonitoringFlagsTest.php
rename to modules/monitoring/test/php/application/views/helpers/MonitoringFlagsTest.php
index f8b031dd9..b03eff55f 100644
--- a/test/php/modules/monitoring/application/views/helpers/MonitoringFlagsTest.php
+++ b/modules/monitoring/test/php/application/views/helpers/MonitoringFlagsTest.php
@@ -4,7 +4,7 @@ namespace Test\Modules\Monitoring\Application\Views\Helpers;
 
 require_once 'Zend/View/Helper/Abstract.php';
 require_once 'Zend/View.php';
-require_once __DIR__. '/../../../../../../../modules/monitoring/application/views/helpers/MonitoringFlags.php';
+require_once __DIR__. '/../../../../../application/views/helpers/MonitoringFlags.php';
 
 class MonitoringFlagsTest extends \PHPUnit_Framework_TestCase
 {
diff --git a/modules/monitoring/test/php/library/Command/MetaTest.php b/modules/monitoring/test/php/library/Command/MetaTest.php
new file mode 100644
index 000000000..dc06e10ba
--- /dev/null
+++ b/modules/monitoring/test/php/library/Command/MetaTest.php
@@ -0,0 +1,185 @@
+<?php
+
+namespace Test\Modules\Monitoring\Library\Command;
+
+require_once __DIR__. '/../../../../library/Monitoring/Command/Meta.php';
+require_once __DIR__. '/../../../../../../library/Icinga/Exception/ProgrammingError.php';
+
+use Icinga\Monitoring\Command\Meta;
+
+class HostStruct extends \stdClass
+{
+    public $host_name = 'localhost';
+    public $host_address = '127.0.0.1';
+    public $host_state = '1';
+    public $host_handled = '1';
+    public $host_in_downtime = '1';
+    public $host_acknowledged = '1';
+    public $host_check_command = 'check-host-alive';
+    public $host_last_state_change = '1372937083';
+    public $host_alias = 'localhost';
+    public $host_output = 'DDD';
+    public $host_long_output = '';
+    public $host_perfdata = '';
+    public $host_current_check_attempt = '1';
+    public $host_max_check_attempts = '10';
+    public $host_attempt = '1/10';
+    public $host_last_check = '2013-07-04 11:24:42';
+    public $host_next_check = '2013-07-04 11:29:43';
+    public $host_check_type = '1';
+    public $host_last_hard_state_change = '2013-07-04 11:24:43';
+    public $host_last_hard_state = '0';
+    public $host_last_time_up = '2013-07-04 11:20:23';
+    public $host_last_time_down = '2013-07-04 11:24:43';
+    public $host_last_time_unreachable = '0000-00-00 00:00:00';
+    public $host_state_type = '1';
+    public $host_last_notification = '0000-00-00 00:00:00';
+    public $host_next_notification = '0000-00-00 00:00:00';
+    public $host_no_more_notifications = '0';
+    public $host_notifications_enabled = '1';
+    public $host_problem_has_been_acknowledged = '1';
+    public $host_acknowledgement_type = '2';
+    public $host_current_notification_number = '0';
+    public $host_passive_checks_enabled = '1';
+    public $host_active_checks_enabled = '0';
+    public $host_event_handler_enabled = '0';
+    public $host_flap_detection_enabled = '1';
+    public $host_is_flapping = '0';
+    public $host_percent_state_change = '12.36842';
+    public $host_latency = '0.12041';
+    public $host_execution_time = '0';
+    public $host_scheduled_downtime_depth = '1';
+    public $host_failure_prediction_enabled = '1';
+    public $host_process_performance_data = '1';
+    public $host_obsess_over_host = '1';
+    public $host_modified_host_attributes = '14';
+    public $host_event_handler = '';
+    public $host_normal_check_interval = '5';
+    public $host_retry_check_interval = '1';
+    public $host_check_timeperiod_object_id = '27';
+}
+
+class MetaTest extends \PHPUnit_Framework_TestCase
+{
+    public function testRawCommands1()
+    {
+        $meta = new Meta();
+        $commands = $meta->getRawCommands();
+
+        $this->assertCount(173, $commands);
+
+        $this->assertTrue(in_array('SCHEDULE_SERVICEGROUP_SVC_DOWNTIME', $commands));
+        $this->assertTrue(in_array('SCHEDULE_SVC_CHECK', $commands));
+        $this->assertTrue(in_array('ENABLE_HOSTGROUP_SVC_CHECKS', $commands));
+        $this->assertTrue(in_array('PROCESS_HOST_CHECK_RESULT', $commands));
+        $this->assertTrue(in_array('ACKNOWLEDGE_SVC_PROBLEM', $commands));
+        $this->assertTrue(in_array('ACKNOWLEDGE_HOST_PROBLEM', $commands));
+        $this->assertTrue(in_array('SCHEDULE_FORCED_SVC_CHECK', $commands));
+        $this->assertTrue(in_array('DISABLE_FLAP_DETECTION', $commands));
+    }
+
+    public function testRawCommands2()
+    {
+        $meta = new Meta();
+        $categories = $meta->getRawCategories();
+
+        $this->assertCount(7, $categories);
+
+        $this->assertEquals(
+            array(
+                'comment',
+                'contact',
+                'global',
+                'host',
+                'hostgroup',
+                'service',
+                'servicegroup'
+            ),
+            $categories
+        );
+    }
+
+    public function testRawCommands3()
+    {
+        $meta = new Meta();
+
+        $this->assertCount(9, $meta->getRawCommandsForCategory('hostgroup'));
+        $this->assertCount(14, $meta->getRawCommandsForCategory('servicegroup'));
+
+        $test1 = $meta->getRawCommandsForCategory('global');
+        $this->count(26, $test1);
+
+        $this->assertTrue(in_array('DISABLE_NOTIFICATIONS', $test1));
+        $this->assertTrue(in_array('RESTART_PROCESS', $test1));
+        $this->assertTrue(in_array('ENABLE_FLAP_DETECTION', $test1));
+        $this->assertTrue(in_array('PROCESS_FILE', $test1));
+    }
+
+    /**
+     * @expectedException Icinga\Exception\ProgrammingError
+     * @expectedExceptionMessage Category does not exists: DOES_NOT_EXIST
+     */
+    public function testRawCommands4()
+    {
+        $meta = new Meta();
+        $meta->getRawCommandsForCategory('DOES_NOT_EXIST');
+    }
+
+    public function testObjectForCommand1()
+    {
+        $meta = new Meta();
+
+        $object = new HostStruct();
+
+        $commands = $meta->getCommandForObject($object, Meta::TYPE_SMALL);
+
+        $this->assertEquals(3, $commands[0]->id);
+        $this->assertEquals(27, $commands[1]->id);
+
+        $object->host_state = '0';
+
+        $commands = $meta->getCommandForObject($object, Meta::TYPE_SMALL);
+
+        $this->assertEquals(3, $commands[0]->id);
+        $this->assertFalse(isset($commands[1])); // STATE IS OK AGAIN
+
+        $object->host_state = '1';
+        $object->host_acknowledged = '0';
+
+        $commands = $meta->getCommandForObject($object, Meta::TYPE_SMALL);
+
+        $this->assertEquals(3, $commands[0]->id);
+        $this->assertEquals(26, $commands[1]->id);
+    }
+
+    public function testObjectForCommand2()
+    {
+        $meta = new Meta();
+
+        $object = new HostStruct();
+
+        $object->host_obsess_over_host = '0';
+        $object->host_flap_detection_enabled = '0';
+        $object->host_active_checks_enabled = '0';
+
+        $commands = $meta->getCommandForObject($object, Meta::TYPE_FULL);
+
+        $this->assertEquals(2, $commands[0]->id);
+        $this->assertEquals(6, $commands[3]->id);
+    }
+
+    /**
+     * @expectedException Icinga\Exception\ProgrammingError
+     * @expectedExceptionMessage Type has no commands defined: UNKNOWN
+     */
+    public function testObjectForCommand3()
+    {
+        $meta = new Meta();
+
+        $test = new \stdClass();
+        $test->UNKNOWN_state = '2';
+        $test->UNKNOWN_flap_detection_enabled = '1';
+
+        $commands = $meta->getCommandForObject($test, Meta::TYPE_FULL);
+    }
+}
\ No newline at end of file
diff --git a/public/css/components/details.less b/public/css/components/details.less
index 4bcd4a3ea..1fb17c8d0 100755
--- a/public/css/components/details.less
+++ b/public/css/components/details.less
@@ -110,12 +110,19 @@ font-size: 12px;
   .flag {
     display: inline-block;
     padding: 2px;
+    width: 80px;
+    text-align: center;
   }
 
   .flag-enabled {
     background-color: #00ff00;
   }
 
+  .flag-disabled {
+    background-color: #cc0000;
+    color: white;
+  }
+
   th {
     text-align: left;
   }
@@ -127,4 +134,20 @@ font-size: 12px;
   td, th {
     border: 1px #ff0000 solid;
   }
+}
+
+.command-container {
+
+  div.command-section {
+    overflow: hidden;
+    margin: 0 0 0 10px;
+  }
+
+  div.command-section div {
+    margin: 4px 0 0 0;
+  }
+
+  div.command-section div button {
+    width: 100%;
+  }
 }
\ No newline at end of file
diff --git a/test/php/phpunit.xml b/test/php/phpunit.xml
index ceabd9dc4..32b7fcdd0 100644
--- a/test/php/phpunit.xml
+++ b/test/php/phpunit.xml
@@ -8,7 +8,10 @@
             <directory>application/</directory>
             <directory>bin/</directory>
             <directory>library/</directory>
-            <directory>modules/</directory>
+
+            <!-- Module tests are independent from core tests -->
+            <directory>../../modules/*/test/php</directory>
+            <exclude>../../modules/*/test/php/regression</exclude>
         </testsuite>
 
         <!--
@@ -16,6 +19,7 @@
         -->
         <testsuite name="regression">
             <directory>regression/</directory>
+            <directory>../../modules/*/test/regression</directory>
         </testsuite>
     </testsuites>
     <filter>