diff --git a/application/controllers/ConfigController.php b/application/controllers/ConfigController.php index 9318520e8..9602396ed 100644 --- a/application/controllers/ConfigController.php +++ b/application/controllers/ConfigController.php @@ -39,28 +39,34 @@ class ConfigController extends ActionController $allowedActions = array(); if ($auth->hasPermission('system/config/application')) { $tabs->add('application', array( - 'title' => $this->translate('Application'), + 'title' => $this->translate('Adjust the general configuration of Icinga Web 2'), + 'label' => $this->translate('Application'), 'url' => 'config/application' )); $allowedActions[] = 'application'; } if ($auth->hasPermission('system/config/authentication')) { $tabs->add('authentication', array( - 'title' => $this->translate('Authentication'), + 'title' => $this->translate('Configure how users authenticate with and log into Icinga Web 2'), + 'label' => $this->translate('Authentication'), 'url' => 'config/authentication' )); $allowedActions[] = 'authentication'; } if ($auth->hasPermission('system/config/resources')) { $tabs->add('resource', array( - 'title' => $this->translate('Resources'), + 'title' => $this->translate('Configure which resources are being utilized by Icinga Web 2'), + 'label' => $this->translate('Resources'), 'url' => 'config/resource' )); $allowedActions[] = 'resource'; } if ($auth->hasPermission('system/config/roles')) { $tabs->add('roles', array( - 'title' => $this->translate('Roles'), + 'title' => $this->translate( + 'Configure roles to permit or restrict users and groups accessing Icinga Web 2' + ), + 'label' => $this->translate('Roles'), 'url' => 'roles' )); $allowedActions[] = 'roles'; diff --git a/application/controllers/ListController.php b/application/controllers/ListController.php index 4736720ce..d55381987 100644 --- a/application/controllers/ListController.php +++ b/application/controllers/ListController.php @@ -23,7 +23,7 @@ class ListController extends Controller protected function addTitleTab($action) { $this->getTabs()->add($action, array( - 'title' => ucfirst($action), + 'label' => ucfirst($action), 'url' => Url::fromPath( 'list/' . str_replace(' ', '', $action) diff --git a/application/controllers/PreferenceController.php b/application/controllers/PreferenceController.php index e1fd00f46..ac67f495b 100644 --- a/application/controllers/PreferenceController.php +++ b/application/controllers/PreferenceController.php @@ -25,8 +25,9 @@ class PreferenceController extends BasePreferenceController return array( 'preferences' => new Tab( array( - 'title' => t('Preferences'), - 'url' => Url::fromPath('/preference') + 'title' => t('Adjust the preferences of Icinga Web 2 according to your needs'), + 'label' => t('Preferences'), + 'url' => Url::fromPath('/preference') ) ) ); diff --git a/application/controllers/RolesController.php b/application/controllers/RolesController.php index 097c79e29..6cbb4f703 100644 --- a/application/controllers/RolesController.php +++ b/application/controllers/RolesController.php @@ -25,24 +25,30 @@ class RolesController extends ActionController $auth = $this->Auth(); if ($auth->hasPermission('system/config/application')) { $tabs->add('application', array( - 'title' => $this->translate('Application'), + 'title' => $this->translate('Adjust the general configuration of Icinga Web 2'), + 'label' => $this->translate('Application'), 'url' => 'config' )); } if ($auth->hasPermission('system/config/authentication')) { $tabs->add('authentication', array( - 'title' => $this->translate('Authentication'), + 'title' => $this->translate('Configure how users authenticate with and log into Icinga Web 2'), + 'label' => $this->translate('Authentication'), 'url' => 'config/authentication' )); } if ($auth->hasPermission('system/config/resources')) { $tabs->add('resource', array( - 'title' => $this->translate('Resources'), + 'title' => $this->translate('Configure which resources are being utilized by Icinga Web 2'), + 'label' => $this->translate('Resources'), 'url' => 'config/resource' )); } $tabs->add('roles', array( - 'title' => $this->translate('Roles'), + 'title' => $this->translate( + 'Configure roles to permit or restrict users and groups accessing Icinga Web 2' + ), + 'label' => $this->translate('Roles'), 'url' => 'roles' )); $this->getTabs()->setTitle($this->translate('Role Configuration')); diff --git a/application/views/scripts/config/application.phtml b/application/views/scripts/config/application.phtml index edb15d31a..4255befc4 100644 --- a/application/views/scripts/config/application.phtml +++ b/application/views/scripts/config/application.phtml @@ -3,7 +3,7 @@

- translate('Generic Configuration'); ?> + translate('General Configuration'); ?>

messageBox)): ?> messageBox->render() ?> diff --git a/application/views/scripts/config/module.phtml b/application/views/scripts/config/module.phtml index c41fdbf3a..28fbeb39e 100644 --- a/application/views/scripts/config/module.phtml +++ b/application/views/scripts/config/module.phtml @@ -22,14 +22,20 @@ translate('State') ?> - qlink($this->translate('disable'), 'config/moduledisable', array( - 'name' => $module->getName() - )) ?> + qlink( + $this->translate('disable'), + 'config/moduledisable', + array('name' => $module->getName()), + array('title' => sprintf($this->translate('Disable the %s module'), $module->getName())) + ); ?> - qlink($this->translate('enable'), 'config/moduleenable', array( - 'name' => $module->getName() - )) ?> + qlink( + $this->translate('enable'), + 'config/moduleenable', + array('name' => $module->getName()), + array('title' => sprintf($this->translate('Enable the %s module'), $module->getName())) + ); ?> diff --git a/application/views/scripts/config/modules.phtml b/application/views/scripts/config/modules.phtml index 25b57533d..feb7facad 100644 --- a/application/views/scripts/config/modules.phtml +++ b/application/views/scripts/config/modules.phtml @@ -11,22 +11,18 @@ enabled && $module->loaded) { - $icon = $this->icon('thumbs-up'); - $title = sprintf($this->translate('Module %s is enabled'), $module->name); + echo $this->icon('thumbs-up', sprintf($this->translate('Module %s is enabled'), $module->name)); } elseif (! $module->enabled) { - $icon = $this->icon('thumbs-down'); - $title = sprintf($this->translate('Module %s is disabled'), $module->name); + echo $this->icon('thumbs-down', sprintf($this->translate('Module %s is disabled'), $module->name)); } else { // ! $module->loaded - $icon = $this->icon('thumbs-down'); - $title = sprintf($this->translate('Module %s has failed to load'), $module->name); + echo $this->icon('thumbs-down', sprintf($this->translate('Module %s has failed to load'), $module->name)); } echo $this->qlink( - $icon . $this->escape($module->name), + $module->name, 'config/module/', array('name' => $module->name), - array('title' => $title), - false + array('title' => sprintf($this->translate('Show the overview of the %s module'), $module->name)) ); ?> diff --git a/application/views/scripts/config/resource.phtml b/application/views/scripts/config/resource.phtml index c4eb9360f..342320ab9 100644 --- a/application/views/scripts/config/resource.phtml +++ b/application/views/scripts/config/resource.phtml @@ -26,21 +26,25 @@ qlink( - $this->icon('edit') . ' ' . $this->escape($name), + $name, 'config/editresource', array('resource' => $name), - array('title' => sprintf($this->translate('Edit resource %s'), $name)), - false + array( + 'icon' => 'edit', + 'title' => sprintf($this->translate('Edit resource %s'), $name) + ) ); ?>
qlink( - $this->icon('cancel'), + '', 'config/removeresource', array('resource' => $name), - array('title' => sprintf($this->translate('Remove resource %s'), $name)), - false + array( + 'icon' => 'cancel', + 'title' => sprintf($this->translate('Remove resource %s'), $name) + ) ); ?>
diff --git a/application/views/scripts/dashboard/settings.phtml b/application/views/scripts/dashboard/settings.phtml index 2ac9a1dfa..d1ed998d1 100644 --- a/application/views/scripts/dashboard/settings.phtml +++ b/application/views/scripts/dashboard/settings.phtml @@ -24,11 +24,13 @@ qlink( - $this->icon('cancel'), + '', 'dashboard/remove-pane', array('pane' => $pane->getName()), - array('title' => sprintf($this->translate('Remove pane %s'), $pane->getName())), - false + array( + 'icon' => 'cancel', + 'title' => sprintf($this->translate('Remove pane %s'), $pane->getName()) + ) ); ?> @@ -44,20 +46,30 @@ getDisabled() === true) continue; ?> - - getTitle(); ?> - + qlink( + $dashlet->getTitle(), + 'dashboard/update-dashlet', + array('pane' => $pane->getName(), 'dashlet' => $dashlet->getTitle()), + array('title' => sprintf($this->translate('Edit dashlet %s'), $dashlet->getTitle())) + ); ?> - getUrl(); ?> + qlink( + $dashlet->getUrl(), + $dashlet->getUrl(), + null, + array('title' => sprintf($this->translate('Show dashlet %s'), $dashlet->getTitle())) + ); ?> qlink( - $this->icon('cancel'), + '', 'dashboard/remove-dashlet', array('pane' => $pane->getName(), 'dashlet' => $dashlet->getTitle()), - array('title' => sprintf($this->translate('Remove dashlet %s from pane %s'), $dashlet->getTitle(), $pane->getName())), - false + array( + 'icon' => 'cancel', + 'title' => sprintf($this->translate('Remove dashlet %s from pane %s'), $dashlet->getTitle(), $pane->getName()) + ) ); ?> diff --git a/application/views/scripts/form/reorder-authbackend.phtml b/application/views/scripts/form/reorder-authbackend.phtml index 23a6a67f5..02f62572d 100644 --- a/application/views/scripts/form/reorder-authbackend.phtml +++ b/application/views/scripts/form/reorder-authbackend.phtml @@ -11,20 +11,24 @@ qlink( - $this->icon('edit') . ' ' . $this->escape($backendNames[$i]), + $backendNames[$i], 'config/editAuthenticationBackend', array('auth_backend' => $backendNames[$i]), - array('title' => sprintf($this->translate('Edit authentication backend %s'), $backendNames[$i])), - false + array( + 'icon' => 'edit', + 'title' => sprintf($this->translate('Edit authentication backend %s'), $backendNames[$i]) + ) ); ?> qlink( - $this->icon('cancel'), + '', 'config/removeAuthenticationBackend', array('auth_backend' => $backendNames[$i]), - array('title' => sprintf($this->translate('Remove authentication backend %s'), $backendNames[$i])), - false + array( + 'icon' => 'cancel', + 'title' => sprintf($this->translate('Remove authentication backend %s'), $backendNames[$i]) + ) ); ?> diff --git a/application/views/scripts/joystickPagination.phtml b/application/views/scripts/joystickPagination.phtml index a89d2601d..9549fc2b0 100644 --- a/application/views/scripts/joystickPagination.phtml +++ b/application/views/scripts/joystickPagination.phtml @@ -6,7 +6,7 @@ if ($xAxisPaginator->count() <= 1 && $yAxisPaginator->count() <= 1) { return; // Display this pagination only if there are multiple pages } -$fromTo = t('%s: %d to %d of %d (on the %s-axis)'); +$showText = $this->translate('%s: Show %s %u to %u out of %u', 'pagination.joystick'); $xAxisPages = $xAxisPaginator->getPages('all'); $yAxisPages = $yAxisPaginator->getPages('all'); @@ -28,16 +28,25 @@ $nextXAxisPage = $currentXAxisPage < $totalXAxisPages ? $currentXAxisPage + 1 :   - icon('up-open'); ?> + qlink( + '', + Url::fromRequest(), + array( + 'page' => $currentXAxisPage . ',' . $prevYAxisPage + ), + array( + 'icon' => 'up-open', + 'data-base-target' => '_self', + 'title' => sprintf( + $showText, + $this->translate('Y-Axis', 'pagination.joystick'), + $this->translate('hosts', 'pagination.joystick'), + ($prevYAxisPage - 1) * $yAxisPages->itemCountPerPage + 1, + $prevYAxisPage * $yAxisPages->itemCountPerPage, + $yAxisPages->totalItemCount + ) + ) + ); ?> icon('up-open'); ?> @@ -47,16 +56,25 @@ $nextXAxisPage = $currentXAxisPage < $totalXAxisPages ? $currentXAxisPage + 1 : - icon('left-open'); ?> + qlink( + '', + Url::fromRequest(), + array( + 'page' => $prevXAxisPage . ',' . $currentYAxisPage + ), + array( + 'icon' => 'left-open', + 'data-base-target' => '_self', + 'title' => sprintf( + $showText, + $this->translate('X-Axis', 'pagination.joystick'), + $this->translate('services', 'pagination.joystick'), + ($prevXAxisPage - 1) * $xAxisPages->itemCountPerPage + 1, + $prevXAxisPage * $xAxisPages->itemCountPerPage, + $xAxisPages->totalItemCount + ) + ) + ); ?> icon('left-open'); ?> @@ -64,16 +82,26 @@ $nextXAxisPage = $currentXAxisPage < $totalXAxisPages ? $currentXAxisPage + 1 :   - icon('right-open'); ?> + qlink( + '', + Url::fromRequest(), + array( + 'page' => $nextXAxisPage . ',' . $currentYAxisPage + ), + array( + 'icon' => 'right-open', + 'data-base-target' => '_self', + 'title' => sprintf( + $showText, + $this->translate('X-Axis', 'pagination.joystick'), + $this->translate('services', 'pagination.joystick'), + $currentXAxisPage * $xAxisPages->itemCountPerPage + 1, + $nextXAxisPage === $xAxisPages->last ? $xAxisPages->totalItemCount : $nextXAxisPage * $xAxisPages->itemCountPerPage, + $xAxisPages->totalItemCount + ) + ), + false + ); ?> icon('right-open'); ?> @@ -83,16 +111,25 @@ $nextXAxisPage = $currentXAxisPage < $totalXAxisPages ? $currentXAxisPage + 1 :   - icon('down-open'); ?> + qlink( + '', + Url::fromRequest(), + array( + 'page' => $currentXAxisPage . ',' . $nextYAxisPage + ), + array( + 'icon' => 'down-open', + 'data-base-target' => '_self', + 'title' => sprintf( + $showText, + $this->translate('Y-Axis', 'pagination.joystick'), + $this->translate('hosts', 'pagination.joystick'), + $currentYAxisPage * $yAxisPages->itemCountPerPage + 1, + $nextYAxisPage === $yAxisPages->last ? $yAxisPages->totalItemCount : $nextYAxisPage * $yAxisPages->itemCountPerPage, + $yAxisPages->totalItemCount + ) + ) + ); ?> icon('down-open'); ?> diff --git a/application/views/scripts/mixedPagination.phtml b/application/views/scripts/mixedPagination.phtml index bac2b202b..4704d8f51 100644 --- a/application/views/scripts/mixedPagination.phtml +++ b/application/views/scripts/mixedPagination.phtml @@ -13,7 +13,7 @@ if ($this->pageCount <= 1) return;
diff --git a/doc/accessibility/link-labels.html b/doc/accessibility/link-labels.html new file mode 100644 index 000000000..439adb85e --- /dev/null +++ b/doc/accessibility/link-labels.html @@ -0,0 +1,15 @@ + + + + + + Accessibility: Link Labels + + + + + localhost + + \ No newline at end of file diff --git a/library/Icinga/Application/Modules/Module.php b/library/Icinga/Application/Modules/Module.php index 602811b56..b59154020 100644 --- a/library/Icinga/Application/Modules/Module.php +++ b/library/Icinga/Application/Modules/Module.php @@ -641,7 +641,7 @@ class Module $tabs->add('info', array( 'url' => 'config/module', 'urlParams' => array('name' => $this->getName()), - 'title' => 'Module: ' . $this->getName() + 'label' => 'Module: ' . $this->getName() )); foreach ($this->configTabs as $name => $config) { $tabs->add($name, $config); diff --git a/library/Icinga/Protocol/Ldap/Connection.php b/library/Icinga/Protocol/Ldap/Connection.php index 6db88365a..1cd979a74 100644 --- a/library/Icinga/Protocol/Ldap/Connection.php +++ b/library/Icinga/Protocol/Ldap/Connection.php @@ -4,6 +4,7 @@ namespace Icinga\Protocol\Ldap; use Exception; +use Icinga\Exception\ProgrammingError; use Icinga\Protocol\Ldap\Exception as LdapException; use Icinga\Application\Platform; use Icinga\Application\Config; @@ -267,13 +268,22 @@ class Connection $this->connect(); $this->bind(); - if ($query->getUsePagedResults() && version_compare(PHP_VERSION, '5.4.0') >= 0) { + if ($this->pageControlAvailable($query)) { return $this->runPagedQuery($query, $fields); } else { return $this->runQuery($query, $fields); } } + /** + * Execute the given LDAP query and return the resulting entries + * + * @param Query $query The query to execute + * @param array $fields The fields that will be fetched from the matches + * + * @return array The matched entries + * @throws LdapException + */ protected function runQuery(Query $query, $fields = array()) { $limit = $query->getLimit(); @@ -322,8 +332,35 @@ class Connection return $entries; } - protected function runPagedQuery(Query $query, $fields = array()) + /** + * Returns whether requesting the page control is available + */ + protected function pageControlAvailable(Query $query) { + return $query->getUsePagedResults() && version_compare(PHP_VERSION, '5.4.0') >= 0; + } + + /** + * Execute the given LDAP query while requesting pagination control to separate + * big responses into smaller chunks + * + * @param Query $query The query to execute + * @param array $fields The fields that will be fetched from the matches + * @param int $page_size The maximum page size, defaults to Connection::PAGE_SIZE + * + * @return array The matched entries + * @throws LdapException + * @throws ProgrammingError When executed without available page controls (check with pageControlAvailable() ) + */ + protected function runPagedQuery(Query $query, $fields = array(), $pageSize = null) + { + if (! $this->pageControlAvailable($query)) { + throw new ProgrammingError('LDAP: Page control not available.'); + } + if (! isset($pageSize)) { + $pageSize = static::PAGE_SIZE; + } + $limit = $query->getLimit(); $offset = $query->hasOffset() ? $query->getOffset() - 1 : 0; $queryString = $query->create(); @@ -337,7 +374,10 @@ class Connection $cookie = ''; $entries = array(); do { - ldap_control_paged_result($this->ds, static::PAGE_SIZE, true, $cookie); + // do not set controlPageResult as a critical extension, since we still want the + // server to return an answer in case the pagination extension is missing. + ldap_control_paged_result($this->ds, $pageSize, false, $cookie); + $results = @ldap_search($this->ds, $base, $queryString, $fields, 0, $limit ? $offset + $limit : 0); if ($results === false) { if (ldap_errno($this->ds) === self::LDAP_NO_SUCH_OBJECT) { @@ -375,18 +415,16 @@ class Connection } } while (($limit === 0 || $limit !== count($entries)) && ($entry = ldap_next_entry($this->ds, $entry))); - try { - ldap_control_paged_result_response($this->ds, $results, $cookie); - } catch (Exception $e) { + if (false === ldap_control_paged_result_response($this->ds, $results, $cookie)) { // If the page size is greater than or equal to the sizeLimit value, the server should ignore the // control as the request can be satisfied in a single page: https://www.ietf.org/rfc/rfc2696.txt // This applies no matter whether paged search requests are permitted or not. You're done once you // got everything you were out for. if (count($entries) !== $limit) { - Logger::warning( - 'Unable to request paged LDAP results. Does the server allow paged search requests? (%s)', - $e->getMessage() - ); + + // The server does not support pagination, but still returned a response by ignoring the + // pagedResultsControl. We output a warning to indicate that the pagination control was ignored. + Logger::warning('Unable to request paged LDAP results. Does the server allow paged search requests?'); } } @@ -404,7 +442,7 @@ class Connection ldap_control_paged_result($this->ds, 0); } - return $entries; // TODO(7693): Sort entries post-processed + return $entries; } protected function cleanupAttributes($attrs) diff --git a/library/Icinga/Web/View/helpers/url.php b/library/Icinga/Web/View/helpers/url.php index 9fa5d4a69..925a225f9 100644 --- a/library/Icinga/Web/View/helpers/url.php +++ b/library/Icinga/Web/View/helpers/url.php @@ -27,16 +27,24 @@ $this->addHelperFunction('url', function ($path = null, $params = null) { return $url; }); -$this->addHelperFunction('qlink', function ($title, $url, $params = null, $properties = array(), $escape = true) use ($view) { - if (array_key_exists('title', $properties) && !array_key_exists('aria-label', $properties)) { - $properties['aria-label'] = $properties['title']; +$this->addHelperFunction('qlink', function ($title, $url, $params = null, $properties = null, $escape = true) use ($view) { + $icon = ''; + if ($properties) { + if (array_key_exists('title', $properties) && !array_key_exists('aria-label', $properties)) { + $properties['aria-label'] = $properties['title']; + } + + if (array_key_exists('icon', $properties)) { + $icon = $view->icon($properties['icon']); + unset($properties['icon']); + } } return sprintf( '%s', $view->url($url, $params), $view->propertiesToString($properties), - $escape ? $view->escape($title) : $title + $icon . ($escape ? $view->escape($title) : $title) ); }); diff --git a/library/Icinga/Web/Widget/Chart/HistoryColorGrid.php b/library/Icinga/Web/Widget/Chart/HistoryColorGrid.php index 4e07d06b3..96b012db5 100644 --- a/library/Icinga/Web/Widget/Chart/HistoryColorGrid.php +++ b/library/Icinga/Web/Widget/Chart/HistoryColorGrid.php @@ -122,17 +122,18 @@ class HistoryColorGrid extends AbstractWidget { { if (array_key_exists($day, $this->data) && $this->data[$day]['value'] > 0) { $entry = $this->data[$day]; - return' '; - } else { - return ''; + } else { + return ''; } } diff --git a/library/Icinga/Web/Widget/Dashboard.php b/library/Icinga/Web/Widget/Dashboard.php index 8354f6fbb..00d39cd71 100644 --- a/library/Icinga/Web/Widget/Dashboard.php +++ b/library/Icinga/Web/Widget/Dashboard.php @@ -211,7 +211,11 @@ class Dashboard extends AbstractWidget $this->tabs->add( $key, array( - 'title' => $pane->getTitle(), + 'title' => sprintf( + t('Show %s', 'dashboard.pane.tooltip'), + $pane->getTitle() + ), + 'label' => $pane->getTitle(), 'url' => clone($url), 'urlParams' => array($this->tabParam => $key) ) diff --git a/library/Icinga/Web/Widget/Dashboard/Dashlet.php b/library/Icinga/Web/Widget/Dashboard/Dashlet.php index 0a3443771..614caf845 100644 --- a/library/Icinga/Web/Widget/Dashboard/Dashlet.php +++ b/library/Icinga/Web/Widget/Dashboard/Dashlet.php @@ -51,7 +51,7 @@ class Dashlet extends UserWidget private $template =<<<'EOD'
-

{TITLE}

+

{TITLE}