Merge branch '1.0.x'

Conflicts:
	Form/Extension/DescriptionFormTypeExtension.php
	Parser/FormTypeParser.php
	Tests/Fixtures/Form/TestType.php
This commit is contained in:
William DURAND 2012-06-27 09:46:29 +02:00
commit 519b356374
9 changed files with 351 additions and 85 deletions

View File

@ -23,7 +23,8 @@ class Configuration implements ConfigurationInterface
->root('nelmio_api_doc') ->root('nelmio_api_doc')
->children() ->children()
->scalarNode('name')->defaultValue('API documentation')->end() ->scalarNode('name')->defaultValue('API documentation')->end()
; ->scalarNode('sandbox_target')->defaultValue('/app_dev.php')->end()
->end();
return $treeBuilder; return $treeBuilder;
} }

View File

@ -29,6 +29,7 @@ class NelmioApiDocExtension extends Extension
$config = $processor->processConfiguration($configuration, $configs); $config = $processor->processConfiguration($configuration, $configs);
$container->setParameter('nelmio_api_doc.api_name', $config['name']); $container->setParameter('nelmio_api_doc.api_name', $config['name']);
$container->setParameter('nelmio_api_doc.api_sandbox_target', $config['sandbox_target']);
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('formatters.xml'); $loader->load('formatters.xml');

View File

@ -200,7 +200,7 @@ class ApiDocExtractor
} }
} }
$route->addOptions(array('_paramDocs' => $paramDocs)); $route->setOptions(array_merge($route->getOptions(), array('_paramDocs' => $paramDocs)));
return array('annotation' => $annotation, 'route' => $route); return array('annotation' => $annotation, 'route' => $route);
} }

View File

@ -20,6 +20,11 @@ class HtmlFormatter extends AbstractFormatter
*/ */
private $apiName; private $apiName;
/**
* @var string
*/
private $sandboxTarget;
/** /**
* @var \Symfony\Component\Templating\EngineInterface * @var \Symfony\Component\Templating\EngineInterface
*/ */
@ -33,6 +38,14 @@ class HtmlFormatter extends AbstractFormatter
$this->apiName = $apiName; $this->apiName = $apiName;
} }
/**
* @param string $sandboxTarget
*/
public function setSandboxTarget($sandboxTarget)
{
$this->sandboxTarget = $sandboxTarget;
}
/** /**
* @param EngineInterface $engine * @param EngineInterface $engine
*/ */
@ -70,6 +83,7 @@ class HtmlFormatter extends AbstractFormatter
{ {
return array( return array(
'apiName' => $this->apiName, 'apiName' => $this->apiName,
'sandboxTarget' => $this->sandboxTarget,
'date' => date(DATE_RFC822), 'date' => date(DATE_RFC822),
'css' => file_get_contents(__DIR__ . '/../Resources/public/css/screen.css'), 'css' => file_get_contents(__DIR__ . '/../Resources/public/css/screen.css'),
); );

View File

@ -30,6 +30,9 @@
<call method="setApiName"> <call method="setApiName">
<argument>%nelmio_api_doc.api_name%</argument> <argument>%nelmio_api_doc.api_name%</argument>
</call> </call>
<call method="setSandboxTarget">
<argument>%nelmio_api_doc.api_sandbox_target%</argument>
</call>
</service> </service>
</services> </services>

View File

@ -1302,3 +1302,60 @@ body ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operatio
#header a { #header a {
text-decoration: none; text-decoration: none;
} }
/** sandbox */
.pane:not(.selected) {
display: none;
}
.result pre {
border: none;
padding: 10px;
font-size: 0.9em;
overflow: auto;
}
.tabs li {
display: inline;
float: left;
margin: 10px;
cursor: pointer;
}
.tabs li.selected {
font-weight: bold;
}
.panes {
clear: both;
border-top: 1px solid #C3D9EC;
}
.pane.sandbox {
border: 1px solid #C3D9EC;
border-top: none;
padding: 10px;
}
.pane.sandbox legend {
margin-bottom: 5px;
}
.remove {
cursor: pointer;
}
.pane.sandbox .result {
display: none;
}
.parameters {
float: left;
width: 50%;
}
.buttons {
clear: both;
padding-top: 10px;
}

View File

@ -6,10 +6,12 @@
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible" /> <meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible" />
<title>{{ apiName }}</title> <title>{{ apiName }}</title>
<link href="http://fonts.googleapis.com/css?family=Droid+Sans:400,700" rel="stylesheet" type="text/css" /> <link href="http://fonts.googleapis.com/css?family=Droid+Sans:400,700" rel="stylesheet" type="text/css" />
<link href="http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.css" rel="stylesheet" type="text/css"></script>
<style type="text/css"> <style type="text/css">
{{ css|raw }} {{ css|raw }}
</style> </style>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"></script> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"></script>
<script src="http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.js" type="text/javascript"></script>
</head> </head>
<body> <body>
<div id="header"> <div id="header">
@ -27,6 +29,129 @@
$('.toggler').click(function() { $('.toggler').click(function() {
$(this).next().slideToggle('slow'); $(this).next().slideToggle('slow');
}); });
$('.tabs li').click(function() {
var contentGroup = $(this).parents('.content');
$('.pane.selected', contentGroup).removeClass('selected');
$('.pane.' + $(this).data('pane'), contentGroup).addClass('selected');
$('li', $(this).parent()).removeClass('selected');
$(this).addClass('selected');
});
var prettifyResponse = function(text) {
try {
var data = typeof text === 'string' ? JSON.parse(text) : text;
text = JSON.stringify(data, undefined, ' ');
} catch (err) {
}
// HTML encode the result
return $('<div>').text(text).html();
};
var displayFinalUrl = function(xhr, method, url, container) {
container.html(method + ' ' + url);
};
var displayResponseData = function(xhr, container) {
container.removeClass('prettyprinted');
container.html(prettifyResponse(xhr.responseText));
prettyPrint && prettyPrint();
};
var displayResponseHeaders = function(xhr, container) {
var text = xhr.status + ' ' + xhr.statusText + "\n\n";
text += xhr.getAllResponseHeaders();
container.html(text);
};
var displayResponse = function(xhr, method, url, result_container) {
displayFinalUrl(xhr, method, url, $('.url', result_container));
displayResponseData(xhr, $('.response', result_container));
displayResponseHeaders(xhr, $('.headers', result_container));
result_container.show();
};
$('.pane.sandbox form').submit(function() {
var url = $(this).attr('action'),
method = $(this).attr('method'),
self = this,
params = {
_format: 'json' // default format if not overriden in the form
},
headers = {},
result_container = $('.result', $(this).parent());
// retrieve all the parameters to send
$('.parameters .tuple', $(this)).each(function() {
var key, value;
key = $('.key', $(this)).val();
value = $('.value', $(this)).val();
if (value) {
params[key] = value;
}
});
// retrieve the additional headers to send
$('.headers .tuple', $(this)).each(function() {
var key, value;
key = $('.key', $(this)).val();
value = $('.value', $(this)).val();
if (value) {
headers[key] = value;
}
});
// fix parameters in URL
for (var key in $.extend({}, params)) {
if (url.indexOf('{' + key + '}') !== -1) {
url = url.replace('{' + key + '}', params[key]);
delete params[key];
}
};
// disable all the fiels and buttons
$('input, button', $(this)).attr('disabled', 'disabled');
// and trigger the API call
$.ajax({
url: '{{ sandboxTarget }}' + url,
type: method,
data: params,
headers: headers,
complete: function(xhr) {
displayResponse(xhr, method, url, result_container);
// and enable them back
$('input, button', $(self)).removeAttr('disabled');
}
});
return false;
});
$('.pane.sandbox').on('click', '.add', function() {
var html = $(this).parents('.pane').find('.tuple_template').html();
$(this).before(html);
return false;
});
$('.pane.sandbox').on('click', '.remove', function() {
$(this).parent().remove();
});
</script> </script>
</body> </body>
</html> </html>

View File

@ -14,7 +14,15 @@
{% endif %} {% endif %}
</ul> </ul>
</div> </div>
<div class="content" style="display: {% if displayContent is defined and displayContent == true %}display{% else %}none{% endif %};"> <div class="content" style="display: {% if displayContent is defined and displayContent == true %}display{% else %}none{% endif %};">
<ul class="tabs">
<li class="selected" data-pane="content">Documentation</li>
<li data-pane="sandbox">Sandbox</li>
</ul>
<div class="panes">
<div class="pane content selected">
{% if data.requirements is defined and data.requirements is not empty %} {% if data.requirements is defined and data.requirements is not empty %}
<h4>Requirements</h4> <h4>Requirements</h4>
<table class="fullwidth"> <table class="fullwidth">
@ -89,4 +97,61 @@
</table> </table>
{% endif %} {% endif %}
</div> </div>
<div class="pane sandbox">
<form method="{{ data.method|upper }}" action="{{ data.uri }}">
<fieldset class="parameters">
<legend>Parameters</legend>
{% if data.filters is defined %}
{% for name, infos in data.filters %}
<p class="tuple">
<input type="text" class="key" value="{{ name }}" placeholder="Key" />
<span>=</span>
<input type="text" class="value" placeholder="{% if infos.description is defined %}{{ infos.description }}{% else %}Value{% endif %}" /> <span class="remove">-</span>
</p>
{% endfor %}
{% endif %}
<button class="add">New parameter</button>
</fieldset>
<fieldset class="headers">
<legend>Headers</legend>
<p class="tuple">
<input type="text" class="key" placeholder="Key" />
<span>=</span>
<input type="text" class="value" placeholder="Value" /> <span class="remove">-</span>
</p>
<button class="add">New header</button>
</fieldset>
<div class="buttons">
<input type="submit" value="Try!" />
</div>
</form>
<script type="text/x-tmpl" class="tuple_template">
<p class="tuple">
<input type="text" class="key" placeholder="Key" />
<span>=</span>
<input type="text" class="value" placeholder="Value" /> <span class="remove">-</span>
</p>
</script>
<div class="result">
<h4>Request URL</h4>
<pre class="url"></pre>
<h4>Response Body</h4>
<pre class="response prettyprint"></pre>
<h4>Response headers</h4>
<pre class="headers"></pre>
</div>
</div>
</div>
</div>
</li> </li>