diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml
index 84c0f3e..09e450e 100644
--- a/.github/workflows/tests.yaml
+++ b/.github/workflows/tests.yaml
@@ -14,36 +14,19 @@ jobs:
max-parallel: 4
matrix:
operatingSystem: [ubuntu-latest]
- phpVersion: ['8.1', '8.2', '8.3']
+ phpVersion: ['8.2', '8.3', '8.4']
fail-fast: false
- env:
- extensions: curl, fileinfo, gd, mbstring, openssl, pdo, pdo_sqlite, sqlite3, xml, zip
concurrency:
- group: ${{ github.ref }}-${{ github.workflow }}-${{ matrix.operatingSystem }}-${{ matrix.phpVersion }}
+ group: tests-${{ github.ref }}-${{ github.workflow }}-${{ matrix.operatingSystem }}-${{ matrix.phpVersion }}
cancel-in-progress: true
steps:
- - name: Checkout Winter CMS
- uses: actions/checkout@v4
- with:
- repository: wintercms/winter
- ref: develop
-
- - name: Checkout Winter Docs plugin
- uses: actions/checkout@v4
- with:
- path: plugins/winter/docs
-
- - name: Install PHP
- uses: shivammathur/setup-php@v2
+ - name: Setup Winter CMS
+ uses: wintercms/setup-winter-action@v1
with:
php-version: ${{ matrix.phpVersion }}
- tools: composer:v2
- extensions: ${{ env.extensions }}
-
- - name: Install Composer dependencies
- run: |
- sed -i 's|plugins/myauthor/\*/composer.json|plugins/*/*/composer.json|g' composer.json
- composer install --no-interaction --no-progress --no-scripts
+ winter-ref: wip/1.3
+ plugin-author: winter
+ plugin-name: docs
- name: Run tests
run: php artisan winter:test -p Winter.Docs
diff --git a/.gitignore b/.gitignore
index 2526679..392937a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
composer.lock
-vendor
+vendor/
.phpunit.result.cache
+.phpunit.cache/
coverage.xml
.DS_Store
diff --git a/classes/BaseDocumentation.php b/classes/BaseDocumentation.php
index 1e94753..c0e715d 100644
--- a/classes/BaseDocumentation.php
+++ b/classes/BaseDocumentation.php
@@ -345,7 +345,16 @@ public function extract(): void
}
}
- $zip->extractTo($this->getDownloadPath('extracted'), $toExtract);
+ if (!$zip->extractTo($this->getDownloadPath('extracted'), $toExtract)) {
+ throw new ApplicationException(
+ sprintf(
+ 'Could not extract the documentation for "%s" from the remote source "%s" - %s',
+ $this->identifier,
+ $this->source,
+ $zip->getStatusString()
+ )
+ );
+ }
// Move remaining files into location
$extractPath = $this->getDownloadPath('extracted/' . $this->zipFolder);
diff --git a/classes/PHPApiDocumentation.php b/classes/PHPApiDocumentation.php
index 360734d..249fb9e 100644
--- a/classes/PHPApiDocumentation.php
+++ b/classes/PHPApiDocumentation.php
@@ -2,10 +2,10 @@
namespace Winter\Docs\Classes;
-use File;
use Illuminate\Support\Facades\App;
use Twig\TemplateWrapper;
use Winter\Storm\Exception\ApplicationException;
+use Winter\Storm\Support\Facades\File;
/**
* PHP API Documentation instance.
@@ -32,6 +32,11 @@ class PHPApiDocumentation extends BaseDocumentation
*/
protected string $template;
+ /**
+ * Path to the Twig template for rendering event API docs.
+ */
+ protected string $eventTemplate;
+
/**
* Prepared template for rendering API docs.
*/
@@ -167,12 +172,24 @@ protected function processClassLevel(PHPApiParser $parser, array $classMap, arra
'title' => $key,
];
+ try {
+ $rendered = $this->preparedTemplate->render([
+ 'class' => $class,
+ ]);
+ } catch (\Throwable $e) {
+ throw new ApplicationException(
+ sprintf(
+ 'An error occurred while rendering the API documentation for class "%s": %s',
+ $class['name'],
+ $e->getMessage()
+ )
+ );
+ }
+
// Create docs
$this->getStorageDisk()->put(
$this->getProcessedPath(ltrim($baseNamespace . '/' . $key . '.htm')),
- $this->prependFrontMatter($class, $this->preparedTemplate->render([
- 'class' => $class,
- ]))
+ $this->prependFrontMatter($class, $rendered)
);
$nav[] = $navItem;
diff --git a/classes/PHPApiParser.php b/classes/PHPApiParser.php
index a4f34d8..c4272b8 100644
--- a/classes/PHPApiParser.php
+++ b/classes/PHPApiParser.php
@@ -104,7 +104,7 @@ public function __construct(string $basePath, array|string $sourcePaths = [], ar
public function parse(): void
{
// Create parser and node finder
- $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
+ $parser = (new ParserFactory)->createForHostVersion();
$nodeFinder = new NodeFinder;
// Add name resolver
diff --git a/composer.json b/composer.json
index a403e09..fba9e1e 100644
--- a/composer.json
+++ b/composer.json
@@ -25,8 +25,8 @@
"require": {
"php": ">=7.2.9",
"composer/installers": "~1.0",
- "nikic/php-parser": "^4.11.0",
- "phpdocumentor/reflection-docblock": "^5.2.2"
+ "nikic/php-parser": "^5.6.0",
+ "phpdocumentor/reflection-docblock": "^5.6.2"
},
"extra": {
"installer-name": "docs",
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index f06746c..dedebc4 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -1,25 +1,22 @@
-
-
- ./tests
-
-
-
-
-
- classes
- Plugin.php
-
-
+
+
+ ./tests
+
+
+
+
+ classes
+ Plugin.php
+
+
diff --git a/tests/classes/ApiParserTest.php b/tests/classes/ApiParserTest.php
index 82c27a8..675cd82 100644
--- a/tests/classes/ApiParserTest.php
+++ b/tests/classes/ApiParserTest.php
@@ -1,12 +1,15 @@
-assertCount(7, $this->apiParser->getPaths());
@@ -44,10 +44,7 @@ public function testGetPaths()
], $filenames);
}
- /**
- * @covers \Winter\Docs\Classes\PHPApiParser::parse()
- * @testdox can parse all PHP files and present the schema in an array.
- */
+ #[TestDox('can parse all PHP files and present the schema in an array..')]
public function testParse()
{
$this->apiParser->parse();
diff --git a/tests/classes/BaseDocumentationTest.php b/tests/classes/BaseDocumentationTest.php
index 2a452f4..2c89e3d 100644
--- a/tests/classes/BaseDocumentationTest.php
+++ b/tests/classes/BaseDocumentationTest.php
@@ -2,124 +2,73 @@
namespace Winter\Docs\Tests\Classes;
+use PHPUnit\Framework\Attributes\CoversClass;
+use PHPUnit\Framework\Attributes\TestDox;
use System\Tests\Bootstrap\TestCase;
+use Winter\Docs\Classes\BaseDocumentation;
use Winter\Storm\Exception\ApplicationException;
-/**
- * @covers \Winter\Docs\Classes\BaseDocumentation
- * @testdox The Base Documentation abstract (\Winter\Docs\Classes\BaseDocumentation)
- */
+#[CoversClass(\Winter\Docs\Classes\BaseDocumentation::class)]
+#[TestDox('The Base Documentation abstract (\Winter\Docs\Classes\BaseDocumentation)')]
class BaseDocumentationTest extends TestCase
{
- /**
- * @covers \Winter\Docs\Classes\BaseDocumentation::download()
- * @covers \Winter\Docs\Classes\BaseDocumentation::isDownloaded()
- * @testdox can download a remote documentation ZIP file and indicate that it is downloaded.
- */
- public function testDownload(): void
+ #[TestDox('can download, extract a downloaded docs ZIP file and clean-up afterwards.')]
+ public function testDownloadExtractAndCleanUp(): void
{
- $doc = $this->getMockForAbstractClass(
- 'Winter\Docs\Classes\BaseDocumentation',
- [
+ $doc = $this->getMockBuilder(BaseDocumentation::class)
+ ->setConstructorArgs([
'Winter.Docs.Test',
[
'name' => 'Winter Docs Test',
- 'type' => 'user',
+ 'type' => 'md',
'source' => 'remote',
- 'url' => 'https://github.com/wintercms/docs/archive/refs/heads/main.zip',
- 'zipFolder' => 'docs-main',
- ]
- ]
- );
+ 'url' => 'https://github.com/wintercms/docs/archive/refs/heads/develop.zip',
+ 'zipFolder' => 'docs-develop',
+ ],
+ ])
+ ->onlyMethods(['process', 'getPageList'])
+ ->getMock();
$doc->download();
+
$this->assertFileExists($doc->getDownloadPath('archive.zip'));
$this->assertTrue($doc->isDownloaded());
- }
- /**
- * @covers \Winter\Docs\Classes\BaseDocumentation::download()
- * @testdox will throw an exception if the documentation URL is invalid when downloading.
- */
- public function testDownloadInvalidUrl(): void
- {
- $this->expectException(ApplicationException::class);
- $this->expectExceptionMessageMatches('/Could not retrieve the documentation/i');
+ $doc->extract();
- $doc = $this->getMockForAbstractClass(
- 'Winter\Docs\Classes\BaseDocumentation',
- [
- 'Winter.Docs.Test',
- [
- 'name' => 'Winter Docs Test',
- 'type' => 'md',
- 'source' => 'remote',
- 'url' => 'https://wintercms.com/missing/docs.zip',
- 'zipFolder' => 'docs-main',
- ]
- ]
- );
+ $this->assertDirectoryExists($doc->getDownloadPath('collated'));
+ $this->assertFileExists($doc->getDownloadPath('collated/snowboard/introduction.md'));
+ // Re-download the file
$doc->download();
- }
- /**
- * @covers \Winter\Docs\Classes\BaseDocumentation::extract()
- * @testdox can extract a downloaded docs ZIP file.
- */
- public function testExtract(): void
- {
- $doc = $this->getMockForAbstractClass(
- 'Winter\Docs\Classes\BaseDocumentation',
- [
- 'Winter.Docs.Test',
- [
- 'name' => 'Winter Docs Test',
- 'type' => 'md',
- 'source' => 'remote',
- 'url' => 'https://github.com/wintercms/docs/archive/refs/heads/develop.zip',
- 'zipFolder' => 'docs-develop',
- ]
- ]
- );
-
- $doc->download();
- $doc->extract();
+ // Clean up
+ $doc->cleanupDownload();
- $this->assertDirectoryExists($doc->getDownloadPath('collated'));
- $this->assertFileExists($doc->getDownloadPath('collated/snowboard/introduction.md'));
+ $this->assertFileDoesNotExist($doc->getDownloadPath('archive.zip'));
+ $this->assertDirectoryDoesNotExist($doc->getDownloadPath('extracted'));
}
-/**
- * @covers \Winter\Docs\Classes\BaseDocumentation::cleanupDownload()
- * @testdox can clean up downloaded and extracted assets.
- */
- public function testCleanupDownload(): void
+ #[TestDox('will throw an exception if the documentation URL is invalid when downloading.')]
+ public function testDownloadInvalidUrl(): void
{
- $doc = $this->getMockForAbstractClass(
- 'Winter\Docs\Classes\BaseDocumentation',
- [
+ $this->expectException(ApplicationException::class);
+ $this->expectExceptionMessageMatches('/Could not retrieve the documentation/i');
+
+ $doc = $this->getMockBuilder(BaseDocumentation::class)
+ ->setConstructorArgs([
'Winter.Docs.Test',
[
'name' => 'Winter Docs Test',
'type' => 'md',
'source' => 'remote',
- 'url' => 'https://github.com/wintercms/docs/archive/refs/heads/develop.zip',
- 'zipFolder' => 'docs-develop',
- ]
- ]
- );
-
- $doc->download();
- $doc->extract();
+ 'url' => 'https://wintercms.com/missing/docs.zip',
+ 'zipFolder' => 'docs-main',
+ ],
+ ])
+ ->onlyMethods(['process', 'getPageList'])
+ ->getMock();
- // Re-download the file
$doc->download();
-
- // Clean up
- $doc->cleanupDownload();
-
- $this->assertFileDoesNotExist($doc->getDownloadPath('archive.zip'));
- $this->assertDirectoryDoesNotExist($doc->getDownloadPath('extracted'));
}
}
diff --git a/tests/classes/DocsManagerTest.php b/tests/classes/DocsManagerTest.php
index 5881168..981bba4 100644
--- a/tests/classes/DocsManagerTest.php
+++ b/tests/classes/DocsManagerTest.php
@@ -2,14 +2,15 @@
namespace Winter\Docs\Tests\Classes;
+use PHPUnit\Framework\Attributes\CoversClass;
+use PHPUnit\Framework\Attributes\TestDox;
use System\Tests\Bootstrap\PluginTestCase;
use System\Classes\PluginManager;
use Winter\Docs\Classes\DocsManager;
-/**
- * @covers \Winter\Docs\Classes\DocsManager
- * @testdox The Documentation Manager (\Winter\Docs\Classes\DocsManager)
- */
+
+#[CoversClass(\Winter\Docs\Classes\DocsManager::class)]
+#[TestDox('The Documentation Manager (\Winter\Docs\Classes\DocsManager)')]
class DocsManagerTest extends PluginTestCase
{
protected $docsManager;
@@ -33,10 +34,7 @@ public function setUp(): void
$this->docsManager = DocsManager::instance();
}
- /**
- * @covers \Winter\Docs\Classes\DocsManager::makeIdentifier()
- * @testdox can make valid identifiers for docs.
- */
+ #[TestDox('can make valid identifiers for docs.')]
public function testMakeIdentifier()
{
$this->assertEquals(
@@ -60,12 +58,7 @@ public function testMakeIdentifier()
);
}
- /**
- * @covers \Winter\Docs\Classes\DocsManager::addDocumentation()
- * @covers \Winter\Docs\Classes\DocsManager::removeDocumentation()
- * @covers \Winter\Docs\Classes\DocsManager::hasDocumentation()
- * @testdox can manually add and remove documentation.
- */
+ #[TestDox('can manually add and remove documentation.')]
public function testAddDocumentation()
{
$this->assertFalse($this->docsManager->hasDocumentation('Docs.Test', 'user'));
diff --git a/updates/version.yaml b/updates/version.yaml
index 83bf9d3..d577554 100644
--- a/updates/version.yaml
+++ b/updates/version.yaml
@@ -1,2 +1,3 @@
"1.0.0": Initial version of the Winter Docs plugin.
"1.0.1": Allow processing of documentation through job queue.
+"2.0.0": "Support Winter v1.3"
diff --git a/views/api-doc.twig b/views/api-doc.twig
index 12eebf2..abb0912 100644
--- a/views/api-doc.twig
+++ b/views/api-doc.twig
@@ -1,4 +1,18 @@
{% endif %}
+ {% if class.extendedBy %}
+
+
+ Extended by
+
+
+ {% endif %}
+ {% if class.implementedBy %}
+
+
+ Implemented by
+
+
+ {% endif %}
+ {% if class.usedBy %}
+
+
+ Used by
+
+
+ {% endif %}
-
+
{{ class.name }}
-
-
-
- {% if class.final %}
-
- final
-
- {% else %}
-
- abstract
-
- {% endif %}
-
-
- class
-
-
-
- {{ class.name }}
-
-
- {% if class.extends %}
-
- extends
-
-
-
- {{ class.extends }}
-
- {% endif %}
-
+
{% if class.docs.summary %}
@@ -113,8 +132,487 @@
{% endif %}
+
+ {%- if class.final -%}
+ final
+ {%- elseif class.abstract -%}
+ abstract
+ {%- endif %} {{ class.type }} {{ class.namespace }}\{{ class.name -}}
+
+ {%- if class.extends.class %}{{ "\n" }}extends {{class.extends.class }}{% endif -%}
+
+ {%- if class.implements %}{{ "\n" }}implements {% endif -%}
+ {%- for interface in class.implements %}{{ "\n" }} {{ interface.class }}{% if not loop.last %},{% endif%}{% endfor -%}
+
+
{% if class.docs.body %}
{{ class.docs.body | raw }}
{% endif %}
+
+{% if class.extends %}
+
+ #
+ Extends
+
+
+
+{% endif %}
+
+{% if class.traits %}
+
+ #
+ Traits
+
+
+
+
+
+ | Trait |
+ Description |
+
+
+
+ {% for trait in class.traits %}
+
+
+
+ {{ trait.name }}
+
+ |
+
+ {{ trait.summary | raw }}
+ |
+
+ {% endfor %}
+
+
+{% endif %}
+
+{% if class.constants %}
+
+ #
+ Constants
+
+
+
+
+
+ | Constant |
+ Type |
+ Value |
+ Description |
+
+
+
+ {% for constant in class.constants %}
+
+
+ {{ constant.name }}
+ |
+
+ {% if constant.type.definition == 'reference' and constant.type.type.linked %}
+
+ {{ constant.type.type.name }}
+
+ {% elseif constant.type.definition == 'reference' %}
+ {{ constant.type.type.name }}
+ {% else %}
+ {{ constant.type.type }}
+ {% endif %}
+ |
+
+ {{ constant.value }}
+ |
+
+ {{ constant.docs.summary | raw }}
+ |
+
+ {% endfor %}
+
+
+{% endif %}
+
+{% if class.properties %}
+
+ #
+ Properties
+
+
+ {% for property in class.properties %}
+
+ #
+
+ {% if property.inherited %}
+
+ inherited
+
+ {% endif %}
+
+
+ {{ property.visibility }}
+
+
+ {% if property.static %}
+
+ static
+
+ {% endif %}
+
+ ${{ property.name }}
+
+
+ :
+ {% if property.type.definition == 'union' %}
+ {% for type in property.type.types %}
+ {% if not loop.first %}
+ |
+ {% endif %}
+ {% if type.definition == 'reference' and type.type.linked %}
+
+ {{- type.type.name -}}
+
+ {% elseif type.definition == 'reference' %}
+ {{ type.type.name }}
+ {% else %}
+ {{ type.type }}
+ {% endif %}
+ {% endfor %}
+ {% else %}
+ {% if property.type.definition == 'reference' and property.type.type.linked %}
+
+ {{- property.type.type.name -}}
+
+ {% elseif property.type.definition == 'reference' %}
+ {{ property.type.type.name }}
+ {% else %}
+ {{ property.type.type }}
+ {% endif %}
+ {% endif %}
+ {% if property.default %}
+
+ = {{ property.default }}
+
+ {% endif %}
+
+
+
+ {% if property.inherited %}
+
+ {% endif %}
+
+
+ {{ property.docs.summary | raw }}
+
+ {{ property.docs.body | raw }}
+
+ {% endfor %}
+{% endif %}
+
+{% if class.methods %}
+
+ #
+ Methods
+
+
+ {% for method in class.methods %}
+
+ #
+
+ {% if method.inherited %}
+
+ inherited
+
+ {% endif %}
+
+
+ {{ method.visibility }}
+
+
+ {% if method.static %}
+
+ static
+
+ {% endif %}
+ {% if method.final %}
+
+ final
+
+ {% endif %}
+
+ {{ method.name }}
+ (
+ {%- if method.params|length > 0 -%}
+
+ {%- for param in method.params -%}
+ {%- if param.type.definition == 'union' -%}
+ {%- for type in param.type.types -%}
+ {%- if not loop.first %} | {% endif -%}
+ {%- if type.definition == 'reference' and type.type.linked -%}
+
+ {{- type.type.name -}}
+
+ {%- elseif type.definition == 'reference'-%}
+ {{ type.type.name }}
+ {%- else -%}
+ {{ type.type }}
+ {%- endif -%}
+ {%- endfor -%}
+ {%- else -%}
+ {%- if param.type.definition == 'reference' and param.type.type.linked -%}
+
+ {{- param.type.type.name -}}
+
+ {%- elseif param.type.definition == 'reference' -%}
+ {{ param.type.type.name }}
+ {%- elseif param.type.definition == 'scalar' and param.type.type != 'mixed' -%}
+ {{ param.type.type }}
+ {%- endif -%}
+ {%- endif -%}
+ {%- if param.type.definition != 'scalar' or param.type.type != 'mixed' %} {% endif -%}
+ ${{ param.name }}
+
+ {%- if param.default %} = {{ param.default }}{% endif -%}
+ {%- if not loop.last %}, {% endif -%}
+ {%- endfor -%}
+
+ {%- endif -%}
+ )
+
+ {% if method.returns.type.definition != 'scalar' or method.returns.type.type != 'mixed' %}
+ : {%- if method.returns.type.definition == 'union' -%}
+ {%- for type in method.returns.type.types -%}
+ {%- if not loop.first %} | {% endif -%}
+ {%- if type.definition == 'reference' and type.type.linked -%}
+
+ {{- type.type.name -}}
+
+ {%- elseif type.definition == 'reference'-%}
+ {{ type.type.name }}
+ {%- else -%}
+ {{ type.type }}
+ {%- endif -%}
+ {%- endfor -%}
+ {%- else -%}
+ {%- if method.returns.type.definition == 'reference' and method.returns.type.type.linked -%}
+
+ {{- method.returns.type.type.name -}}
+
+ {%- elseif method.returns.type.definition == 'reference' -%}
+ {{ method.returns.type.type.name }}
+ {%- elseif method.returns.type.definition == 'scalar' -%}
+ {{ method.returns.type.type }}
+ {%- endif -%}
+ {%- endif -%}
+ {% endif %}
+
+
+ {% if method.inherited %}
+
+ {% endif %}
+
+
+ {{ method.docs.summary | raw }}
+
+ {{ method.docs.body | raw }}
+
+ {% if method.params|length > 0 %}
+
Parameters
+
+
+
+
+ | Property |
+ Type |
+ Description |
+
+
+
+ {% for param in method.params %}
+
+ |
+ ${{ param.name }}
+ |
+
+ {%- if param.type.definition == 'union' -%}
+ {%- for type in param.type.types -%}
+ {%- if not loop.first %} | {% endif -%}
+ {%- if type.definition == 'reference' and type.type.linked -%}
+
+ {{- type.type.name -}}
+
+ {%- elseif type.definition == 'reference'-%}
+ {{ type.type.name }}
+ {%- else -%}
+ {{ type.type }}
+ {%- endif -%}
+ {%- endfor -%}
+ {%- else -%}
+ {%- if param.type.definition == 'reference' and param.type.type.linked -%}
+
+ {{- param.type.type.name -}}
+
+ {%- elseif param.type.definition == 'reference' -%}
+ {{ param.type.type.name }}
+ {%- elseif param.type.definition == 'scalar' -%}
+ {{ param.type.type }}
+ {%- endif -%}
+ {%- endif -%}
+ |
+
+ {{ param.summary | raw }}
+ |
+
+ {% endfor %}
+
+
+ {% endif %}
+
+
Returns
+
+
+ {%- if method.returns.type.definition == 'union' -%}
+ {%- for type in method.returns.type.types -%}
+ {%- if not loop.first %} | {% endif -%}
+ {%- if type.definition == 'reference' and type.type.linked -%}
+
+ {{- type.type.name -}}
+
+ {%- elseif type.definition == 'reference'-%}
+ {{ type.type.name }}
+ {%- else -%}
+ {{ type.type }}
+ {%- endif -%}
+ {%- endfor -%}
+ {%- else -%}
+ {%- if method.returns.type.definition == 'reference' and method.returns.type.type.linked -%}
+
+ {{- method.returns.type.type.name -}}
+
+ {%- elseif method.returns.type.definition == 'reference' -%}
+ {{ method.returns.type.type.name }}
+ {%- elseif method.returns.type.definition == 'scalar' -%}
+ {{ method.returns.type.type }}
+ {%- endif -%}
+ {%- endif -%}
+
+
+ {{ method.docs.return.summary | raw }}
+
+
+ {% endfor %}
+{% endif %}
+
+{% if class.extendedBy %}
+
+ #
+ Extended by
+
+
+
+
+
+ | Class |
+ Description |
+
+
+
+ {% for extendedBy in class.extendedBy %}
+
+
+
+ {{ extendedBy.name }}
+
+ |
+ {{ extendedBy.summary | raw }} |
+
+ {% endfor %}
+
+
+{% endif %}
+
+{% if class.implementedBy %}
+
+ #
+ Implemented by
+
+
+
+
+
+ | Class |
+ Description |
+
+
+
+ {% for implementedBy in class.implementedBy %}
+
+
+
+ {{ implementedBy.name }}
+
+ |
+ {{ implementedBy.summary | raw }} |
+
+ {% endfor %}
+
+
+{% endif %}
+
+{% if class.usedBy %}
+
+ #
+ Used by
+
+
+
+
+
+ | Class |
+ Description |
+
+
+
+ {% for usedBy in class.usedBy %}
+
+
+
+ {{ usedBy.name }}
+
+ |
+ {{ usedBy.summary | raw }} |
+
+ {% endfor %}
+
+
+{% endif %}