Merge remote-tracking branch 'origin/master' into feature/organize-headings-7976

This commit is contained in:
Marius Hein 2015-02-23 14:28:48 +01:00
commit 2dda3be87b
14 changed files with 256 additions and 25 deletions

View File

@ -38,6 +38,20 @@ abstract class Chart implements Drawable
*/ */
protected $palette; protected $palette;
/**
* The title of this chart, used for providing accessibility features
*
* @var string
*/
public $title;
/**
* The description for this chart, mandatory for providing accessibility features
*
* @var string
*/
public $description;
/** /**
* Create a new chart object and create internal objects * Create a new chart object and create internal objects
* *
@ -86,7 +100,6 @@ abstract class Chart implements Drawable
} }
/** /**
*
* Render this graph and return the created SVG * Render this graph and return the created SVG
* *
* @return string The SVG created by the SvgRenderer * @return string The SVG created by the SvgRenderer
@ -105,6 +118,11 @@ abstract class Chart implements Drawable
$this->renderer->setXAspectRatioAlignment(SVGRenderer::X_ASPECT_RATIO_MIN); $this->renderer->setXAspectRatioAlignment(SVGRenderer::X_ASPECT_RATIO_MIN);
$this->renderer->setYAspectRatioAlignment(SVGRenderer::Y_ASPECT_RATIO_MIN); $this->renderer->setYAspectRatioAlignment(SVGRenderer::Y_ASPECT_RATIO_MIN);
} }
$this->renderer->setAriaDescription($this->description);
$this->renderer->setAriaTitle($this->title);
$this->renderer->getCanvas()->setAriaRole('presentation');
$this->renderer->getCanvas()->addElement($this); $this->renderer->getCanvas()->addElement($this);
return $this->renderer->render(); return $this->renderer->render();
} }

View File

@ -65,7 +65,13 @@ class BarGraph extends Styleable implements Drawable
) { ) {
$this->order = $order; $this->order = $order;
$this->dataSet = $dataSet; $this->dataSet = $dataSet;
$this->tooltips = $tooltips; $this->tooltips = $tooltips;
foreach ($this->tooltips as $value) {
$ts[] = $value;
}
$this->tooltips = $ts;
$this->graphs = $graphs; $this->graphs = $graphs;
} }

View File

@ -38,6 +38,13 @@ class LineGraph extends Styleable implements Drawable
*/ */
private $isDiscrete = false; private $isDiscrete = false;
/**
* The tooltips
*
* @var
*/
private $tooltips;
/** /**
* The default stroke width * The default stroke width
* @var int * @var int
@ -56,10 +63,22 @@ class LineGraph extends Styleable implements Drawable
* *
* @param array $dataset An array of [x, y] arrays to display * @param array $dataset An array of [x, y] arrays to display
*/ */
public function __construct(array $dataset) public function __construct(
{ array $dataset,
array &$graphs,
$order,
array $tooltips = null
) {
usort($dataset, array($this, 'sortByX')); usort($dataset, array($this, 'sortByX'));
$this->dataset = $dataset; $this->dataset = $dataset;
$this->graphs = $graphs;
$this->tooltips = $tooltips;
foreach ($this->tooltips as $value) {
$ts[] = $value;
}
$this->tooltips = $ts;
$this->order = $order;
} }
/** /**
@ -142,14 +161,41 @@ class LineGraph extends Styleable implements Drawable
$path->setAdditionalStyle('clip-path: url(#clip);'); $path->setAdditionalStyle('clip-path: url(#clip);');
$path->setId($this->id); $path->setId($this->id);
$group = $path->toSvg($ctx); $group = $path->toSvg($ctx);
foreach ($this->dataset as $x => $point) {
if ($this->showDataPoints === true) { if ($this->showDataPoints === true) {
foreach ($this->dataset as $point) {
$dot = new Circle($point[0], $point[1], $this->dotWith); $dot = new Circle($point[0], $point[1], $this->dotWith);
$dot->setFill($this->strokeColor); $dot->setFill($this->strokeColor);
$group->appendChild($dot->toSvg($ctx)); $group->appendChild($dot->toSvg($ctx));
} }
// Draw invisible circle for tooltip hovering
$invisible = new Circle($point[0], $point[1], 20);
$invisible->setFill($this->strokeColor);
$invisible->setAdditionalStyle('opacity: 0.0;');
$invisible->setAttribute('class', 'chart-data');
if (isset($this->tooltips[$x])) {
$data = array(
'label' => isset($this->graphs[$this->order]['label']) ?
strtolower($this->graphs[$this->order]['label']) : '',
'color' => isset($this->graphs[$this->order]['color']) ?
strtolower($this->graphs[$this->order]['color']) : '#fff'
);
$format = isset($this->graphs[$this->order]['tooltip'])
? $this->graphs[$this->order]['tooltip'] : null;
$invisible->setAttribute(
'title',
$this->tooltips[$x]->renderNoHtml($this->order, $data, $format)
);
$invisible->setAttribute(
'data-title-rich',
$this->tooltips[$x]->render($this->order, $data, $format)
);
} }
$group->appendChild($invisible->toSvg($ctx));
}
return $group; return $group;
} }
} }

View File

@ -66,7 +66,7 @@ class Tooltip
*/ */
public function __construct ( public function __construct (
$data = array(), $data = array(),
$format = '<b>{title}</b></b><br/> {value} of {sum} {label}' $format = '<b>{title}</b>: {value} {label}'
) { ) {
$this->data = array_merge($this->data, $data); $this->data = array_merge($this->data, $data);
$this->defaultFormat = $format; $this->defaultFormat = $format;

View File

@ -84,6 +84,13 @@ class GridChart extends Chart
*/ */
private $tooltips = array(); private $tooltips = array();
public function __construct()
{
$this->title = t('Grid Chart');
$this->description = t('Contains data in a bar or line chart.');
parent::__construct();
}
/** /**
* Check if the current dataset has the proper structure for this chart. * Check if the current dataset has the proper structure for this chart.
* *
@ -395,7 +402,12 @@ class GridChart extends Chart
); );
break; break;
case self::TYPE_LINE: case self::TYPE_LINE:
$graphObj = new LineGraph($axis->transform($graph['data'])); $graphObj = new LineGraph(
$axis->transform($graph['data']),
$graphs,
$dataset,
$this->tooltips
);
break; break;
default: default:
continue; continue;

View File

@ -50,6 +50,13 @@ class PieChart extends Chart
*/ */
private $noCaption = false; private $noCaption = false;
public function __construct()
{
$this->title = t('Pie Chart');
$this->description = t('Contains data in a pie chart.');
parent::__construct();
}
/** /**
* Test if the given pies have the correct format * Test if the given pies have the correct format
* *

View File

@ -43,6 +43,13 @@ class Canvas implements Drawable
*/ */
private $rect; private $rect;
/**
* The aria role used to describe this canvas' purpose in the accessibility tree
*
* @var string
*/
private $ariaRole;
/** /**
* Create this canvas * Create this canvas
* *
@ -111,6 +118,23 @@ class Canvas implements Drawable
$innerContainer->appendChild($child->toSvg($ctx)); $innerContainer->appendChild($child->toSvg($ctx));
} }
if (isset($this->ariaRole)) {
$outer->setAttribute('role', $this->ariaRole);
}
return $outer; return $outer;
} }
/**
* Set the aria role used to determine the meaning of this canvas in the accessibility tree
*
* The role 'presentation' will indicate that the purpose of this canvas is entirely decorative, while the role
* 'img' will indicate that the canvas contains an image, with a possible title or a description. For other
* possible roles, see http://www.w3.org/TR/wai-aria/roles
*
* @param $role string The aria role to set
*/
public function setAriaRole($role)
{
$this->ariaRole = $role;
}
} }

View File

@ -48,6 +48,27 @@ class SVGRenderer
*/ */
private $svg; private $svg;
/**
* The description of this SVG, useful for screen readers
*
* @var string
*/
private $ariaDescription;
/**
* The title of this SVG, useful for screen readers
*
* @var string
*/
private $ariaTitle;
/**
* The aria role used by this svg element
*
* @var string
*/
private $ariaRole = 'img';
/** /**
* The root layer for all elements * The root layer for all elements
* *
@ -126,6 +147,7 @@ class SVGRenderer
$svg = $this->document->createElement('svg'); $svg = $this->document->createElement('svg');
$svg->setAttribute('xmlns', 'http://www.w3.org/2000/svg'); $svg->setAttribute('xmlns', 'http://www.w3.org/2000/svg');
$svg->setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink'); $svg->setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
$svg->setAttribute('role', $this->ariaRole);
$svg->setAttribute('width', '100%'); $svg->setAttribute('width', '100%');
$svg->setAttribute('height', '100%'); $svg->setAttribute('height', '100%');
$svg->setAttribute( $svg->setAttribute(
@ -150,6 +172,42 @@ class SVGRenderer
return $svg; return $svg;
} }
/**
* Add aria title and description
*
* Adds an aria title and desc element to the given SVG node, which are used to describe this SVG by accessibility
* tools such as screen readers.
*
* @param DOMNode $svg The SVG DOMNode to which the aria attributes should be attached
* @param $title The title text
* @param $description The description text
*/
private function addAriaDescription (DOMNode $svg, $titleText, $descriptionText)
{
$doc = $svg->ownerDocument;
$titleId = $descId = '';
if (isset ($this->ariaTitle)) {
$titleId = 'aria-title-' . $this->stripNonAlphanumeric($titleText);
$title = $doc->createElement('title');
$title->setAttribute('id', $titleId);
$title->appendChild($doc->createTextNode($titleText));
$svg->appendChild($title);
}
if (isset ($this->ariaDescription)) {
$descId = 'aria-desc-' . $this->stripNonAlphanumeric($descriptionText);
$desc = $doc->createElement('desc');
$desc->setAttribute('id', $descId);
$desc->appendChild($doc->createTextNode($descriptionText));
$svg->appendChild($desc);
}
$svg->setAttribute('aria-labelledby', join(' ', array($titleId, $descId)));
}
/** /**
* Initialises the XML-document, SVG-element and this figure's root canvas * Initialises the XML-document, SVG-element and this figure's root canvas
* *
@ -172,6 +230,7 @@ class SVGRenderer
{ {
$this->createRootDocument(); $this->createRootDocument();
$ctx = $this->createRenderContext(); $ctx = $this->createRenderContext();
$this->addAriaDescription($this->svg, $this->ariaTitle, $this->ariaDescription);
$this->svg->appendChild($this->rootCanvas->toSvg($ctx)); $this->svg->appendChild($this->rootCanvas->toSvg($ctx));
$this->document->formatOutput = true; $this->document->formatOutput = true;
return $this->document->saveXML(); return $this->document->saveXML();
@ -232,4 +291,40 @@ class SVGRenderer
{ {
$this->yAspectRatio = $alignment; $this->yAspectRatio = $alignment;
} }
/**
* Set the aria description, that is used as a title for this SVG in screen readers
*
* @param $text
*/
public function setAriaTitle($text)
{
$this->ariaTitle = $text;
}
/**
* Set the aria description, that is used to describe this SVG in screen readers
*
* @param $text
*/
public function setAriaDescription($text)
{
$this->ariaDescription = $text;
}
/**
* Set the aria role, that is used to describe the purpose of this SVG in screen readers
*
* @param $text
*/
public function setAriaRole($text)
{
$this->ariaRole = $text;
}
private function stripNonAlphanumeric($str)
{
return preg_replace('/[^A-Za-z]+/', '', $str);
}
} }

View File

@ -44,7 +44,6 @@ EOD;
</noscript> </noscript>
EOD; EOD;
/** /**
* @var Url * @var Url
*/ */

View File

@ -343,6 +343,8 @@ class Monitoring_AlertsummaryController extends Controller
public function createHealingChart() public function createHealingChart()
{ {
$gridChart = new GridChart(); $gridChart = new GridChart();
$gridChart->title = t('Healing Chart');
$gridChart->description = t('Notifications and average reaction time per hour.');
$gridChart->alignTopLeft(); $gridChart->alignTopLeft();
$gridChart->setAxisLabel($this->createPeriodDescription(), mt('monitoring', 'Notifications')) $gridChart->setAxisLabel($this->createPeriodDescription(), mt('monitoring', 'Notifications'))
@ -446,7 +448,8 @@ class Monitoring_AlertsummaryController extends Controller
'label' => $this->translate('Notifications'), 'label' => $this->translate('Notifications'),
'color' => '#07C0D9', 'color' => '#07C0D9',
'data' => $notifications, 'data' => $notifications,
'showPoints' => true 'showPoints' => true,
'tooltip' => '<b>{title}:</b> {value} {label}'
) )
); );
@ -455,7 +458,8 @@ class Monitoring_AlertsummaryController extends Controller
'label' => $this->translate('Avg (min)'), 'label' => $this->translate('Avg (min)'),
'color' => '#ffaa44', 'color' => '#ffaa44',
'data' => $dAvg, 'data' => $dAvg,
'showPoints' => true 'showPoints' => true,
'tooltip' => t('<b>{title}:</b> {value}m min. reaction time')
) )
); );
@ -464,7 +468,8 @@ class Monitoring_AlertsummaryController extends Controller
'label' => $this->translate('Max (min)'), 'label' => $this->translate('Max (min)'),
'color' => '#ff5566', 'color' => '#ff5566',
'data' => $dMax, 'data' => $dMax,
'showPoints' => true 'showPoints' => true,
'tooltip' => t('<b>{title}:</b> {value}m max. reaction time')
) )
); );
@ -479,6 +484,8 @@ class Monitoring_AlertsummaryController extends Controller
public function createDefectImage() public function createDefectImage()
{ {
$gridChart = new GridChart(); $gridChart = new GridChart();
$gridChart->title = t('Defect Chart');
$gridChart->description = t('Notifications and defects per hour');
$gridChart->alignTopLeft(); $gridChart->alignTopLeft();
$gridChart->setAxisLabel($this->createPeriodDescription(), mt('monitoring', 'Notifications')) $gridChart->setAxisLabel($this->createPeriodDescription(), mt('monitoring', 'Notifications'))
@ -491,7 +498,8 @@ class Monitoring_AlertsummaryController extends Controller
'label' => $this->translate('Notifications'), 'label' => $this->translate('Notifications'),
'color' => '#07C0D9', 'color' => '#07C0D9',
'data' => $this->notificationData, 'data' => $this->notificationData,
'showPoints' => true 'showPoints' => true,
'tooltip' => '<b>{title}:</b> {value} {label}'
) )
); );
@ -500,7 +508,8 @@ class Monitoring_AlertsummaryController extends Controller
'label' => $this->translate('Defects'), 'label' => $this->translate('Defects'),
'color' => '#ff5566', 'color' => '#ff5566',
'data' => $this->problemData, 'data' => $this->problemData,
'showPoints' => true 'showPoints' => true,
'tooltip' => '<b>{title}:</b> {value} {label}'
) )
); );

View File

@ -236,6 +236,9 @@ class Monitoring_ChartController extends Controller
$unknownBars[] = array($servicegroup->servicegroup, $servicegroup->services_unknown_unhandled); $unknownBars[] = array($servicegroup->servicegroup, $servicegroup->services_unknown_unhandled);
} }
$this->view->chart = new GridChart(); $this->view->chart = new GridChart();
$this->view->chart->title = t('Service Group Chart');
$this->view->chart->description = t('Contains service states for each service group.');
$this->view->chart->alignTopLeft(); $this->view->chart->alignTopLeft();
$this->view->chart->setAxisLabel('', mt('monitoring', 'Services')) $this->view->chart->setAxisLabel('', mt('monitoring', 'Services'))
->setXAxis(new StaticAxis()) ->setXAxis(new StaticAxis())
@ -292,6 +295,9 @@ class Monitoring_ChartController extends Controller
} }
$tooltip = mt('monitoring', '<b>{title}:</b><br> {value} of {sum} hosts are {label}'); $tooltip = mt('monitoring', '<b>{title}:</b><br> {value} of {sum} hosts are {label}');
$this->view->chart = new GridChart(); $this->view->chart = new GridChart();
$this->view->chart->title = t('Host Group Chart');
$this->view->chart->description = t('Contains host states of each service group.');
$this->view->chart->alignTopLeft(); $this->view->chart->alignTopLeft();
$this->view->chart->setAxisLabel('', mt('monitoring', 'Hosts')) $this->view->chart->setAxisLabel('', mt('monitoring', 'Hosts'))
->setXAxis(new StaticAxis()) ->setXAxis(new StaticAxis())

View File

@ -43,8 +43,11 @@ class BackendConfigForm extends ConfigForm
{ {
$resources = array(); $resources = array();
foreach ($resourceConfig as $name => $resource) { foreach ($resourceConfig as $name => $resource) {
if ($resource->type === 'db' || $resource->type === 'livestatus') { // if ($resource->type === 'db' || $resource->type === 'livestatus') {
$resources[$resource->type === 'db' ? 'ido' : 'livestatus'][$name] = $name; // $resources[$resource->type === 'db' ? 'ido' : 'livestatus'][$name] = $name;
// }
if ($resource->type === 'db') {
$resources['ido'][$name] = $name;
} }
} }
@ -183,13 +186,19 @@ class BackendConfigForm extends ConfigForm
{ {
$resourceType = isset($formData['type']) ? $formData['type'] : key($this->resources); $resourceType = isset($formData['type']) ? $formData['type'] : key($this->resources);
if ($resourceType === 'livestatus') {
throw new ConfigurationError(
'We\'ve disabled livestatus support for now because it\'s not feature complete yet'
);
}
$resourceTypes = array(); $resourceTypes = array();
if ($resourceType === 'ido' || array_key_exists('ido', $this->resources)) { if ($resourceType === 'ido' || array_key_exists('ido', $this->resources)) {
$resourceTypes['ido'] = 'IDO Backend'; $resourceTypes['ido'] = 'IDO Backend';
} }
if ($resourceType === 'livestatus' || array_key_exists('livestatus', $this->resources)) { // if ($resourceType === 'livestatus' || array_key_exists('livestatus', $this->resources)) {
$resourceTypes['livestatus'] = 'Livestatus'; // $resourceTypes['livestatus'] = 'Livestatus';
} // }
$this->addElement( $this->addElement(
'checkbox', 'checkbox',

View File

@ -51,7 +51,7 @@ class BackendPage extends Form
if (Platform::hasMysqlSupport() || Platform::hasPostgresqlSupport()) { if (Platform::hasMysqlSupport() || Platform::hasPostgresqlSupport()) {
$resourceTypes['ido'] = 'IDO'; $resourceTypes['ido'] = 'IDO';
} }
$resourceTypes['livestatus'] = 'Livestatus'; // $resourceTypes['livestatus'] = 'Livestatus';
$this->addElement( $this->addElement(
'select', 'select',

View File

@ -27,7 +27,7 @@
var $el = $(this); var $el = $(this);
$el.attr('title', $el.data('title-rich') || $el.attr('title')); $el.attr('title', $el.data('title-rich') || $el.attr('title'));
}); });
$('svg rect.chart-data[title]', el).tipsy({ gravity: 'se', html: true }); $('svg .chart-data', el).tipsy({ gravity: 'se', html: true });
$('.historycolorgrid a[title]', el).tipsy({ gravity: 's', offset: 2 }); $('.historycolorgrid a[title]', el).tipsy({ gravity: 's', offset: 2 });
$('img.icon[title]', el).tipsy({ gravity: $.fn.tipsy.autoNS, offset: 2 }); $('img.icon[title]', el).tipsy({ gravity: $.fn.tipsy.autoNS, offset: 2 });
$('[title]', el).tipsy({ gravity: $.fn.tipsy.autoNS, delayIn: 500 }); $('[title]', el).tipsy({ gravity: $.fn.tipsy.autoNS, delayIn: 500 });