Release 10.0.0 #100
|
@ -13,6 +13,7 @@ The **patch** part changes incrementally at each release.
|
|||
### Added
|
||||
* evolinux-base: On debian 10 and later, add noexec on /dev/shm
|
||||
* php: variable to install the mysqlnd module instead of the default mysql module
|
||||
* packweb-apache: Deploy opcache.php to give some insights on PHP's opcache status
|
||||
|
||||
### Changed
|
||||
* elasticsearch: listen on local interface only by default
|
||||
|
|
744
packweb-apache/files/opcache.php
Normal file
744
packweb-apache/files/opcache.php
Normal file
|
@ -0,0 +1,744 @@
|
|||
<?php
|
||||
|
||||
define('THOUSAND_SEPARATOR',true);
|
||||
|
||||
if (!extension_loaded('Zend OPcache')) {
|
||||
echo '<div style="background-color: #F2DEDE; color: #B94A48; padding: 1em;">You do not have the Zend OPcache extension loaded, sample data is being shown instead.</div>';
|
||||
require 'data-sample.php';
|
||||
}
|
||||
|
||||
class OpCacheDataModel
|
||||
{
|
||||
private $_configuration;
|
||||
private $_status;
|
||||
private $_d3Scripts = array();
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->_configuration = opcache_get_configuration();
|
||||
$this->_status = opcache_get_status();
|
||||
}
|
||||
|
||||
public function getPageTitle()
|
||||
{
|
||||
return 'PHP ' . phpversion() . " with OpCache {$this->_configuration['version']['version']}";
|
||||
}
|
||||
|
||||
public function getStatusDataRows()
|
||||
{
|
||||
$rows = array();
|
||||
foreach ($this->_status as $key => $value) {
|
||||
if ($key === 'scripts') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $k => $v) {
|
||||
if ($v === false) {
|
||||
$value = 'false';
|
||||
}
|
||||
if ($v === true) {
|
||||
$value = 'true';
|
||||
}
|
||||
if ($k === 'used_memory' || $k === 'free_memory' || $k === 'wasted_memory') {
|
||||
$v = $this->_size_for_humans(
|
||||
$v
|
||||
);
|
||||
}
|
||||
if ($k === 'current_wasted_percentage' || $k === 'opcache_hit_rate') {
|
||||
$v = number_format(
|
||||
$v,
|
||||
2
|
||||
) . '%';
|
||||
}
|
||||
if ($k === 'blacklist_miss_ratio') {
|
||||
$v = number_format($v, 2) . '%';
|
||||
}
|
||||
if ($k === 'start_time' || $k === 'last_restart_time') {
|
||||
$v = ($v ? date(DATE_RFC822, $v) : 'never');
|
||||
}
|
||||
if (THOUSAND_SEPARATOR === true && is_int($v)) {
|
||||
$v = number_format($v);
|
||||
}
|
||||
|
||||
$rows[] = "<tr><th>$k</th><td>$v</td></tr>\n";
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ($value === false) {
|
||||
$value = 'false';
|
||||
}
|
||||
if ($value === true) {
|
||||
$value = 'true';
|
||||
}
|
||||
$rows[] = "<tr><th>$key</th><td>$value</td></tr>\n";
|
||||
}
|
||||
|
||||
return implode("\n", $rows);
|
||||
}
|
||||
|
||||
public function getConfigDataRows()
|
||||
{
|
||||
$rows = array();
|
||||
foreach ($this->_configuration['directives'] as $key => $value) {
|
||||
if ($value === false) {
|
||||
$value = 'false';
|
||||
}
|
||||
if ($value === true) {
|
||||
$value = 'true';
|
||||
}
|
||||
if ($key == 'opcache.memory_consumption') {
|
||||
$value = $this->_size_for_humans($value);
|
||||
}
|
||||
$rows[] = "<tr><th>$key</th><td>$value</td></tr>\n";
|
||||
}
|
||||
|
||||
return implode("\n", $rows);
|
||||
}
|
||||
|
||||
public function getScriptStatusRows()
|
||||
{
|
||||
foreach ($this->_status['scripts'] as $key => $data) {
|
||||
$dirs[dirname($key)][basename($key)] = $data;
|
||||
$this->_arrayPset($this->_d3Scripts, $key, array(
|
||||
'name' => basename($key),
|
||||
'size' => $data['memory_consumption'],
|
||||
));
|
||||
}
|
||||
|
||||
asort($dirs);
|
||||
|
||||
$basename = '';
|
||||
while (true) {
|
||||
if (count($this->_d3Scripts) !=1) break;
|
||||
$basename .= DIRECTORY_SEPARATOR . key($this->_d3Scripts);
|
||||
$this->_d3Scripts = reset($this->_d3Scripts);
|
||||
}
|
||||
|
||||
$this->_d3Scripts = $this->_processPartition($this->_d3Scripts, $basename);
|
||||
$id = 1;
|
||||
|
||||
$rows = array();
|
||||
foreach ($dirs as $dir => $files) {
|
||||
$count = count($files);
|
||||
$file_plural = $count > 1 ? 's' : null;
|
||||
$m = 0;
|
||||
foreach ($files as $file => $data) {
|
||||
$m += $data["memory_consumption"];
|
||||
}
|
||||
$m = $this->_size_for_humans($m);
|
||||
|
||||
if ($count > 1) {
|
||||
$rows[] = '<tr>';
|
||||
$rows[] = "<th class=\"clickable\" id=\"head-{$id}\" colspan=\"3\" onclick=\"toggleVisible('#head-{$id}', '#row-{$id}')\">{$dir} ({$count} file{$file_plural}, {$m})</th>";
|
||||
$rows[] = '</tr>';
|
||||
}
|
||||
|
||||
foreach ($files as $file => $data) {
|
||||
$rows[] = "<tr id=\"row-{$id}\">";
|
||||
$rows[] = "<td>" . $this->_format_value($data["hits"]) . "</td>";
|
||||
$rows[] = "<td>" . $this->_size_for_humans($data["memory_consumption"]) . "</td>";
|
||||
$rows[] = $count > 1 ? "<td>{$file}</td>" : "<td>{$dir}/{$file}</td>";
|
||||
$rows[] = '</tr>';
|
||||
}
|
||||
|
||||
++$id;
|
||||
}
|
||||
|
||||
return implode("\n", $rows);
|
||||
}
|
||||
|
||||
public function getScriptStatusCount()
|
||||
{
|
||||
return count($this->_status["scripts"]);
|
||||
}
|
||||
|
||||
public function getGraphDataSetJson()
|
||||
{
|
||||
$dataset = array();
|
||||
$dataset['memory'] = array(
|
||||
$this->_status['memory_usage']['used_memory'],
|
||||
$this->_status['memory_usage']['free_memory'],
|
||||
$this->_status['memory_usage']['wasted_memory'],
|
||||
);
|
||||
|
||||
$dataset['keys'] = array(
|
||||
$this->_status['opcache_statistics']['num_cached_keys'],
|
||||
$this->_status['opcache_statistics']['max_cached_keys'] - $this->_status['opcache_statistics']['num_cached_keys'],
|
||||
0
|
||||
);
|
||||
|
||||
$dataset['hits'] = array(
|
||||
$this->_status['opcache_statistics']['misses'],
|
||||
$this->_status['opcache_statistics']['hits'],
|
||||
0,
|
||||
);
|
||||
|
||||
$dataset['restarts'] = array(
|
||||
$this->_status['opcache_statistics']['oom_restarts'],
|
||||
$this->_status['opcache_statistics']['manual_restarts'],
|
||||
$this->_status['opcache_statistics']['hash_restarts'],
|
||||
);
|
||||
|
||||
if (THOUSAND_SEPARATOR === true) {
|
||||
$dataset['TSEP'] = 1;
|
||||
} else {
|
||||
$dataset['TSEP'] = 0;
|
||||
}
|
||||
|
||||
return json_encode($dataset);
|
||||
}
|
||||
|
||||
public function getHumanUsedMemory()
|
||||
{
|
||||
return $this->_size_for_humans($this->getUsedMemory());
|
||||
}
|
||||
|
||||
public function getHumanFreeMemory()
|
||||
{
|
||||
return $this->_size_for_humans($this->getFreeMemory());
|
||||
}
|
||||
|
||||
public function getHumanWastedMemory()
|
||||
{
|
||||
return $this->_size_for_humans($this->getWastedMemory());
|
||||
}
|
||||
|
||||
public function getUsedMemory()
|
||||
{
|
||||
return $this->_status['memory_usage']['used_memory'];
|
||||
}
|
||||
|
||||
public function getFreeMemory()
|
||||
{
|
||||
return $this->_status['memory_usage']['free_memory'];
|
||||
}
|
||||
|
||||
public function getWastedMemory()
|
||||
{
|
||||
return $this->_status['memory_usage']['wasted_memory'];
|
||||
}
|
||||
|
||||
public function getWastedMemoryPercentage()
|
||||
{
|
||||
return number_format($this->_status['memory_usage']['current_wasted_percentage'], 2);
|
||||
}
|
||||
|
||||
public function getD3Scripts()
|
||||
{
|
||||
return $this->_d3Scripts;
|
||||
}
|
||||
|
||||
private function _processPartition($value, $name = null)
|
||||
{
|
||||
if (array_key_exists('size', $value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$array = array('name' => $name,'children' => array());
|
||||
|
||||
foreach ($value as $k => $v) {
|
||||
$array['children'][] = $this->_processPartition($v, $k);
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
private function _format_value($value)
|
||||
{
|
||||
if (THOUSAND_SEPARATOR === true) {
|
||||
return number_format($value);
|
||||
} else {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
private function _size_for_humans($bytes)
|
||||
{
|
||||
if ($bytes > 1048576) {
|
||||
return sprintf('%.2f MB', $bytes / 1048576);
|
||||
} else {
|
||||
if ($bytes > 1024) {
|
||||
return sprintf('%.2f kB', $bytes / 1024);
|
||||
} else {
|
||||
return sprintf('%d bytes', $bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Borrowed from Laravel
|
||||
private function _arrayPset(&$array, $key, $value)
|
||||
{
|
||||
if (is_null($key)) return $array = $value;
|
||||
$keys = explode(DIRECTORY_SEPARATOR, ltrim($key, DIRECTORY_SEPARATOR));
|
||||
while (count($keys) > 1) {
|
||||
$key = array_shift($keys);
|
||||
if ( ! isset($array[$key]) || ! is_array($array[$key])) {
|
||||
$array[$key] = array();
|
||||
}
|
||||
$array =& $array[$key];
|
||||
}
|
||||
$array[array_shift($keys)] = $value;
|
||||
return $array;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$dataModel = new OpCacheDataModel();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body {
|
||||
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#container {
|
||||
width: 1024px;
|
||||
margin: auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
h1 {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
tbody tr:nth-child(even) {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
p.capitalize {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
position: relative;
|
||||
float: left;
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
.tab {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.tab label {
|
||||
background: #eee;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid #ccc;
|
||||
margin-left: -1px;
|
||||
position: relative;
|
||||
left: 1px;
|
||||
}
|
||||
|
||||
.tab [type=radio] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tab th, .tab td {
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: absolute;
|
||||
top: 28px;
|
||||
left: 0;
|
||||
background: white;
|
||||
border: 1px solid #ccc;
|
||||
height: 450px;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.content table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.content th, .tab:nth-child(3) td {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.content td {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
[type=radio]:checked ~ label {
|
||||
background: white;
|
||||
border-bottom: 1px solid white;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
[type=radio]:checked ~ label ~ .content {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#graph {
|
||||
float: right;
|
||||
width: 40%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#graph > form {
|
||||
position: absolute;
|
||||
right: 60px;
|
||||
top: -20px;
|
||||
}
|
||||
|
||||
#graph > svg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
#stats {
|
||||
position: absolute;
|
||||
right: 125px;
|
||||
top: 145px;
|
||||
}
|
||||
|
||||
#stats th, #stats td {
|
||||
padding: 6px 10px;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
#partition {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 10;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: #ddd;
|
||||
display: none;
|
||||
}
|
||||
|
||||
#close-partition {
|
||||
display: none;
|
||||
position: absolute;
|
||||
z-index: 20;
|
||||
right: 15px;
|
||||
top: 15px;
|
||||
background: #f9373d;
|
||||
color: #fff;
|
||||
padding: 12px 15px;
|
||||
}
|
||||
|
||||
#close-partition:hover {
|
||||
background: #D32F33;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#partition rect {
|
||||
stroke: #fff;
|
||||
fill: #aaa;
|
||||
fill-opacity: 1;
|
||||
}
|
||||
|
||||
#partition rect.parent {
|
||||
cursor: pointer;
|
||||
fill: steelblue;
|
||||
}
|
||||
|
||||
#partition text {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
label {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.0.1/d3.v3.min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
|
||||
<script>
|
||||
var hidden = {};
|
||||
function toggleVisible(head, row) {
|
||||
if (!hidden[row]) {
|
||||
d3.selectAll(row).transition().style('display', 'none');
|
||||
hidden[row] = true;
|
||||
d3.select(head).transition().style('color', '#ccc');
|
||||
} else {
|
||||
d3.selectAll(row).transition().style('display');
|
||||
hidden[row] = false;
|
||||
d3.select(head).transition().style('color', '#000');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<title><?php echo $dataModel->getPageTitle(); ?></title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="container">
|
||||
<h1><?php echo $dataModel->getPageTitle(); ?></h1>
|
||||
|
||||
<div class="tabs">
|
||||
|
||||
<div class="tab">
|
||||
<input type="radio" id="tab-status" name="tab-group-1" checked>
|
||||
<label for="tab-status">Status</label>
|
||||
<div class="content">
|
||||
<table>
|
||||
<?php echo $dataModel->getStatusDataRows(); ?>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab">
|
||||
<input type="radio" id="tab-config" name="tab-group-1">
|
||||
<label for="tab-config">Configuration</label>
|
||||
<div class="content">
|
||||
<table>
|
||||
<?php echo $dataModel->getConfigDataRows(); ?>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab">
|
||||
<input type="radio" id="tab-scripts" name="tab-group-1">
|
||||
<label for="tab-scripts">Scripts (<?php echo $dataModel->getScriptStatusCount(); ?>)</label>
|
||||
<div class="content">
|
||||
<table style="font-size:0.8em;">
|
||||
<tr>
|
||||
<th width="10%">Hits</th>
|
||||
<th width="20%">Memory</th>
|
||||
<th width="70%">Path</th>
|
||||
</tr>
|
||||
<?php echo $dataModel->getScriptStatusRows(); ?>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab">
|
||||
<input type="radio" id="tab-visualise" name="tab-group-1">
|
||||
<label for="tab-visualise">Visualise Partition</label>
|
||||
<div class="content"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="graph">
|
||||
<form>
|
||||
<label><input type="radio" name="dataset" value="memory" checked> Memory</label>
|
||||
<label><input type="radio" name="dataset" value="keys"> Keys</label>
|
||||
<label><input type="radio" name="dataset" value="hits"> Hits</label>
|
||||
<label><input type="radio" name="dataset" value="restarts"> Restarts</label>
|
||||
</form>
|
||||
|
||||
<div id="stats"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="close-partition">✖ Close Visualisation</div>
|
||||
<div id="partition"></div>
|
||||
|
||||
<script>
|
||||
var dataset = <?php echo $dataModel->getGraphDataSetJson(); ?>;
|
||||
|
||||
var width = 400,
|
||||
height = 400,
|
||||
radius = Math.min(width, height) / 2,
|
||||
colours = ['#B41F1F', '#1FB437', '#ff7f0e'];
|
||||
|
||||
d3.scale.customColours = function() {
|
||||
return d3.scale.ordinal().range(colours);
|
||||
};
|
||||
|
||||
var colour = d3.scale.customColours();
|
||||
var pie = d3.layout.pie().sort(null);
|
||||
|
||||
var arc = d3.svg.arc().innerRadius(radius - 20).outerRadius(radius - 50);
|
||||
var svg = d3.select("#graph").append("svg")
|
||||
.attr("width", width)
|
||||
.attr("height", height)
|
||||
.append("g")
|
||||
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
|
||||
|
||||
var path = svg.selectAll("path")
|
||||
.data(pie(dataset.memory))
|
||||
.enter().append("path")
|
||||
.attr("fill", function(d, i) { return colour(i); })
|
||||
.attr("d", arc)
|
||||
.each(function(d) { this._current = d; }); // store the initial values
|
||||
|
||||
d3.selectAll("input").on("change", change);
|
||||
set_text("memory");
|
||||
|
||||
function set_text(t) {
|
||||
if (t === "memory") {
|
||||
d3.select("#stats").html(
|
||||
"<table><tr><th style='background:#B41F1F;'>Used</th><td><?php echo $dataModel->getHumanUsedMemory()?></td></tr>"+
|
||||
"<tr><th style='background:#1FB437;'>Free</th><td><?php echo $dataModel->getHumanFreeMemory()?></td></tr>"+
|
||||
"<tr><th style='background:#ff7f0e;' rowspan=\"2\">Wasted</th><td><?php echo $dataModel->getHumanWastedMemory()?></td></tr>"+
|
||||
"<tr><td><?php echo $dataModel->getWastedMemoryPercentage()?>%</td></tr></table>"
|
||||
);
|
||||
} else if (t === "keys") {
|
||||
d3.select("#stats").html(
|
||||
"<table><tr><th style='background:#B41F1F;'>Cached keys</th><td>"+format_value(dataset[t][0])+"</td></tr>"+
|
||||
"<tr><th style='background:#1FB437;'>Free Keys</th><td>"+format_value(dataset[t][1])+"</td></tr></table>"
|
||||
);
|
||||
} else if (t === "hits") {
|
||||
d3.select("#stats").html(
|
||||
"<table><tr><th style='background:#B41F1F;'>Misses</th><td>"+format_value(dataset[t][0])+"</td></tr>"+
|
||||
"<tr><th style='background:#1FB437;'>Cache Hits</th><td>"+format_value(dataset[t][1])+"</td></tr></table>"
|
||||
);
|
||||
} else if (t === "restarts") {
|
||||
d3.select("#stats").html(
|
||||
"<table><tr><th style='background:#B41F1F;'>Memory</th><td>"+dataset[t][0]+"</td></tr>"+
|
||||
"<tr><th style='background:#1FB437;'>Manual</th><td>"+dataset[t][1]+"</td></tr>"+
|
||||
"<tr><th style='background:#ff7f0e;'>Keys</th><td>"+dataset[t][2]+"</td></tr></table>"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function change() {
|
||||
// Filter out any zero values to see if there is anything left
|
||||
var remove_zero_values = dataset[this.value].filter(function(value) {
|
||||
return value > 0;
|
||||
});
|
||||
|
||||
// Skip if the value is undefined for some reason
|
||||
if (typeof dataset[this.value] !== 'undefined' && remove_zero_values.length > 0) {
|
||||
$('#graph').find('> svg').show();
|
||||
path = path.data(pie(dataset[this.value])); // update the data
|
||||
path.transition().duration(750).attrTween("d", arcTween); // redraw the arcs
|
||||
// Hide the graph if we can't draw it correctly, not ideal but this works
|
||||
} else {
|
||||
$('#graph').find('> svg').hide();
|
||||
}
|
||||
|
||||
set_text(this.value);
|
||||
}
|
||||
|
||||
function arcTween(a) {
|
||||
var i = d3.interpolate(this._current, a);
|
||||
this._current = i(0);
|
||||
return function(t) {
|
||||
return arc(i(t));
|
||||
};
|
||||
}
|
||||
|
||||
function size_for_humans(bytes) {
|
||||
if (bytes > 1048576) {
|
||||
return (bytes/1048576).toFixed(2) + ' MB';
|
||||
} else if (bytes > 1024) {
|
||||
return (bytes/1024).toFixed(2) + ' KB';
|
||||
} else return bytes + ' bytes';
|
||||
}
|
||||
|
||||
function format_value(value) {
|
||||
if (dataset["TSEP"] == 1) {
|
||||
return value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
var w = window.innerWidth,
|
||||
h = window.innerHeight,
|
||||
x = d3.scale.linear().range([0, w]),
|
||||
y = d3.scale.linear().range([0, h]);
|
||||
|
||||
var vis = d3.select("#partition")
|
||||
.style("width", w + "px")
|
||||
.style("height", h + "px")
|
||||
.append("svg:svg")
|
||||
.attr("width", w)
|
||||
.attr("height", h);
|
||||
|
||||
var partition = d3.layout.partition()
|
||||
.value(function(d) { return d.size; });
|
||||
|
||||
root = JSON.parse('<?php echo json_encode($dataModel->getD3Scripts()); ?>');
|
||||
|
||||
var g = vis.selectAll("g")
|
||||
.data(partition.nodes(root))
|
||||
.enter().append("svg:g")
|
||||
.attr("transform", function(d) { return "translate(" + x(d.y) + "," + y(d.x) + ")"; })
|
||||
.on("click", click);
|
||||
|
||||
var kx = w / root.dx,
|
||||
ky = h / 1;
|
||||
|
||||
g.append("svg:rect")
|
||||
.attr("width", root.dy * kx)
|
||||
.attr("height", function(d) { return d.dx * ky; })
|
||||
.attr("class", function(d) { return d.children ? "parent" : "child"; });
|
||||
|
||||
g.append("svg:text")
|
||||
.attr("transform", transform)
|
||||
.attr("dy", ".35em")
|
||||
.style("opacity", function(d) { return d.dx * ky > 12 ? 1 : 0; })
|
||||
.text(function(d) { return d.name; })
|
||||
|
||||
d3.select(window)
|
||||
.on("click", function() { click(root); })
|
||||
|
||||
function click(d) {
|
||||
if (!d.children) return;
|
||||
|
||||
kx = (d.y ? w - 40 : w) / (1 - d.y);
|
||||
ky = h / d.dx;
|
||||
x.domain([d.y, 1]).range([d.y ? 40 : 0, w]);
|
||||
y.domain([d.x, d.x + d.dx]);
|
||||
|
||||
var t = g.transition()
|
||||
.duration(d3.event.altKey ? 7500 : 750)
|
||||
.attr("transform", function(d) { return "translate(" + x(d.y) + "," + y(d.x) + ")"; });
|
||||
|
||||
t.select("rect")
|
||||
.attr("width", d.dy * kx)
|
||||
.attr("height", function(d) { return d.dx * ky; });
|
||||
|
||||
t.select("text")
|
||||
.attr("transform", transform)
|
||||
.style("opacity", function(d) { return d.dx * ky > 12 ? 1 : 0; });
|
||||
|
||||
d3.event.stopPropagation();
|
||||
}
|
||||
|
||||
function transform(d) {
|
||||
return "translate(8," + d.dx * ky / 2 + ")";
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
function handleVisualisationToggle(close) {
|
||||
|
||||
$('#partition, #close-partition').fadeToggle();
|
||||
|
||||
// Is the visualisation being closed? If so show the status tab again
|
||||
if (close) {
|
||||
|
||||
$('#tab-visualise').removeAttr('checked');
|
||||
$('#tab-status').trigger('click');
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$('label[for="tab-visualise"], #close-partition').on('click', function() {
|
||||
|
||||
handleVisualisationToggle(($(this).attr('id') === 'close-partition'));
|
||||
|
||||
});
|
||||
|
||||
$(document).keyup(function(e) {
|
||||
|
||||
if (e.keyCode == 27) handleVisualisationToggle(true);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -17,6 +17,18 @@
|
|||
line: ' <li><a href="/info.php">Infos PHP</a></li>'
|
||||
regexp: "Infos PHP"
|
||||
|
||||
- name: install opcache.php
|
||||
copy:
|
||||
src: opcache.php
|
||||
dest: /var/www/opcache.php
|
||||
mode: "0644"
|
||||
|
||||
- name: enable opcache.php link in default site index
|
||||
lineinfile:
|
||||
dest: /var/www/index.html
|
||||
line: ' <li><a href="/opcache.php">Infos OpCache PHP</a></li>'
|
||||
regexp: "Infos OpCache PHP"
|
||||
|
||||
- name: Add elements to user account template
|
||||
file:
|
||||
path: "/etc/skel/{{ item.path }}"
|
||||
|
|
Loading…
Reference in a new issue