From fa556facfb7e1a2365c0e3e660436b19d3c9e6fb Mon Sep 17 00:00:00 2001 From: d-skora <d.skora@clearcode.cc> Date: Fri, 3 Oct 2014 16:12:35 +0200 Subject: [PATCH] hide annotation with tests --- core/API/DocumentationGenerator.php | 117 ++++++++++- core/API/Proxy.php | 46 +++++ plugins/CoreAdminHome/API.php | 1 + plugins/CoreAdminHome/CoreAdminHome.php | 13 +- plugins/DBStats/API.php | 1 + plugins/DBStats/DBStats.php | 13 +- .../Core/API/DocumentationGeneratorTest.php | 195 ++++++++++++++++++ 7 files changed, 375 insertions(+), 11 deletions(-) create mode 100644 tests/PHPUnit/Core/API/DocumentationGeneratorTest.php diff --git a/core/API/DocumentationGenerator.php b/core/API/DocumentationGenerator.php index 63ae943433..96cad6393d 100644 --- a/core/API/DocumentationGenerator.php +++ b/core/API/DocumentationGenerator.php @@ -12,10 +12,10 @@ use Exception; use Piwik\Common; use Piwik\Piwik; use Piwik\Url; +use ReflectionClass; class DocumentationGenerator { - protected $modulesToHide = array('CoreAdminHome', 'DBStats'); protected $countPluginsLoaded = 0; /** @@ -49,21 +49,20 @@ class DocumentationGenerator } $str = $toc = ''; - $parametersToSet = array( - 'idSite' => Common::getRequestVar('idSite', 1, 'int'), - 'period' => Common::getRequestVar('period', 'day', 'string'), - 'date' => Common::getRequestVar('date', 'today', 'string') - ); foreach (Proxy::getInstance()->getMetadata() as $class => $info) { $moduleName = Proxy::getInstance()->getModuleNameFromClassName($class); + $rClass = new ReflectionClass($class); - if (in_array($moduleName, $this->modulesToHide)) { + if (!Piwik::hasUserSuperUserAccess() && $this->checkIfClassCommentContainsHideAnnotation($rClass)) { continue; } - $toc .= "<a href='#$moduleName'>$moduleName</a><br/>"; - $str .= $this->getInterfaceString($moduleName, $class, $info, $parametersToSet, $outputExampleUrls, $prefixUrls); + $toDisplay = $this->prepareModulesAndMethods($info, $moduleName); + foreach ($toDisplay as $moduleName => $methods) { + $toc .= $this->prepareModuleToDisplay($moduleName); + $str .= $this->prepareMethodToDisplay($moduleName, $info, $methods, $class, $outputExampleUrls, $prefixUrls); + } } $str = "<h2 id='topApiRef' name='topApiRef'>Quick access to APIs</h2> @@ -73,6 +72,106 @@ class DocumentationGenerator return $str; } + public function prepareModuleToDisplay($moduleName) + { + return "<a href='#$moduleName'>$moduleName</a><br/>"; + } + + public function prepareMethodToDisplay($moduleName, $info, $methods, $class, $outputExampleUrls, $prefixUrls) + { + $str = ''; + $str .= "\n<a name='$moduleName' id='$moduleName'></a><h2>Module " . $moduleName . "</h2>"; + $info['__documentation'] = $this->checkDocumentation($info['__documentation']); + $str .= "<div class='apiDescription'> " . $info['__documentation'] . " </div>"; + foreach ($methods as $methodName) { + $params = $this->getParametersString($class, $methodName); + + $str .= "\n <div class='apiMethod'>- <b>$moduleName.$methodName </b>" . $params . ""; + $str .= '<small>'; + if ($outputExampleUrls) { + $str .= $this->addExamples($class, $methodName, $prefixUrls); + } + $str .= '</small>'; + $str .= "</div>\n"; + } + + return $str; + } + + public function prepareModulesAndMethods($info, $moduleName) + { + $toDisplay = array(); + + foreach ($info as $methodName => $infoMethod) { + if ($methodName == '__documentation') { + continue; + } + $toDisplay[$moduleName][] = $methodName; + } + + return $toDisplay; + } + + public function addExamples($class, $methodName, $prefixUrls) + { + $token_auth = "&token_auth=" . Piwik::getCurrentUserTokenAuth(); + $parametersToSet = array( + 'idSite' => Common::getRequestVar('idSite', 1, 'int'), + 'period' => Common::getRequestVar('period', 'day', 'string'), + 'date' => Common::getRequestVar('date', 'today', 'string') + ); + $str = ''; +// used when we include this output in the Piwik official documentation for example + $str .= "<span class=\"example\">"; + $exampleUrl = $this->getExampleUrl($class, $methodName, $parametersToSet); + if ($exampleUrl !== false) { + $lastNUrls = ''; + if (preg_match('/(&period)|(&date)/', $exampleUrl)) { + $exampleUrlRss = $prefixUrls . $this->getExampleUrl($class, $methodName, array('date' => 'last10', 'period' => 'day') + $parametersToSet); + $lastNUrls = ", RSS of the last <a target=_blank href='$exampleUrlRss&format=rss$token_auth&translateColumnNames=1'>10 days</a>"; + } + $exampleUrl = $prefixUrls . $exampleUrl; + $str .= " [ Example in + <a target=_blank href='$exampleUrl&format=xml$token_auth'>XML</a>, + <a target=_blank href='$exampleUrl&format=JSON$token_auth'>Json</a>, + <a target=_blank href='$exampleUrl&format=Tsv$token_auth&translateColumnNames=1'>Tsv (Excel)</a> + $lastNUrls + ]"; + } else { + $str .= " [ No example available ]"; + } + $str .= "</span>"; + return $str; + } + + /** + * Check if Class contains @hide + * + * @param ReflectionClass $rClass instance of ReflectionMethod + * @return bool + */ + public function checkIfClassCommentContainsHideAnnotation(ReflectionClass $rClass) + { + if (strstr($rClass->getDocComment(), '@hide') === false) { + return false; + } + return true; + } + + /** + * Check if documentation contains @hide annotation and deletes it + * + * @param $moduleToCheck + * @return mixed + */ + public function checkDocumentation($moduleToCheck) + { + if (strpos($moduleToCheck, '@hide') == true) { + $moduleToCheck = str_replace(strtok(strstr($moduleToCheck, '@hide'), "\n"), "", $moduleToCheck); + } + return $moduleToCheck; + } + private function getInterfaceString($moduleName, $class, $info, $parametersToSet, $outputExampleUrls, $prefixUrls) { $str = ''; diff --git a/core/API/Proxy.php b/core/API/Proxy.php index daaa5c9f42..1a87352abe 100644 --- a/core/API/Proxy.php +++ b/core/API/Proxy.php @@ -361,6 +361,51 @@ class Proxy extends Singleton $this->metadataArray = array(); } + /** + * Check if method contains @hide + * + * @param ReflectionMethod $method instance of ReflectionMethod + * @return bool + */ + public function checkIfMethodContainsHideAnnotation($method) + { + $docComment = $method->getDocComment(); + $hideLine = strstr($docComment, '@hide'); + if($hideLine) { + $hideString = trim(str_replace("@hide", "", strtok($hideLine, "\n"))); + if($hideString) { + $response = false; + $hideArray = explode(" ", $hideString); + $hideString = $hideArray[0]; + /** + * Triggered to check if plugin should be hidden from the API for the current user. + * + * This event exists for checking if the user should be able to see the plugin API. + * If &$response is set to false then the user will be able to see the plugin API. + * If &$response is set to true then the plugin API will be hidden for the user. + * + * **Example** + * + * public function checkIfNotSuperUser(&$response) + * { + * try { + * Piwik::checkUserHasSuperUserAccess(); + * $response = false; + * } catch (\Exception $e) { + * $response = true; + * } + * } + * + * @param bool &$response Boolean value containing information + * if the plugin API should be hidden from the current user. + */ + Piwik::postEvent(sprintf('API.DocumentationGenerator.hide%s', $hideString), array(&$response)); + return $response; + } + } + return false; + } + /** * Returns an array containing the values of the parameters to pass to the method to call * @@ -433,6 +478,7 @@ class Proxy extends Singleton && $method->getName() != 'getInstance' && false === strstr($method->getDocComment(), '@deprecated') && (!$this->hideIgnoredFunctions || false === strstr($method->getDocComment(), '@ignore')) + && (Piwik::hasUserSuperUserAccess() || false === $this->checkIfMethodContainsHideAnnotation($method)) ) { $name = $method->getName(); $parameters = $method->getParameters(); diff --git a/plugins/CoreAdminHome/API.php b/plugins/CoreAdminHome/API.php index a25a522cb4..0586d0b3b1 100644 --- a/plugins/CoreAdminHome/API.php +++ b/plugins/CoreAdminHome/API.php @@ -22,6 +22,7 @@ use Piwik\Site; use Piwik\TaskScheduler; /** + * @hide ExceptForSuperUser * @method static \Piwik\Plugins\CoreAdminHome\API getInstance() */ class API extends \Piwik\Plugin\API diff --git a/plugins/CoreAdminHome/CoreAdminHome.php b/plugins/CoreAdminHome/CoreAdminHome.php index 86b5f26ec7..8057a9b93a 100644 --- a/plugins/CoreAdminHome/CoreAdminHome.php +++ b/plugins/CoreAdminHome/CoreAdminHome.php @@ -9,6 +9,7 @@ namespace Piwik\Plugins\CoreAdminHome; use Piwik\Db; +use Piwik\Piwik; use Piwik\Settings\UserSetting; /** @@ -24,7 +25,8 @@ class CoreAdminHome extends \Piwik\Plugin return array( 'AssetManager.getStylesheetFiles' => 'getStylesheetFiles', 'AssetManager.getJavaScriptFiles' => 'getJsFiles', - 'UsersManager.deleteUser' => 'cleanupUser' + 'UsersManager.deleteUser' => 'cleanupUser', + 'API.DocumentationGenerator.hideExceptForSuperUser' => 'checkIfNotSuperUser' ); } @@ -60,4 +62,13 @@ class CoreAdminHome extends \Piwik\Plugin $jsFiles[] = "plugins/CoreAdminHome/javascripts/pluginSettings.js"; } + public function checkIfNotSuperUser(&$response) + { + try { + Piwik::checkUserHasSuperUserAccess(); + $response = false; + } catch (\Exception $e) { + $response = true; + } + } } diff --git a/plugins/DBStats/API.php b/plugins/DBStats/API.php index 442c78752a..f6443b12da 100644 --- a/plugins/DBStats/API.php +++ b/plugins/DBStats/API.php @@ -13,6 +13,7 @@ use Piwik\DataTable; use Piwik\Piwik; /** + * @hide ExceptForSuperUser * @see plugins/DBStats/MySQLMetadataProvider.php */ require_once PIWIK_INCLUDE_PATH . '/plugins/DBStats/MySQLMetadataProvider.php'; diff --git a/plugins/DBStats/DBStats.php b/plugins/DBStats/DBStats.php index 1f5f9d7609..45269630b5 100644 --- a/plugins/DBStats/DBStats.php +++ b/plugins/DBStats/DBStats.php @@ -23,7 +23,8 @@ class DBStats extends \Piwik\Plugin { return array( 'AssetManager.getStylesheetFiles' => 'getStylesheetFiles', - "TestingEnvironment.addHooks" => 'setupTestEnvironment' + "TestingEnvironment.addHooks" => 'setupTestEnvironment', + 'API.DocumentationGenerator.hideExceptForSuperUser' => 'checkIfNotSuperUser' ); } @@ -39,4 +40,14 @@ class DBStats extends \Piwik\Plugin $dao = new Mocks\MockDataAccess(); }); } + + public function checkIfNotSuperUser(&$response) + { + try { + Piwik::checkUserHasSuperUserAccess(); + $response = false; + } catch (\Exception $e) { + $response = true; + } + } } diff --git a/tests/PHPUnit/Core/API/DocumentationGeneratorTest.php b/tests/PHPUnit/Core/API/DocumentationGeneratorTest.php new file mode 100644 index 0000000000..1179890171 --- /dev/null +++ b/tests/PHPUnit/Core/API/DocumentationGeneratorTest.php @@ -0,0 +1,195 @@ +<?php +/** + * Copyright (C) Piwik PRO - All rights reserved. + * + * Using this code requires that you first get a license from Piwik PRO. + * Unauthorized copying of this file, via any medium is strictly prohibited. + * + * @link http://piwik.pro + */ +use Piwik\API\DocumentationGenerator; +use Piwik\API\Proxy; +use Piwik\EventDispatcher; +/** + * @group CoreD + */ +class DocumentationGeneratorTest extends PHPUnit_Framework_TestCase +{ + public function testCheckIfModuleContainsHideAnnotation() + { + $annotation = '@hide ExceptForSuperUser test test'; + $mock = $this->getMockBuilder('ReflectionClass') + ->disableOriginalConstructor() + ->setMethods(array('getDocComment')) + ->getMock(); + $mock->expects($this->once())->method('getDocComment')->willReturn($annotation); + $documentationGenerator = new DocumentationGenerator(); + $this->assertTrue($documentationGenerator->checkIfClassCommentContainsHideAnnotation($mock)); + } + public function testCheckDocumentation() + { + $moduleToCheck = 'this is documentation which contains @hide ExceptForSuperUser'; + $documentationAfterCheck = 'this is documentation which contains '; + $documentationGenerator = new DocumentationGenerator(); + $this->assertEquals($documentationGenerator->checkDocumentation($moduleToCheck), $documentationAfterCheck); + } + public function testCheckIfMethodCommentContainsHideAnnotation() + { + $annotation = '@hide ForAll test test'; + $mock = $this->getMockBuilder('ReflectionMethod') + ->disableOriginalConstructor() + ->setMethods(array('getDocComment')) + ->getMock(); + $mock->expects($this->once())->method('getDocComment')->willReturn($annotation); + EventDispatcher::getInstance()->addObserver('API.DocumentationGenerator.hideForAll', + function (&$response) { + $response = true; + }); + $this->assertEquals(Proxy::getInstance()->checkIfMethodContainsHideAnnotation($mock), true); + } + public function testPrepareModuleToDisplay() + { + $moduleName = 'VisitTime'; + $moduleToDisplay = "<a href='#VisitTime'>VisitTime</a><br/>"; + $documentationGenerator = new DocumentationGenerator(); + $this->assertEquals($documentationGenerator->prepareModuleToDisplay($moduleName), $moduleToDisplay); + } + /** + * @dataProvider providerPrepareModulesAndMethods + */ + public function testPrepareModulesAndMethods($toDisplay, $actualModulesAndMethods) + { + $this->assertEquals($toDisplay, $actualModulesAndMethods); + } + public function providerPrepareModulesAndMethods() + { + $toDisplay = array( + 'VisitTime'=> + array( + 'getVisitInformationPerLocalTime', + 'getVisitInformationPerServerTime', + 'getByDayOfWeek' + ) + ); + $info = array( + 'getVisitInformationPerLocalTime' => array( + 'idSite', + 'period', + 'date' + ), + 'getVisitInformationPerServerTime' => array( + 'idSite', + 'period', + 'date' + ), + 'getByDayOfWeek' => array( + 'idSite', + 'period', + 'date' + ), + '__documentation' => + 'VisitTime API lets you access reports by Hour (Server time), and by Hour Local Time of your visitors.', + ); + $moduleName = 'VisitTime'; + $documentationGenerator = New DocumentationGenerator(); + $actualModulesAndMethods = $documentationGenerator->prepareModulesAndMethods($info, $moduleName); + return array( + array($toDisplay, $actualModulesAndMethods) + ); + } + /** + * @dataProvider providerPrepareMethodToDisplay + */ + public function testPrepareMethodToDisplay($elementShouldContainsInMethods, $methods) + { + $this->assertContains($elementShouldContainsInMethods, $methods); + } + public function providerPrepareMethodToDisplay() + { + $info = array( + 'sendFeedbackForFeature' => array( + 'featureName', + 'like', + ), + '__documentation' => 'API for plugin Feedback', + ); + $moduleName = 'Feedback'; + $methods = array( + 'sendFeedbackForFeature' + ); + $class = '\Piwik\Plugins\Feedback\API'; + $outputExampleUrls = true; + $prefixUrls = ''; + $firstElementToAssert = "<a name='Feedback' id='Feedback'></a><h2>Module Feedback</h2>" + ."<div class='apiDescription'> API for plugin Feedback </div>"; + $secondElementToAssert = "<div class='apiMethod'>- <b>Feedback.sendFeedbackForFeature </b>" + ."(featureName, like, message = '')" + ."<small><span class=\"example\"> [ No example available ]</span></small></div>"; + $documentationGenerator = new DocumentationGenerator(); + $preparedMethods = $documentationGenerator->prepareMethodToDisplay( + $moduleName, + $info, + $methods, + $class, + $outputExampleUrls, + $prefixUrls + ); + return array( + array($firstElementToAssert, $preparedMethods), + array($secondElementToAssert, $preparedMethods) + ); + } + /** + * @dataProvider providerAddExamples + */ + public function testAddExamples($example, $examples) + { + $this->assertContains($example, $examples); + } + public function providerAddExamples() + { + $class = '\Piwik\Plugins\VisitTime\API'; + $methodName = 'getVisitInformationPerLocalTime'; + $prefixUrls = ''; + $documentationGenerator = new DocumentationGenerator(); + $xmlExample = "<a target=_blank href='?module=API&method=VisitTime.getVisitInformationPerLocalTime" + ."&idSite=1&period=day&date=today&format=xml&token_auth='>XML</a>"; + $jsonExample = "<a target=_blank href='?module=API&method=VisitTime.getVisitInformationPerLocalTime" + ."&idSite=1&period=day&date=today&format=JSON&token_auth='>Json</a>"; + $excelElement = "<a target=_blank href='?module=API&method=VisitTime.getVisitInformationPerLocalTime" + ."&idSite=1&period=day&date=today&format=Tsv&token_auth=&translateColumnNames=1'>Tsv (Excel)</a>"; + $rss = "RSS of the last <a target=_blank href='?module=API&method=VisitTime.getVisitInformationPerLocalTime" + ."&idSite=1&period=day&date=last10&format=rss&token_auth=&translateColumnNames=1'>10 days</a>"; + $examples = $documentationGenerator->addExamples($class, $methodName, $prefixUrls); + return array( + array($xmlExample, $examples), + array($jsonExample, $examples), + array($excelElement, $examples), + array($rss, $examples) + ); + } + public function testGetExampleUrl() + { + $class = '\Piwik\Plugins\VisitTime\API'; + $methodName = 'getVisitInformationPerLocalTime'; + $parametersToSet = array( + 'idSite' => 1, + 'period' => 'day', + 'date' => 'yesterday' + ); + $expectedExampleUrl = + '?module=API&method=VisitTime.getVisitInformationPerLocalTime&idSite=1&period=day&date=yesterday'; + $documentationGenerator = new DocumentationGenerator(); + $this->assertEquals( + $expectedExampleUrl, + $documentationGenerator->getExampleUrl($class, $methodName, $parametersToSet)); + } + public function testGetParametersString() + { + $class = '\Piwik\Plugins\VisitTime\API'; + $name = 'getVisitInformationPerLocalTime'; + $parameters = "(idSite, period, date, segment = '')"; + $documentationGenerator = new DocumentationGenerator(); + $this->assertEquals($parameters, $documentationGenerator->getParametersString($class, $name)); + } +} \ No newline at end of file -- GitLab