Commit 6d3e60f9 by Carsten Brandt

Merge pull request #2656 from yiisoft/apidoc-refactoring

Apidoc refactoring
parents 96d13189 ca6bfdbb
......@@ -115,14 +115,14 @@ Removing Event Handlers
The correspondoing `off` method removes an event handler:
```php
// $component->off($eventName);
$component->off($eventName);
```
Yii supports the ability to associate multiple handlers with the same event. When using `off` as in the above,
every handler is removed. To remove only a specific handler, provide that as the second argument to `off`:
```php
// $component->off($eventName, $handler);
$component->off($eventName, $handler);
```
The `$handler` should be presented in the `off` method in the same way as was presented in `on` in order to remove it.
......
......@@ -25,14 +25,36 @@ to the require section of your composer.json.
Usage
-----
To generate API documentation, run the `apidoc` command.
This extension offers two commands:
- `api` to generate class API documentation.
- `guide` to render nice HTML pages from markdown files such as the yii guide.
Simple usage for stand alone class documentation:
```
vendor/bin/apidoc api source/directory ./output
```
Simple usage for stand alone guide documentation:
```
vendor/bin/apidoc guide source/docs ./output
```
You can combine them to generate class API and guide doc in one place:
```
vendor/bin/apidoc source/directory ./output
# first generate guide docs to allow links from code to guide you may skip this if you do not need these.
vendor/bin/apidoc guide source/docs ./output
# second generate API docs
vendor/bin/apidoc api source/directory ./output
# third run guide docs again to have class links enabled
vendor/bin/apidoc guide source/docs ./output
```
By default the `offline` template will be used. You can choose a different templates with the `--template=name` parameter.
Currently there is only the `offline` template available.
By default the `bootstrap` template will be used. You can choose a different templates with the `--template=name` parameter.
Currently there is only the `bootstrap` template available.
You may also add the `yii\apidoc\commands\RenderController` to your console application class map and
run it inside of your applications console app.
......
......@@ -7,29 +7,28 @@
namespace yii\apidoc\commands;
use phpDocumentor\Reflection\FileReflector;
use TokenReflection\ReflectionFile;
use yii\apidoc\templates\BaseRenderer;
use yii\console\Controller;
use yii\apidoc\components\BaseController;
use yii\apidoc\models\Context;
use yii\apidoc\renderers\ApiRenderer;
use yii\apidoc\renderers\BaseRenderer;
use yii\helpers\ArrayHelper;
use yii\helpers\Console;
use yii\helpers\FileHelper;
use yii\apidoc\components\OfflineRenderer;
use yii\apidoc\models\Context;
use Yii;
/**
* Command to render API Documentation files
* Generate class API documentation
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class RenderController extends Controller
class ApiController extends BaseController
{
public $template = 'bootstrap';
/**
* @var string url to where the guide files are located
*/
public $guide;
// TODO add force update option
/**
* Renders API documentation files
* @param array $sourceDirs
......@@ -38,56 +37,50 @@ class RenderController extends Controller
*/
public function actionIndex(array $sourceDirs, $targetDir)
{
$targetDir = rtrim(Yii::getAlias($targetDir), '\\/');
if (is_dir($targetDir) && !$this->confirm('TargetDirectory already exists. Overwrite?')) {
return 2;
}
if (!is_dir($targetDir)) {
mkdir($targetDir);
}
$renderer = $this->findRenderer();
if ($renderer === false) {
$renderer = $this->findRenderer($this->template);
$targetDir = $this->normalizeTargetDir($targetDir);
if ($targetDir === false || $renderer === false) {
return 1;
}
$renderer->targetDir = $targetDir;
if ($this->guide !== null && $renderer->hasProperty('guideUrl')) {
$renderer->guideUrl = './';
$renderer->markDownFiles = $this->findMarkdownFiles($this->guide, ['README.md']);
}
$this->stdout('Searching files to process... ');
$files = [];
foreach($sourceDirs as $source) {
foreach($this->findFiles($source) as $fileName) {
$files[$fileName] = $fileName;
}
}
$renderer->apiUrl = './';
$this->stdout('done.' . PHP_EOL, Console::FG_GREEN);
if (empty($files)) {
$this->stderr('Error: No php files found to process.' . PHP_EOL);
return 1;
// setup reference to guide
if ($this->guide !== null) {
$guideUrl = $this->guide;
$referenceFile = $guideUrl . '/' . BaseRenderer::GUIDE_PREFIX . 'references.txt';
} else {
$guideUrl = './';
$referenceFile = $targetDir . '/' . BaseRenderer::GUIDE_PREFIX . 'references.txt';
}
if (file_exists($referenceFile)) {
$renderer->guideUrl = $guideUrl;
$renderer->guideReferences = [];
foreach(explode("\n", file_get_contents($referenceFile)) as $reference) {
$renderer->guideReferences[BaseRenderer::GUIDE_PREFIX . $reference]['url'] = $renderer->generateGuideUrl($reference);
}
}
$context = new Context();
$cacheFile = $targetDir . '/cache/' . md5(serialize($files)) . '.tmp';
if (file_exists($cacheFile)) {
$this->stdout('Loading processed data from cache... ');
$context = unserialize(file_get_contents($cacheFile));
$this->stdout('done.' . PHP_EOL, Console::FG_GREEN);
$this->stdout('Checking for updated files... ');
foreach($context->files as $file => $sha) {
if (sha1_file($file) === $sha) {
unset($files[$file]);
}
// search for files to process
$files = $this->searchFiles($sourceDirs);
// load context from cache
$context = $this->loadContext($targetDir);
$this->stdout('Checking for updated files... ');
foreach($context->files as $file => $sha) {
if (!file_exists($file)) {
$this->stdout('At least one file has been removed. Rebuilding the context...');
$context = new Context();
$files = $this->searchFiles($sourceDirs);
break;
}
if (sha1_file($file) === $sha) {
unset($files[$file]);
}
$this->stdout('done.' . PHP_EOL, Console::FG_GREEN);
}
$this->stdout('done.' . PHP_EOL, Console::FG_GREEN);
// process files
$fileCount = count($files);
$this->stdout($fileCount . ' file' . ($fileCount == 1 ? '' : 's') . ' to update.' . PHP_EOL);
Console::startProgress(0, $fileCount, 'Processing files... ', false);
......@@ -100,71 +93,51 @@ class RenderController extends Controller
$this->stdout('done.' . PHP_EOL, Console::FG_GREEN);
// save processed data to cache
if (!is_dir(dirname($cacheFile))) {
mkdir(dirname($cacheFile));
}
file_put_contents($cacheFile, serialize($context));
$this->storeContext($context, $targetDir);
$this->stdout('Updating cross references and backlinks... ');
$context->updateReferences();
$this->stdout('done.' . PHP_EOL, Console::FG_GREEN);
$this->updateContext($context);
// render models
$renderer->renderApi($context, $this);
$renderer->controller = $this;
$renderer->render($context, $targetDir);
ArrayHelper::multisort($context->errors, 'file');
print_r($context->errors);
// render guide if specified
if ($this->guide !== null) {
$renderer->renderMarkdownFiles($this);
$this->stdout('Publishing images...');
FileHelper::copyDirectory(rtrim($this->guide, '/\\') . '/images', $targetDir . '/images');
$this->stdout('done.' . PHP_EOL, Console::FG_GREEN);
if (!empty($context->errors)) {
ArrayHelper::multisort($context->errors, 'file');
file_put_contents($targetDir . '/errors.txt', print_r($context->errors, true));
$this->stdout(count($context->errors) . " errors have been logged to $targetDir/errors.txt\n", Console::FG_RED, Console::BOLD);
}
}
/**
* @return BaseRenderer
*/
protected function findRenderer()
{
$rendererClass = 'yii\\apidoc\\templates\\' . $this->template . '\\Renderer';
if (!class_exists($rendererClass)) {
$this->stderr('Renderer not found.' . PHP_EOL);
return false;
}
return new $rendererClass();
}
protected function findFiles($path, $except = ['vendor/', 'tests/'])
{
$path = FileHelper::normalizePath($path);
$options = [
'filter' => function ($path) {
if (is_file($path)) {
$file = basename($path);
if ($file[0] < 'A' || $file[0] > 'Z') {
return false;
if (is_file($path)) {
$file = basename($path);
if ($file[0] < 'A' || $file[0] > 'Z') {
return false;
}
}
}
return null;
},
return null;
},
'only' => ['*.php'],
'except' => $except,
];
return FileHelper::findFiles($path, $options);
}
protected function findMarkdownFiles($path, $except = [])
/**
* @return ApiRenderer
*/
protected function findRenderer($template)
{
$path = FileHelper::normalizePath($path);
$options = [
'only' => ['*.md'],
'except' => $except,
];
return FileHelper::findFiles($path, $options);
$rendererClass = 'yii\\apidoc\\templates\\' . $template . '\\ApiRenderer';
if (!class_exists($rendererClass)) {
$this->stderr('Renderer not found.' . PHP_EOL);
return false;
}
return new $rendererClass();
}
/**
......@@ -174,4 +147,4 @@ class RenderController extends Controller
{
return array_merge(parent::globalOptions(), ['template', 'guide']);
}
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\commands;
use yii\apidoc\components\BaseController;
use yii\apidoc\models\Context;
use yii\apidoc\renderers\BaseRenderer;
use yii\apidoc\renderers\GuideRenderer;
use yii\helpers\Console;
use yii\helpers\FileHelper;
use Yii;
/**
* This command can render documentation stored as markdown files such as the yii guide
* or your own applications documentation setup.
*
*/
class GuideController extends BaseController
{
/**
* @var string path or URL to the api docs to allow links to classes and properties/methods.
*/
public $apiDocs;
/**
* Renders API documentation files
* @param array $sourceDirs
* @param string $targetDir
* @return int
*/
public function actionIndex(array $sourceDirs, $targetDir)
{
$renderer = $this->findRenderer($this->template);
$targetDir = $this->normalizeTargetDir($targetDir);
if ($targetDir === false || $renderer === false) {
return 1;
}
$renderer->guideUrl = './';
// setup reference to apidoc
if ($this->apiDocs !== null) {
$renderer->apiUrl = $this->apiDocs;
$renderer->apiContext = $this->loadContext($this->apiDocs);
} elseif (file_exists($targetDir . '/cache/apidoc.data')) {
$renderer->apiUrl = './';
$renderer->apiContext = $this->loadContext($targetDir);
} else {
$renderer->apiContext = new Context();
}
$this->updateContext($renderer->apiContext);
// search for files to process
$files = $this->searchFiles($sourceDirs);
$renderer->controller = $this;
$renderer->render($files, $targetDir);
$this->stdout('Publishing images...');
foreach($sourceDirs as $source) {
FileHelper::copyDirectory(rtrim($source, '/\\') . '/images', $targetDir . '/images');
}
$this->stdout('done.' . PHP_EOL, Console::FG_GREEN);
// generate api references.txt
$references = [];
foreach($files as $file) {
$references[] = basename($file, '.md');
}
file_put_contents($targetDir . '/guide-references.txt', implode("\n", $references));
}
protected function findFiles($path, $except = ['README.md'])
{
$path = FileHelper::normalizePath($path);
$options = [
'only' => ['*.md'],
'except' => $except,
];
return FileHelper::findFiles($path, $options);
}
/**
* @return GuideRenderer
*/
protected function findRenderer($template)
{
$rendererClass = 'yii\\apidoc\\templates\\' . $template . '\\GuideRenderer';
if (!class_exists($rendererClass)) {
$this->stderr('Renderer not found.' . PHP_EOL);
return false;
}
return new $rendererClass();
}
/**
* @inheritdoc
*/
public function globalOptions()
{
return array_merge(parent::globalOptions(), ['apiDocs']);
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\components;
use yii\apidoc\renderers\BaseRenderer;
use yii\console\Controller;
use yii\helpers\Console;
use yii\apidoc\models\Context;
use Yii;
/**
* Command to render API Documentation files
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
abstract class BaseController extends Controller
{
/**
* @var string template to use for rendering
*/
public $template = 'bootstrap';
/**
* @var string|array files to exclude. NOT IMPLEMENTED YET
*/
public $exclude; // TODO implement
protected function normalizeTargetDir($target)
{
$target = rtrim(Yii::getAlias($target), '\\/');
if (file_exists($target)) {
if (is_dir($target) && !$this->confirm('TargetDirectory already exists. Overwrite?', true)) {
$this->stderr('User aborted.' . PHP_EOL);
return false;
}
if (is_file($target)) {
$this->stderr("Error: Target directory \"$target\" is a file!" . PHP_EOL);
return false;
}
} else {
mkdir($target, 0777, true);
}
return $target;
}
protected function searchFiles($sourceDirs)
{
$this->stdout('Searching files to process... ');
$files = [];
foreach($sourceDirs as $source) {
foreach($this->findFiles($source) as $fileName) {
$files[$fileName] = $fileName;
}
}
$this->stdout('done.' . PHP_EOL, Console::FG_GREEN);
if (empty($files)) {
$this->stderr('Error: No files found to process.' . PHP_EOL);
return 1;
}
return $files;
}
protected abstract function findFiles($dir);
protected function loadContext($location)
{
$context = new Context();
$cacheFile = $location . '/cache/apidoc.data';
$this->stdout('Loading apidoc data from cache... ');
if (file_exists($cacheFile)) {
$context = unserialize(file_get_contents($cacheFile));
$this->stdout('done.' . PHP_EOL, Console::FG_GREEN);
} else {
$this->stdout('no data available.' . PHP_EOL, Console::FG_YELLOW);
}
return $context;
}
protected function storeContext($context, $location)
{
$cacheFile = $location . '/cache/apidoc.data';
if (!is_dir($dir = dirname($cacheFile))) {
mkdir($dir, 0777, true);
}
file_put_contents($cacheFile, serialize($context));
}
/**
* @param Context $context
*/
protected function updateContext($context)
{
$this->stdout('Updating cross references and backlinks... ');
$context->updateReferences();
$this->stdout('done.' . PHP_EOL, Console::FG_GREEN);
}
/**
* @return BaseRenderer
*/
protected abstract function findRenderer($template);
/**
* @inheritdoc
*/
public function globalOptions()
{
return array_merge(parent::globalOptions(), ['template', 'exclude']);
}
}
......@@ -11,7 +11,7 @@ use cebe\markdown\GithubMarkdown;
use phpDocumentor\Reflection\DocBlock\Type\Collection;
use yii\apidoc\models\MethodDoc;
use yii\apidoc\models\TypeDoc;
use yii\apidoc\templates\BaseRenderer;
use yii\apidoc\renderers\BaseRenderer;
use yii\helpers\Inflector;
use yii\helpers\Markdown;
......@@ -35,7 +35,7 @@ class ApiMarkdown extends GithubMarkdown
parent::prepare();
// add references to guide pages
$this->references = array_merge($this->references, static::$renderer->getGuideReferences());
$this->references = array_merge($this->references, static::$renderer->guideReferences);
}
/**
......@@ -140,9 +140,9 @@ class ApiMarkdown extends GithubMarkdown
// Collection resolves relative types
$typeName = (new Collection([$typeName], $context->phpDocContext))->__toString();
}
$type = static::$renderer->context->getType($typeName);
$type = static::$renderer->apiContext->getType($typeName);
if ($type === null) {
static::$renderer->context->errors[] = [
static::$renderer->apiContext->errors[] = [
'file' => ($context !== null) ? $context->sourceFile : null,
'message' => 'broken link to ' . $typeName . '::' . $subjectName . (($context !== null) ? ' in ' . $context->name : ''),
];
......@@ -159,11 +159,11 @@ class ApiMarkdown extends GithubMarkdown
}
}
return [
static::$renderer->subjectLink($subject, $title),
static::$renderer->createSubjectLink($subject, $title),
$offset
];
} else {
static::$renderer->context->errors[] = [
static::$renderer->apiContext->errors[] = [
'file' => ($context !== null) ? $context->sourceFile : null,
'message' => 'broken link to ' . $type->name . '::' . $subjectName . (($context !== null) ? ' in ' . $context->name : ''),
];
......@@ -175,7 +175,7 @@ class ApiMarkdown extends GithubMarkdown
}
} elseif ($context !== null && ($subject = $context->findSubject($object)) !== null) {
return [
static::$renderer->subjectLink($subject, $title),
static::$renderer->createSubjectLink($subject, $title),
$offset
];
}
......@@ -183,13 +183,13 @@ class ApiMarkdown extends GithubMarkdown
// Collection resolves relative types
$object = (new Collection([$object], $context->phpDocContext))->__toString();
}
if (($type = static::$renderer->context->getType($object)) !== null) {
if (($type = static::$renderer->apiContext->getType($object)) !== null) {
return [
static::$renderer->typeLink($type, $title),
static::$renderer->createTypeLink($type, null, $title),
$offset
];
}
static::$renderer->context->errors[] = [
static::$renderer->apiContext->errors[] = [
'file' => ($context !== null) ? $context->sourceFile : null,
'message' => 'broken link to ' . $object . (($context !== null) ? ' in ' . $context->name : ''),
];
......@@ -228,7 +228,7 @@ class ApiMarkdown extends GithubMarkdown
}
if (is_string($context)) {
$context = static::$renderer->context->getType($context);
$context = static::$renderer->apiContext->getType($context);
}
Markdown::$flavors['api']->context = $context;
......
......@@ -5,7 +5,7 @@
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\templates;
namespace yii\apidoc\renderers;
use Yii;
use yii\apidoc\models\ClassDoc;
......@@ -25,47 +25,13 @@ use yii\console\Controller;
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
abstract class BaseRenderer extends Component
abstract class ApiRenderer extends BaseRenderer
{
/**
* @var Context the [[Context]] currently being rendered.
*/
public $context;
/**
* @var array files for guide pages
*/
public $markDownFiles = [];
/**
* Renders a given [[Context]].
*
* @param Context $context the api documentation context to render.
* @param Controller $controller the apidoc controller instance. Can be used to control output.
*/
public abstract function renderApi($context, $controller);
/**
* Renders a given [[Context]].
*
* @param array $files list of markdown files to render
* @param Context $context the api documentation context to render.
* @param Controller $controller the apidoc controller instance. Can be used to control output.
*/
public abstract function renderMarkdownFiles($controller);
/**
* creates a link to a type (class, interface or trait)
* @param ClassDoc|InterfaceDoc|TraitDoc $types
* @param string $title
* @return string
*/
public abstract function typeLink($types, $title = null);
/**
* creates a link to a subject
* @param PropertyDoc|MethodDoc|ConstDoc|EventDoc $subject
* @param string $title
* @return string
* @param $targetDir
*/
public abstract function subjectLink($subject, $title = null);
public abstract function render($context, $targetDir);
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\renderers;
use Yii;
use yii\apidoc\helpers\ApiMarkdown;
use yii\apidoc\models\BaseDoc;
use yii\apidoc\models\ClassDoc;
use yii\apidoc\models\ConstDoc;
use yii\apidoc\models\Context;
use yii\apidoc\models\EventDoc;
use yii\apidoc\models\InterfaceDoc;
use yii\apidoc\models\MethodDoc;
use yii\apidoc\models\PropertyDoc;
use yii\apidoc\models\TraitDoc;
use yii\apidoc\models\TypeDoc;
use yii\base\Component;
use yii\console\Controller;
use yii\helpers\Html;
/**
* Base class for all documentation renderers
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
abstract class BaseRenderer extends Component
{
const GUIDE_PREFIX = 'guide-';
public $apiUrl;
/**
* @var Context the [[Context]] currently being rendered.
*/
public $apiContext;
/**
* @var Controller the apidoc controller instance. Can be used to control output.
*/
public $controller;
public $guideUrl;
public $guideReferences = [];
public function init()
{
ApiMarkdown::$renderer = $this;
}
/**
* creates a link to a type (class, interface or trait)
* @param ClassDoc|InterfaceDoc|TraitDoc|ClassDoc[]|InterfaceDoc[]|TraitDoc[] $types
* @param string $title a title to be used for the link TODO check whether [[yii\...|Class]] is supported
* @param BaseDoc $context
* @return string
*/
public function createTypeLink($types, $context = null, $title = null)
{
if (!is_array($types)) {
$types = [$types];
}
if (count($types) > 1) {
$title = null;
}
$links = [];
foreach($types as $type) {
$postfix = '';
if (!is_object($type)) {
if (substr($type, -2, 2) == '[]') {
$postfix = '[]';
$type = substr($type, 0, -2);
}
if (($t = $this->apiContext->getType(ltrim($type, '\\'))) !== null) {
$type = $t;
} elseif ($type[0] !== '\\' && ($t = $this->apiContext->getType($this->resolveNamespace($context) . '\\' . ltrim($type, '\\'))) !== null) {
$type = $t;
} else {
ltrim($type, '\\');
}
}
if (!is_object($type)) {
$links[] = $type;
} else {
$linkText = $type->name;
if ($title !== null) {
$linkText = $title;
}
$links[] = $this->generateLink($linkText, $this->generateApiUrl($type->name)) . $postfix;
}
}
return implode('|', $links);
}
/**
* creates a link to a subject
* @param PropertyDoc|MethodDoc|ConstDoc|EventDoc $subject
* @param string $title
* @return string
*/
public function createSubjectLink($subject, $title = null)
{
if ($title === null) {
if ($subject instanceof MethodDoc) {
$title = $subject->name . '()';
} else {
$title = $subject->name;
}
}
if (($type = $this->apiContext->getType($subject->definedBy)) === null) {
return $subject->name;
} else {
$link = $this->generateApiUrl($type->name);
if ($subject instanceof MethodDoc) {
$link .= '#' . $subject->name . '()';
} else {
$link .= '#' . $subject->name;
}
$link .= '-detail';
return Html::a($title, null, ['href' => $link]);
}
}
/**
* @param BaseDoc $context
*/
private function resolveNamespace($context)
{
// TODO use phpdoc Context for this
if ($context === null) {
return '';
}
if ($context instanceof TypeDoc) {
return $context->namespace;
}
if ($context->hasProperty('definedBy')) {
$type = $this->apiContext->getType($context);
if ($type !== null) {
return $type->namespace;
}
}
return '';
}
/**
* generate link markup
* @param $text
* @param $href
* @return mixed
*/
protected abstract function generateLink($text, $href);
/**
* Generate an url to a type in apidocs
* @param $typeName
* @return mixed
*/
public abstract function generateApiUrl($typeName);
/**
* Generate an url to a guide page
* @param string $file
* @return string
*/
public function generateGuideUrl($file)
{
return rtrim($this->guideUrl, '/') . '/' . static::GUIDE_PREFIX . basename($file, '.md') . '.html';
}
}
\ No newline at end of file
......@@ -5,18 +5,34 @@
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\templates\offline;
namespace yii\apidoc\renderers;
use Yii;
use yii\apidoc\models\ClassDoc;
use yii\apidoc\models\ConstDoc;
use yii\apidoc\models\Context;
use yii\apidoc\models\EventDoc;
use yii\apidoc\models\InterfaceDoc;
use yii\apidoc\models\MethodDoc;
use yii\apidoc\models\PropertyDoc;
use yii\apidoc\models\TraitDoc;
use yii\base\Component;
use yii\console\Controller;
/**
* Base class for all Guide documentation renderers
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class Renderer extends \yii\apidoc\templates\html\Renderer
abstract class GuideRenderer extends BaseRenderer
{
public $apiLayout = '@yii/apidoc/templates/offline/views/offline.php';
public $indexView = '@yii/apidoc/templates/offline/views/index.php';
/**
* Render markdown files
*
* @param array $files list of markdown files to render
* @param $targetDir
*/
public abstract function render($files, $targetDir);
public $pageTitle = 'Yii Framework 2.0 API Documentation';
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\templates\bootstrap;
use yii\apidoc\helpers\ApiMarkdown;
use yii\apidoc\models\ClassDoc;
use yii\apidoc\models\ConstDoc;
use yii\apidoc\models\Context;
use yii\apidoc\models\EventDoc;
use yii\apidoc\models\InterfaceDoc;
use yii\apidoc\models\MethodDoc;
use yii\apidoc\models\PropertyDoc;
use yii\apidoc\models\TraitDoc;
use yii\console\Controller;
use Yii;
use yii\helpers\Console;
use yii\helpers\Html;
/**
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class ApiRenderer extends \yii\apidoc\templates\html\ApiRenderer
{
use RendererTrait;
public $layout = '@yii/apidoc/templates/bootstrap/layouts/api.php';
public $indexView = '@yii/apidoc/templates/bootstrap/views/index.php';
/**
* @inheritdoc
*/
public function render($context, $targetDir)
{
$types = array_merge($context->classes, $context->interfaces, $context->traits);
$extTypes = [];
foreach($this->extensions as $k => $ext) {
$extType = $this->filterTypes($types, $ext);
if (empty($extType)) {
unset($this->extensions[$k]);
continue;
}
$extTypes[$ext] = $extType;
}
// render view files
parent::render($context, $targetDir);
if ($this->controller !== null) {
$this->controller->stdout('generating extension index files...');
}
foreach($extTypes as $ext => $extType) {
$readme = @file_get_contents("https://raw.github.com/yiisoft/yii2-$ext/master/README.md");
$indexFileContent = $this->renderWithLayout($this->indexView, [
'docContext' => $context,
'types' => $extType,
'readme' => $readme ?: null,
]);
file_put_contents($targetDir . "/ext-{$ext}-index.html", $indexFileContent);
}
$yiiTypes = $this->filterTypes($types, 'yii');
if (empty($yiiTypes)) {
// $readme = @file_get_contents("https://raw.github.com/yiisoft/yii2-framework/master/README.md");
$indexFileContent = $this->renderWithLayout($this->indexView, [
'docContext' => $context,
'types' => $this->filterTypes($types, 'app'),
'readme' => null,
]);
} else {
$readme = @file_get_contents("https://raw.github.com/yiisoft/yii2-framework/master/README.md");
$indexFileContent = $this->renderWithLayout($this->indexView, [
'docContext' => $context,
'types' => $yiiTypes,
'readme' => $readme ?: null,
]);
}
file_put_contents($targetDir . '/index.html', $indexFileContent);
if ($this->controller !== null) {
$this->controller->stdout('done.' . PHP_EOL, Console::FG_GREEN);
}
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\templates\bootstrap;
use yii\apidoc\helpers\ApiMarkdown;
use yii\apidoc\models\ClassDoc;
use yii\apidoc\models\ConstDoc;
use yii\apidoc\models\Context;
use yii\apidoc\models\EventDoc;
use yii\apidoc\models\InterfaceDoc;
use yii\apidoc\models\MethodDoc;
use yii\apidoc\models\PropertyDoc;
use yii\apidoc\models\TraitDoc;
use yii\console\Controller;
use Yii;
use yii\helpers\Console;
use yii\helpers\Html;
/**
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class GuideRenderer extends \yii\apidoc\templates\html\GuideRenderer
{
use RendererTrait;
public $layout = '@yii/apidoc/templates/bootstrap/layouts/guide.php';
/**
* @inheritDoc
*/
public function render($files, $targetDir)
{
$types = array_merge($this->apiContext->classes, $this->apiContext->interfaces, $this->apiContext->traits);
$extTypes = [];
foreach($this->extensions as $k => $ext) {
$extType = $this->filterTypes($types, $ext);
if (empty($extType)) {
unset($this->extensions[$k]);
continue;
}
$extTypes[$ext] = $extType;
}
parent::render($files, $targetDir);
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\templates\bootstrap;
use yii\apidoc\helpers\ApiMarkdown;
use yii\apidoc\models\Context;
use yii\console\Controller;
use Yii;
use yii\helpers\Console;
/**
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class Renderer extends \yii\apidoc\templates\html\Renderer
{
public $apiLayout = '@yii/apidoc/templates/bootstrap/layouts/api.php';
public $guideLayout = '@yii/apidoc/templates/bootstrap/layouts/guide.php';
public $indexView = '@yii/apidoc/templates/bootstrap/views/index.php';
public $pageTitle = 'Yii Framework 2.0 Documentation';
namespace yii\apidoc\templates\bootstrap;
public $guideUrl;
trait RendererTrait
{
public $extensions = [
'apidoc',
'authclient',
......@@ -47,38 +28,6 @@ class Renderer extends \yii\apidoc\templates\html\Renderer
'twig',
];
/**
* Renders a given [[Context]].
*
* @param Context $context the api documentation context to render.
* @param Controller $controller the apidoc controller instance. Can be used to control output.
*/
public function renderApi($context, $controller)
{
parent::renderApi($context, $controller);
$dir = Yii::getAlias($this->targetDir);
$types = array_merge($context->classes, $context->interfaces, $context->traits);
$controller->stdout('generating extension index files...');
foreach($this->extensions as $ext) {
$readme = @file_get_contents("https://raw.github.com/yiisoft/yii2-$ext/master/README.md");
$indexFileContent = $this->renderWithLayout($this->indexView, [
'docContext' => $context,
'types' => $this->filterTypes($types, $ext),
'readme' => $readme ?: null,
]);
file_put_contents($dir . "/ext_{$ext}_index.html", $indexFileContent);
}
$readme = @file_get_contents("https://raw.github.com/yiisoft/yii2-framework/master/README.md");
$indexFileContent = $this->renderWithLayout($this->indexView, [
'docContext' => $context,
'types' => $this->filterTypes($types, 'yii'),
'readme' => $readme ?: null,
]);
file_put_contents($dir . '/index.html', $indexFileContent);
$controller->stdout('done.' . PHP_EOL, Console::FG_GREEN);
}
public function getNavTypes($type, $types)
{
if ($type === null) {
......@@ -90,13 +39,12 @@ class Renderer extends \yii\apidoc\templates\html\Renderer
if ($type->name == 'Yii') {
$navClasses = 'yii';
} elseif (strncmp($type->name, 'yii\\', 4) == 0) {
$navClasses = 'yii';
$subName = substr($type->name, 4);
if (($pos = strpos($subName, '\\')) !== false) {
$subNamespace = substr($subName, 0, $pos);
if (in_array($subNamespace, $extensions)) {
$navClasses = $subNamespace;
} else {
$navClasses = 'yii';
}
}
}
......@@ -116,6 +64,9 @@ class Renderer extends \yii\apidoc\templates\html\Renderer
case 'yii':
$self = $this;
$types = array_filter($types, function($val) use ($self) {
if ($val->name == 'Yii') {
return true;
}
if (strlen($val->name) < 5) {
return false;
}
......@@ -130,77 +81,4 @@ class Renderer extends \yii\apidoc\templates\html\Renderer
}
return $types;
}
/**
* Renders a given [[Context]].
*
* @param Controller $controller the apidoc controller instance. Can be used to control output.
*/
public function renderMarkdownFiles($controller)
{
$files = $this->markDownFiles;
$dir = Yii::getAlias($this->targetDir);
if (!is_dir($dir)) {
mkdir($dir, 0777, true);
}
ApiMarkdown::$renderer = $this;
$fileCount = count($files) + 1;
Console::startProgress(0, $fileCount, 'Rendering markdown files: ', false);
$done = 0;
$fileData = [];
$headlines = [];
foreach($files as $file) {
$fileData[$file] = file_get_contents($file);
if (basename($file) == 'index.md') {
continue; // to not add index file to nav
}
if (preg_match("/^(.*)\n=+/", $fileData[$file], $matches)) {
$headlines[$file] = $matches[1];
} else {
$headlines[$file] = basename($file);
}
}
foreach($fileData as $file => $content) {
$output = ApiMarkdown::process($content); // TODO generate links to yiiframework.com by default
$output = $this->fixMarkdownLinks($output);
if ($this->guideLayout !== false) {
$params = [
'headlines' => $headlines,
'currentFile' => $file,
'content' => $output,
];
$output = $this->getView()->renderFile($this->guideLayout, $params, $this);
}
$fileName = $this->generateGuideFileName($file);
file_put_contents($dir . '/' . $fileName, $output);
Console::updateProgress(++$done, $fileCount);
}
Console::updateProgress(++$done, $fileCount);
Console::endProgress(true);
$controller->stdout('done.' . PHP_EOL, Console::FG_GREEN);
}
protected function generateGuideFileName($file)
{
return 'guide_' . basename($file, '.md') . '.html';
}
public function getGuideReferences()
{
$refs = [];
foreach($this->markDownFiles as $file) {
$refName = 'guide-' . basename($file, '.md');
$refs[$refName] = ['url' => $this->generateGuideFileName($file)];
}
return $refs;
}
protected function fixMarkdownLinks($content)
{
$content = preg_replace('/href\s*=\s*"([^"\/]+)\.md(#.*)?"/i', 'href="guide_\1.html\2"', $content);
return $content;
}
}
\ No newline at end of file
}
\ No newline at end of file
......@@ -109,7 +109,7 @@ class SideNavWidget extends \yii\bootstrap\Widget
unset($items[$i]);
continue;
}
$items[] = $this->renderItem($item);
$items[] = $this->renderItem($item, count($this->items) !== 1);
}
return Html::tag('div', implode("\n", $items), $this->options);
......@@ -118,10 +118,12 @@ class SideNavWidget extends \yii\bootstrap\Widget
/**
* Renders a widget's item.
* @param string|array $item the item to render.
* @param bool $collapsed whether to collapse item if not active
* @throws \yii\base\InvalidConfigException
* @return string the rendering result.
* @throws InvalidConfigException
* @throws InvalidConfigException if label is not defined
*/
public function renderItem($item)
public function renderItem($item, $collapsed = true)
{
if (is_string($item)) {
return $item;
......@@ -163,7 +165,7 @@ class SideNavWidget extends \yii\bootstrap\Widget
'encodeLabels' => $this->encodeLabels,
'view' => $this->getView(),
'options' => [
'class' => "submenu panel-collapse collapse" . ($active ? ' in' : '')
'class' => "submenu panel-collapse collapse" . ($active || !$collapsed ? ' in' : '')
]
]);
}
......
<?php
use yii\apidoc\templates\bootstrap\ApiRenderer;
use yii\apidoc\templates\bootstrap\SideNavWidget;
use yii\helpers\StringHelper;
/**
* @var yii\web\View $this
* @var string $content
*/
/** @var ApiRenderer $renderer */
$renderer = $this->context;
$this->beginContent('@yii/apidoc/templates/bootstrap/layouts/main.php'); ?>
<div class="row">
<div class="col-md-2">
<?php
$types = $this->context->getNavTypes(isset($type) ? $type : null, $types);
$types = $renderer->getNavTypes(isset($type) ? $type : null, $types);
ksort($types);
$nav = [];
foreach($types as $i=>$class) {
......@@ -28,7 +34,7 @@ $this->beginContent('@yii/apidoc/templates/bootstrap/layouts/main.php'); ?>
}
$nav[$namespace]['items'][] = [
'label' => StringHelper::basename($class->name),
'url' => './' . $this->context->generateUrl($class->name),
'url' => './' . $renderer->generateApiUrl($class->name),
'active' => isset($type) && ($class->name == $type->name),
];
} ?>
......
<?php
use yii\apidoc\templates\bootstrap\SideNavWidget;
/**
* @var yii\web\View $this
* @var string $content
*/
$this->beginContent('@yii/apidoc/templates/bootstrap/layouts/main.php'); ?>
......@@ -14,20 +16,13 @@ $this->beginContent('@yii/apidoc/templates/bootstrap/layouts/main.php'); ?>
$nav = [];
$nav[] = [
'label' => 'Index',
'url' => './guide_index.html',
'url' => $this->context->generateGuideUrl('index.md'),
'active' => isset($currentFile) && (basename($currentFile) == 'index.md'),
];
foreach($headlines as $file => $headline) {
// if (!isset($nav[$namespace])) {
// $nav[$namespace] = [
// 'label' => $namespace,
// 'url' => '#',
// 'items' => [],
// ];
// }
$nav/*[$namespace]['items']*/[] = [
$nav[] = [
'label' => $headline,
'url' => './guide_' . str_replace('.md', '.html', basename($file)),
'url' => $this->context->generateGuideUrl($file),
'active' => isset($currentFile) && ($file == $currentFile),
];
} ?>
......
<?php
use yii\apidoc\renderers\BaseRenderer;
use yii\bootstrap\Nav;
use yii\bootstrap\NavBar;
use yii\helpers\Html;
......@@ -17,7 +19,7 @@ $this->registerJs(<<<JS
window.addEventListener("hashchange", shiftWindow);
JS
,
\yii\web\View::POS_HEAD
\yii\web\View::POS_READY
);
$this->beginPage();
......@@ -38,27 +40,32 @@ $this->beginPage();
<?php
NavBar::begin([
'brandLabel' => $this->context->pageTitle,
'brandUrl' => './index.html',
'brandUrl' => ($this->context->apiUrl === null && $this->context->guideUrl !== null) ? './guide-index.html' : './index.html',
'options' => [
'class' => 'navbar-inverse navbar-fixed-top',
],
'renderInnerContainer' => false,
'view' => $this,
]);
$extItems = [];
foreach($this->context->extensions as $ext) {
$extItems[] = [
'label' => $ext,
'url' => "./ext_{$ext}_index.html",
];
$nav = [];
if ($this->context->apiUrl !== null) {
$nav[] = ['label' => 'Class reference', 'url' => rtrim($this->context->apiUrl, '/') . '/index.html'];
if (!empty($this->context->extensions))
{
$extItems = [];
foreach($this->context->extensions as $ext) {
$extItems[] = [
'label' => $ext,
'url' => "./ext-{$ext}-index.html",
];
}
$nav[] = ['label' => 'Extensions', 'items' => $extItems];
}
}
$nav = [
['label' => 'Class reference', 'url' => './index.html'],
// ['label' => 'Application API', 'url' => '/site/about'],
['label' => 'Extensions', 'items' => $extItems],
];
if ($this->context->guideUrl !== null) {
$nav[] = ['label' => 'Guide', 'url' => $this->context->guideUrl . 'guide_index.html'];
$nav[] = ['label' => 'Guide', 'url' => rtrim($this->context->guideUrl, '/') . '/' . BaseRenderer::GUIDE_PREFIX . 'index.html'];
}
echo Nav::widget([
......
......@@ -6,8 +6,11 @@ use yii\apidoc\models\TraitDoc;
/**
* @var ClassDoc[]|InterfaceDoc[]|TraitDoc[] $types
* @var yii\web\View $this
* @var \yii\apidoc\templates\html\ApiRenderer $renderer
*/
$renderer = $this->context;
if (isset($readme)) {
echo \yii\apidoc\helpers\ApiMarkdown::process($readme);
}
......@@ -29,7 +32,7 @@ ksort($types);
foreach($types as $i=>$class):
?>
<tr>
<td><?= $this->context->typeLink($class, $class->name) ?></td>
<td><?= $renderer->createTypeLink($class, $class, $class->name) ?></td>
<td><?= \yii\apidoc\helpers\ApiMarkdown::process($class->shortDescription, $class, true) ?></td>
</tr>
<?php endforeach; ?>
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\templates\html;
use yii\apidoc\helpers\ApiMarkdown;
use yii\console\Controller;
use yii\helpers\Console;
use yii\apidoc\renderers\GuideRenderer as BaseGuideRenderer;
use Yii;
use yii\helpers\Html;
use yii\web\AssetManager;
use yii\web\View;
/**
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
abstract class GuideRenderer extends BaseGuideRenderer
{
public $pageTitle;
public $layout;
/**
* @var View
*/
private $_view;
private $_targetDir;
public function init()
{
parent::init();
if ($this->pageTitle === null) {
$this->pageTitle = 'Yii Framework 2.0 API Documentation'; // TODO guess page title
}
}
/**
* @return View the view instance
*/
public function getView()
{
if ($this->_view === null) {
$this->_view = new View();
$assetPath = Yii::getAlias($this->_targetDir) . '/assets';
if (!is_dir($assetPath)) {
mkdir($assetPath);
}
$this->_view->assetManager = new AssetManager([
'basePath' => $assetPath,
'baseUrl' => './assets',
]);
}
return $this->_view;
}
/**
* Renders a given [[Context]].
*
* @param Controller $controller the apidoc controller instance. Can be used to control output.
*/
public function render($files, $targetDir)
{
$this->_targetDir = $targetDir;
$fileCount = count($files) + 1;
if ($this->controller !== null) {
Console::startProgress(0, $fileCount, 'Rendering markdown files: ', false);
}
$done = 0;
$fileData = [];
$headlines = [];
foreach($files as $file) {
$fileData[$file] = file_get_contents($file);
if (basename($file) == 'index.md') {
continue; // to not add index file to nav
}
if (preg_match("/^(.*)\n=+/", $fileData[$file], $matches)) {
$headlines[$file] = $matches[1];
} else {
$headlines[$file] = basename($file);
}
}
foreach($fileData as $file => $content) {
$output = ApiMarkdown::process($content); // TODO generate links to yiiframework.com by default
$output = $this->fixMarkdownLinks($output);
if ($this->layout !== false) {
$params = [
'headlines' => $headlines,
'currentFile' => $file,
'content' => $output,
];
$output = $this->getView()->renderFile($this->layout, $params, $this);
}
$fileName = $this->generateGuideFileName($file);
file_put_contents($targetDir . '/' . $fileName, $output);
if ($this->controller !== null) {
Console::updateProgress(++$done, $fileCount);
}
}
if ($this->controller !== null) {
Console::updateProgress(++$done, $fileCount);
Console::endProgress(true);
$this->controller->stdout('done.' . PHP_EOL, Console::FG_GREEN);
}
}
protected function generateGuideFileName($file)
{
return static::GUIDE_PREFIX . basename($file, '.md') . '.html';
}
public function getGuideReferences()
{
// TODO implement for api docs
// $refs = [];
// foreach($this->markDownFiles as $file) {
// $refName = 'guide-' . basename($file, '.md');
// $refs[$refName] = ['url' => $this->generateGuideFileName($file)];
// }
// return $refs;
}
protected function fixMarkdownLinks($content)
{
$content = preg_replace('/href\s*=\s*"([^"\/]+)\.md(#.*)?"/i', 'href="' . static::GUIDE_PREFIX . '\1.html\2"', $content);
return $content;
}
/**
* generate link markup
* @param $text
* @param $href
* @return mixed
*/
protected function generateLink($text, $href)
{
return Html::a($text, null, ['href' => $href]);
}
/**
* Generate an url to a type in apidocs
* @param $typeName
* @return mixed
*/
public function generateApiUrl($typeName)
{
return rtrim($this->apiUrl, '/') . '/' . strtolower(str_replace('\\', '-', $typeName)) . '.html';
}
}
\ No newline at end of file
The html API doc template
-------------------------
=========================
This templates provides view files and a Renderer class that can be reused in other html templates.
\ No newline at end of file
......@@ -7,8 +7,11 @@ use yii\helpers\ArrayHelper;
/**
* @var ClassDoc $type
* @var yii\web\View $this
* @var \yii\apidoc\templates\html\ApiRenderer $renderer
*/
$renderer = $this->context;
if (empty($type->constants)) {
return;
}
......@@ -34,7 +37,7 @@ ArrayHelper::multisort($constants, 'name');
<td><?= $constant->name ?><a name="<?= $constant->name ?>-detail"></a></td>
<td><?= $constant->value ?></td>
<td><?= ApiMarkdown::process($constant->shortDescription . "\n" . $constant->description, $constant->definedBy, true) ?></td>
<td><?= $this->context->typeLink($constant->definedBy) ?></td>
<td><?= $renderer->createTypeLink($constant->definedBy) ?></td>
</tr>
<?php endforeach; ?>
</table>
......
......@@ -7,8 +7,11 @@ use yii\helpers\ArrayHelper;
/**
* @var ClassDoc $type
* @var yii\web\View $this
* @var \yii\apidoc\templates\html\ApiRenderer $renderer
*/
$renderer = $this->context;
if (empty($type->events)) {
return;
}
......@@ -31,15 +34,15 @@ ArrayHelper::multisort($events, 'name');
</tr>
<?php foreach($events as $event): ?>
<tr<?= $event->definedBy != $type->name ? ' class="inherited"' : '' ?> id="<?= $event->name ?>">
<td><?= $this->context->subjectLink($event) ?></td>
<td><?= $this->context->typeLink($event->types) ?></td>
<td><?= $renderer->createSubjectLink($event) ?></td>
<td><?= $renderer->createTypeLink($event->types) ?></td>
<td>
<?= ApiMarkdown::process($event->shortDescription, $event->definedBy, true) ?>
<?php if(!empty($event->since)): ?>
(available since version <?= $event->since ?>)
<?php endif; ?>
</td>
<td><?= $this->context->typeLink($event->definedBy) ?></td>
<td><?= $renderer->createTypeLink($event->definedBy) ?></td>
</tr>
<?php endforeach; ?>
</table>
......
......@@ -8,8 +8,11 @@ use yii\helpers\ArrayHelper;
/**
* @var ClassDoc|TraitDoc $type
* @var yii\web\View $this
* @var \yii\apidoc\templates\html\ApiRenderer $renderer
*/
$renderer = $this->context;
$methods = $type->getNativeMethods();
if (empty($methods)) {
return;
......@@ -33,27 +36,27 @@ ArrayHelper::multisort($methods, 'name');
<table class="summaryTable table table-striped table-bordered table-hover">
<tr><td colspan="3">
<div class="signature2"><?= $this->context->renderMethodSignature($method) ?></div>
<div class="signature2"><?= $renderer->renderMethodSignature($method) ?></div>
</td></tr>
<?php if(!empty($method->params) || !empty($method->return) || !empty($method->exceptions)): ?>
<?php foreach($method->params as $param): ?>
<tr>
<td class="paramNameCol"><?= $param->name ?></td>
<td class="paramTypeCol"><?= $this->context->typeLink($param->types) ?></td>
<td class="paramTypeCol"><?= $renderer->createTypeLink($param->types) ?></td>
<td class="paramDescCol"><?= ApiMarkdown::process($param->description, $type) ?></td>
</tr>
<?php endforeach; ?>
<?php if(!empty($method->return)): ?>
<tr>
<td class="paramNameCol"><?= 'return'; ?></td>
<td class="paramTypeCol"><?= $this->context->typeLink($method->returnTypes); ?></td>
<td class="paramTypeCol"><?= $renderer->createTypeLink($method->returnTypes); ?></td>
<td class="paramDescCol"><?= ApiMarkdown::process($method->return, $type); ?></td>
</tr>
<?php endif; ?>
<?php foreach($method->exceptions as $exception => $description): ?>
<tr>
<td class="paramNameCol"><?= 'throws' ?></td>
<td class="paramTypeCol"><?= $this->context->typeLink($exception) ?></td>
<td class="paramTypeCol"><?= $renderer->createTypeLink($exception) ?></td>
<td class="paramDescCol"><?= ApiMarkdown::process($description, $type) ?></td>
</tr>
<?php endforeach; ?>
......
......@@ -10,8 +10,11 @@ use yii\helpers\ArrayHelper;
* @var ClassDoc|InterfaceDoc|TraitDoc $type
* @var boolean $protected
* @var yii\web\View $this
* @var \yii\apidoc\templates\html\ApiRenderer $renderer
*/
$renderer = $this->context;
if ($protected && count($type->getProtectedMethods()) == 0 || !$protected && count($type->getPublicMethods()) == 0) {
return;
} ?>
......@@ -36,9 +39,9 @@ ArrayHelper::multisort($methods, 'name');
foreach($methods as $method): ?>
<?php if($protected && $method->visibility == 'protected' || !$protected && $method->visibility != 'protected'): ?>
<tr<?= $method->definedBy != $type->name ? ' class="inherited"' : '' ?> id="<?= $method->name ?>()">
<td><?= $this->context->subjectLink($method, $method->name.'()') ?></td>
<td><?= $renderer->createSubjectLink($method, $method->name.'()') ?></td>
<td><?= ApiMarkdown::process($method->shortDescription, $method->definedBy, true) ?></td>
<td><?= $this->context->typeLink($method->definedBy, $type) ?></td>
<td><?= $renderer->createTypeLink($method->definedBy, $type) ?></td>
</tr>
<?php endif; ?>
<?php endforeach; ?>
......
......@@ -8,8 +8,11 @@ use yii\helpers\ArrayHelper;
/**
* @var ClassDoc|TraitDoc $type
* @var yii\web\View $this
* @var \yii\apidoc\templates\html\ApiRenderer $renderer
*/
$renderer = $this->context;
$properties = $type->getNativeProperties();
if (empty($properties)) {
return;
......@@ -33,7 +36,7 @@ ArrayHelper::multisort($properties, 'name');
</span>
</div>
<div class="signature"><?= $this->context->renderPropertySignature($property) ?></div>
<div class="signature"><?php echo $renderer->renderPropertySignature($property); ?></div>
<?= ApiMarkdown::process($property->description, $type) ?>
......
......@@ -9,8 +9,11 @@ use yii\helpers\ArrayHelper;
* @var ClassDoc|TraitDoc $type
* @var boolean $protected
* @var yii\web\View $this
* @var \yii\apidoc\templates\html\ApiRenderer $renderer
*/
$renderer = $this->context;
if ($protected && count($type->getProtectedProperties()) == 0 || !$protected && count($type->getPublicProperties()) == 0) {
return;
} ?>
......@@ -36,10 +39,10 @@ ArrayHelper::multisort($properties, 'name');
foreach($properties as $property): ?>
<?php if($protected && $property->visibility == 'protected' || !$protected && $property->visibility != 'protected'): ?>
<tr<?= $property->definedBy != $type->name ? ' class="inherited"' : '' ?> id="<?= $property->name ?>">
<td><?= $this->context->subjectLink($property) ?></td>
<td><?= $this->context->typeLink($property->types) ?></td>
<td><?= $renderer->createSubjectLink($property) ?></td>
<td><?= $renderer->createTypeLink($property->types) ?></td>
<td><?= ApiMarkdown::process($property->shortDescription, $property->definedBy, true) ?></td>
<td><?= $this->context->typeLink($property->definedBy) ?></td>
<td><?= $renderer->createTypeLink($property->definedBy) ?></td>
</tr>
<?php endif; ?>
<?php endforeach; ?>
......
......@@ -7,7 +7,7 @@ use yii\apidoc\models\TraitDoc;
/**
* @var ClassDoc|InterfaceDoc|TraitDoc $type
* @var yii\web\View $this
* @var \yii\apidoc\templates\html\Renderer $renderer
* @var \yii\apidoc\templates\html\ApiRenderer $renderer
*/
$renderer = $this->context;
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\templates\offline\assets;
use yii\web\View;
/**
* The asset bundle for the offline template.
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class AssetBundle extends \yii\web\AssetBundle
{
public $sourcePath = '@yii/apidoc/templates/offline/assets/css';
public $css = [
'api.css',
'style.css',
];
public $depends = [
'yii\web\JqueryAsset',
];
public $jsOptions = [
'position' => View::POS_HEAD,
];
}
pre {
color: #000000;
background-color: #FFF5E6;
font-family: "courier new", "times new roman", monospace;
line-height: 1.3em;
/* Put a nice border around it. */
padding: 1px;
width: 90%;
/* Don't wrap its contents, and show scrollbars. */
/* white-space: nowrap;*/
overflow: auto;
/* Stop after about 24 lines, and just show a scrollbar. */
/* max-height: 24em; */
margin: 5px;
padding-left: 20px;
border: 1px solid #FFE6BF;
border-left: 6px solid #FFE6BF;
}
code {
color: #000000;
background-color: #FFF5E6;
padding: 1px;
}
div.code {
display: none;
color: #000000;
background-color: #FFF5E6;
font-family: "courier new", "times new roman", monospace;
line-height: 1.3em;
/* Put a nice border around it. */
padding: 1px;
width: 90%;
/* Don't wrap its contents, and show scrollbars. */
/* white-space: nowrap;*/
overflow: auto;
/* Stop after about 24 lines, and just show a scrollbar. */
/* max-height: 24em; */
margin: 5px;
padding-left: 20px;
border-left: 6px solid #FFE6BF;
}
table.summaryTable {
background: #E6ECFF;
border-collapse: collapse;
width: 100%;
}
table.summaryTable th, table.summaryTable td {
border: 1px #BFCFFF solid;
padding: 0.2em;
}
table.summaryTable th {
background: #CCD9FF;
text-align: left;
}
#nav {
padding: 3px;
margin: 0 0 10px 0;
border-top: 1px #BFCFFF solid;
}
#classDescription {
padding: 5px;
margin: 10px 0 20px 0;
border-bottom: 1px solid #BFCFFF;
}
.detailHeader {
font-weight: bold;
font-size: 12pt;
margin: 30px 0 5px 0;
border-bottom: 1px solid #BFCFFF;
}
.detailHeaderTag {
font-weight: normal;
font-size: 10pt;
}
.signature, .signature2 {
padding: 3px;
color: #000000;
font-family: "courier new", "times new roman", monospace;
line-height: 1.3em;
}
.signature {
margin: 10px 0 10px 0;
background: #E6ECFF;
border: 1px #BFCFFF solid;
}
.paramNameCol {
width: 12%;
font-weight: bold;
}
.paramTypeCol {
width: 12%;
}
.sourceCode {
margin: 5px 0;
padding:5px;
background:#FFF5E6;
}
\ No newline at end of file
body
{
}
body, div, span, p, input
{
font-family: Verdana, Arial, sans-serif;
font-size: 10pt;
color: #333333;
}
#apiPage {
}
#apiHeader {
padding: 3px;
color: white;
background: #6078BF;
margin-bottom: 5px;
font-weight: bold;
}
#apiHeader a {
color: white;
}
#apiFooter {
margin-top: 5px;
padding: 3px;
border-top: 1px solid #BFCFFF;
text-align: center;
}
<?php
use yii\apidoc\models\ClassDoc;
use yii\apidoc\models\InterfaceDoc;
use yii\apidoc\models\TraitDoc;
/**
* @var ClassDoc[]|InterfaceDoc[]|TraitDoc[] $types
* @var yii\web\View $this
*/
?><h1>Class Reference</h1>
<table class="summaryTable docIndex">
<colgroup>
<col class="col-package" />
<col class="col-class" />
<col class="col-description" />
</colgroup>
<tr>
<th>Class</th>
<th>Description</th>
</tr>
<?php
ksort($types);
foreach($types as $i=>$class):
?>
<tr>
<td><?= $this->context->typeLink($class, $class->name) ?></td>
<td><?= \yii\apidoc\helpers\Markdown::process($class->shortDescription, $class) ?></td>
</tr>
<?php endforeach; ?>
</table>
<?php
/**
* @var yii\web\View $this
*/
\yii\apidoc\templates\offline\assets\AssetBundle::register($this);
$this->beginPage();
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="language" content="en" />
<?php $this->head(); ?>
<title><?php echo $this->context->pageTitle; ?></title>
</head>
<body>
<?php $this->beginBody(); ?>
<div id="apiPage">
<div id="apiHeader">
<a href="http://www.yiiframework.com">Yii Framework</a> v<?php echo Yii::getVersion(); ?> Class Reference
</div><!-- end of header -->
<div id="content">
<?php echo $content; ?>
</div><!-- end of content -->
<div id="apiFooter">
<p>&copy; 2008-2013 by <a href="http://www.yiisoft.com">Yii Software LLC</a></p>
<p>All Rights Reserved.</p>
</div><!-- end of footer -->
<script type="text/javascript">
/*<![CDATA[*/
$("a.toggle").on('click', function() {
var $this = $(this);
if ($this.hasClass('properties-hidden')) {
$this.text($this.text().replace(/Show/,'Hide'));
$this.parents(".summary").find(".inherited").show();
$this.removeClass('properties-hidden');
} else {
$this.text($this.text().replace(/Hide/,'Show'));
$this.parents(".summary").find(".inherited").hide();
$this.addClass('properties-hidden');
}
return false;
});
/*
$(".sourceCode a.show").toggle(function(){
$(this).text($(this).text().replace(/show/,'hide'));
$(this).parents(".sourceCode").find("div.code").show();
},function(){
$(this).text($(this).text().replace(/hide/,'show'));
$(this).parents(".sourceCode").find("div.code").hide();
});
$("a.sourceLink").click(function(){
$(this).attr('target','_blank');
});
*/
/*]]>*/
</script>
</div><!-- end of page -->
<?php $this->endBody(); ?>
</body>
</html>
<?php $this->endPage(); ?>
......@@ -17,24 +17,23 @@ use yii\helpers\Console;
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class Renderer extends \yii\apidoc\templates\html\Renderer
class ApiRenderer extends \yii\apidoc\templates\html\ApiRenderer
{
public $apiLayout = false;
public $layout = false;
public $indexView = '@yii/apidoc/templates/online/views/index.php';
public $pageTitle = 'Yii Framework 2.0 API Documentation';
/**
* Renders a given [[Context]].
*
* @param Context $context the api documentation context to render.
* @param Controller $controller the apidoc controller instance. Can be used to control output.
* @inheritdoc
*/
public function renderApi($context, $controller)
public function render($context, $targetDir)
{
parent::renderApi($context, $controller);
$dir = Yii::getAlias($this->targetDir);
$controller->stdout("writing packages file...");
parent::render($context, $targetDir);
if ($this->controller !== null) {
$this->controller->stdout("writing packages file...");
}
$packages = [];
$notNamespaced = [];
foreach(array_merge($context->classes, $context->interfaces, $context->traits) as $type) {
......@@ -50,17 +49,19 @@ class Renderer extends \yii\apidoc\templates\html\Renderer
foreach($packages as $name => $classes) {
sort($packages[$name]);
}
file_put_contents($dir . '/packages.txt', serialize($packages));
$controller->stdout('done.' . PHP_EOL, Console::FG_GREEN);
file_put_contents($targetDir . '/packages.txt', serialize($packages));
if ($this->controller !== null) {
$this->controller->stdout('done.' . PHP_EOL, Console::FG_GREEN);
}
}
public function generateUrl($typeName)
public function generateApiUrl($typeName)
{
return strtolower(str_replace('\\', '-', $typeName));
}
protected function generateFileName($typeName)
{
return $this->generateUrl($typeName) . '.html';
return $this->generateApiUrl($typeName) . '.html';
}
}
\ No newline at end of file
The `online` API doc template
=============================
This template is used to generate the Yii framework API docs for yiiframework.com.
\ No newline at end of file
......@@ -12,7 +12,7 @@ use yii\console\Exception;
use yii\console\Controller;
/**
* This command allows you to combine and compress your JavaScript and CSS files.
* Allows you to combine and compress your JavaScript and CSS files.
*
* Usage:
* 1. Create a configuration file using 'template' action:
......
......@@ -13,7 +13,7 @@ use yii\console\Exception;
use yii\caching\Cache;
/**
* This command allows you to flush cache.
* Allows you to flush cache.
*
* @author Alexander Makarov <sam@rmcreative.ru>
* @since 2.0
......
......@@ -15,7 +15,7 @@ use yii\helpers\FileHelper;
use yii\test\FixtureTrait;
/**
* This command manages loading and unloading fixtures.
* Manages loading and unloading fixtures.
*
* ~~~
* #load fixtures from UsersFixture class with default namespace "tests\unit\fixtures"
......
......@@ -16,7 +16,7 @@ use yii\helpers\Console;
use yii\helpers\Inflector;
/**
* This command provides help information about console commands.
* Provides help information about console commands.
*
* This command displays the available command list in
* the application or the detailed instructions about using
......@@ -82,6 +82,32 @@ class HelpController extends Controller
}
/**
* Returns an array of commands an their descriptions.
* @return array all available commands as keys and their description as values.
*/
protected function getCommandDescriptions()
{
$descriptions = [];
foreach ($this->getCommands() as $command) {
$description = '';
$result = Yii::$app->createController($command);
if ($result !== false) {
list($controller, $actionID) = $result;
$class = new \ReflectionClass($controller);
$docLines = preg_split('~(\n|\r|\r\n)~', $class->getDocComment());
if (isset($docLines[1])) {
$description = trim($docLines[1], ' *');
}
}
$descriptions[$command] = $description;
}
return $descriptions;
}
/**
* Returns all available actions of the specified controller.
* @param Controller $controller the controller instance
* @return array all available action IDs.
......@@ -141,11 +167,19 @@ class HelpController extends Controller
*/
protected function getHelp()
{
$commands = $this->getCommands();
$commands = $this->getCommandDescriptions();
if (!empty($commands)) {
$this->stdout("\nThe following commands are available:\n\n", Console::BOLD);
foreach ($commands as $command) {
echo "- " . $this->ansiFormat($command, Console::FG_YELLOW) . "\n";
$len = 0;
foreach ($commands as $command => $description) {
if (($l = strlen($command)) > $len) {
$len = $l;
}
}
foreach ($commands as $command => $description) {
echo "- " . $this->ansiFormat($command, Console::FG_YELLOW);
echo str_repeat(' ', $len + 3 - strlen($command)) . $description;
echo "\n";
}
$scriptName = $this->getScriptName();
$this->stdout("\nTo see the help of each command, enter:\n", Console::BOLD);
......
......@@ -14,7 +14,8 @@ use yii\console\Exception;
use yii\helpers\FileHelper;
/**
* This command extracts messages to be translated from source files.
* Extracts messages to be translated from source files.
*
* The extracted messages can be saved the following depending on `format`
* setting in config file:
*
......
......@@ -17,7 +17,7 @@ use yii\helpers\ArrayHelper;
use yii\helpers\FileHelper;
/**
* This command manages application migrations.
* Manages application migrations.
*
* A migration means a set of persistent changes to the application environment
* that is shared among different developers. For example, in an application
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment