r98191 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r98190‎ | r98191 | r98192 >
Date:22:50, 26 September 2011
Author:sean_colombo
Status:deferred (Comments)
Tags:
Comment:
Adding ApiExplorer extension which uses the API to dynamically generate API documentation on Special:ApiExplorer. Depends on the JavascriptAPI extension (currently in Wikia codebase). Would be nice to make this extension able to be aware of the examples that api.php submits & even to let it submit the examples.
Modified paths:
  • /trunk/extensions/ApiExplorer (added) (history)
  • /trunk/extensions/ApiExplorer/ApiExplorer.i18n.php (added) (history)
  • /trunk/extensions/ApiExplorer/SpecialApiExplorer.php (added) (history)
  • /trunk/extensions/ApiExplorer/apiExplorer.css (added) (history)
  • /trunk/extensions/ApiExplorer/apiExplorer.js (added) (history)
  • /trunk/extensions/ApiExplorer/collapse.png (added) (history)
  • /trunk/extensions/ApiExplorer/expand.png (added) (history)

Diff [purge]

Index: trunk/extensions/ApiExplorer/SpecialApiExplorer.php
@@ -0,0 +1,109 @@
 2+<?php
 3+/**
 4+ * A Special Page for interactively exploring the documentation of the specific
 5+ * version of the MediaWiki API running on this MediaWiki installation.
 6+ *
 7+ * @author Sean Colombo <firstname>@<firstname><lastname>.com
 8+ * @addtogroup Extensions
 9+ *
 10+ * TODO: LATER: Auto-create forms (like Flickr's API Explorer) so that
 11+ * the user can do test-runs of all of the API calls. Should show the final URL
 12+ * similar to Special:CategoryIntersection, but should be generic so that it can
 13+ * handle all functions.
 14+ */
 15+
 16+if (!defined('MEDIAWIKI')) die();
 17+
 18+$wgExtensionFunctions[] = 'wfSpecialApiExplorer';
 19+$wgExtensionCredits['specialpage'][] = array(
 20+ 'path' => __FILE__,
 21+ 'name' => 'API Explorer',
 22+ //'url' => 'http://www.mediawiki.org/wiki/Extension:ApiExplorer', // TODO: Put into MediaWiki Extensions repo
 23+ 'description' => 'Extension for interactively viewing live API documentation', // TODO: Update this if we have forms for each function.
 24+ 'descriptionmsg' => 'apiexplorer-desc',
 25+ 'author' => '[http://seancolombo.com Sean Colombo]'
 26+);
 27+
 28+$dir = dirname(__FILE__) . '/';
 29+$wgExtensionMessagesFiles['ApiExplorer'] = $dir . 'ApiExplorer.i18n.php';
 30+
 31+function wfSpecialApiExplorer () {
 32+ global $IP;
 33+ require_once "$IP/includes/SpecialPage.php";
 34+ class SpecialApiExplorer extends SpecialPage {
 35+ /**
 36+ * Constructor
 37+ */
 38+ function SpecialApiExplorer() {
 39+ SpecialPage::SpecialPage( 'ApiExplorer' );
 40+ $this->includable( false );
 41+ }
 42+
 43+ /**
 44+ * Main function of the special page. Basically serves as a wrapper which loads
 45+ * the main functionality (which is in Javascript).
 46+ *
 47+ * @param $par - parameters for SpecialPage. Ignored.
 48+ */
 49+ function execute( $par = null ) {
 50+ global $wgOut, $wgExtensionsPath, $wgCityId, $wgStyleVersion;
 51+ wfProfileIn( __METHOD__ );
 52+
 53+ wfLoadExtensionMessages( "AutoCreateWiki" ); // TODO: This isn't needed anymore, even in Wikia code (which is 2 versions back at the moment), is it?
 54+
 55+ // TODO: Make this work for ResourceLoader (Wikia isn't using RL yet at the time of this writing).
 56+ // Wikia has the cachebuster in the wgExtensionPath (we rewrite that in varnish because many proxies won't cache things that have "?" in the URL), but other MediaWikis need the style-version in the querystring.
 57+ $cbSuffix = ( isset($wgCityId) ? "?{$wgStyleVersion}" : "" );
 58+ $wgOut->addScript( "<script type=\"text/javascript\" src=\"{$wgExtensionsPath}/JavascriptAPI/Mediawiki.js{$cbSuffix}\"></script>" );
 59+ $wgOut->addScript( "<script type=\"text/javascript\" src=\"{$wgExtensionsPath}/ApiExplorer/apiExplorer.js{$cbSuffix}\"></script>" );
 60+ $wgOut->addScript( "<link rel=\"stylesheet\" type=\"text/css\" href=\"{$wgExtensionsPath}/ApiExplorer/apiExplorer.css{$cbSuffix}\" />" );
 61+
 62+ ob_start();
 63+ $buttonHeight = 15;
 64+ $collapseSrc = "$wgExtensionsPath/ApiExplorer/collapse.png$cbSuffix";
 65+ $expandSrc = "$wgExtensionsPath/ApiExplorer/collapse.png$cbSuffix";
 66+ ?><style>
 67+ .collapsible h2 span, .collapsible h3 span{
 68+ width:<?= $buttonHeight ?>px;
 69+ height:1em;
 70+ float:right;
 71+ display:inline-block;
 72+
 73+ background-repeat:no-repeat;
 74+ background-position:right center;
 75+ background-image: url(<?= "$wgExtensionsPath/ApiExplorer/collapse.png$cbSuffix"; ?>);
 76+ }
 77+ .collapsed h2 span, .collapsed h3 span{
 78+ background-image: url(<?= "$wgExtensionsPath/ApiExplorer/expand.png$cbSuffix"; ?>);
 79+ }
 80+ </style>
 81+ <div id='apEx_intro'>
 82+ <?= wfMsg('apiexplorer-intro', "<a href='http://www.mediawiki.org/wiki/API:Main_page'>http://www.mediawiki.org/wiki/API:Main_page</a>") ?>
 83+ </div>
 84+ <div id='apEx_loading'><?= wfMsg('apiexplorer-loading') ?></div>
 85+ <div id='apEx'>
 86+ <?php
 87+ $params = array("modules", "querymodules", "formatmodules");
 88+ foreach($params as $param){
 89+ ?><div class='<?= $param ?> collapsible collapsed paramName' data-param-name='<?= $param ?>'>
 90+ <h2 class='name'><span class='toggleIcon'></span></h2>
 91+ <div class='paramContent'>
 92+ <div class='description'></div>
 93+ <dl>
 94+ <!-- Filled by a call to the API -->
 95+ </dl>
 96+ </div>
 97+ </div><?php
 98+ } ?>
 99+ </div>
 100+ <?php
 101+ $outHtml = ob_get_clean();
 102+
 103+ $this->setHeaders();
 104+ $wgOut->addHTML( $outHtml );
 105+ wfProfileOut( __METHOD__ );
 106+ } // end execute()
 107+ }
 108+
 109+ SpecialPage::addPage( new SpecialApiExplorer );
 110+} // end wfSpecialApiExplorer()
Property changes on: trunk/extensions/ApiExplorer/SpecialApiExplorer.php
___________________________________________________________________
Added: svn:eol-style
1111 + native
Index: trunk/extensions/ApiExplorer/apiExplorer.css
@@ -0,0 +1,105 @@
 2+
 3+#apEx{
 4+ display:none;
 5+}
 6+#apEx_loading{
 7+ display:none;
 8+}
 9+
 10+#apEx div.paramName{
 11+ margin-bottom:10px;
 12+}
 13+dt.collapsible{
 14+ margin-bottom:15px;
 15+}
 16+dt.collapsed{
 17+ width:35%;
 18+ min-width:300px; /* gives less "wall of green" effect when list is loaded & will still expand when the module is clicked on */
 19+}
 20+
 21+#apEx div.paramName h2, .paramContent{
 22+ padding:10px;
 23+ margin:0px;
 24+}
 25+dd.paramContent{ /* even with the rule above, this is still needed in order to have higher specificity than other statements in the skin */
 26+ margin:0px;
 27+}
 28+
 29+.paramContent{
 30+ border-left: 2px solid #008;
 31+ border-right: 2px solid #008;
 32+}
 33+div.paramContent{ /* Top level*/
 34+ background-color: #bdf;
 35+}
 36+dd.paramContent{ /* per-module */
 37+ background-color:#bfd;
 38+}
 39+
 40+.collapsible h2, .collapsible h3{
 41+ /* Make it into a bar */
 42+ margin:0px;
 43+ text-decoration:none;
 44+ cursor:pointer;
 45+ border:2px solid #008;
 46+ -webkit-border-radius: 10px;
 47+ -moz-border-radius: 10px;
 48+ border-radius: 10px;
 49+
 50+ /* Not collapsed... so hide the bottom border so it bleeds into the rest */
 51+ border-bottom:0px;
 52+ border-bottom-left-radius:0px;
 53+ border-bottom-right-radius:0px;
 54+}
 55+.collapsible.collapsed h2, .collapsible.collapsed h3, .paramContent{
 56+ /* Collapsed state for h2, h3 or expanded for content... show bottom border & rounded corners */
 57+ border-bottom:2px solid #008;
 58+ border-bottom-left-radius:10px;
 59+ border-bottom-right-radius:10px;
 60+}
 61+
 62+#apEx div.collapsible h2{
 63+ /* Pretty gradient background */
 64+ background-image: linear-gradient(bottom, rgb(10,74,143) 43%, rgb(52,112,217) 72%);
 65+ background-image: -o-linear-gradient(bottom, rgb(10,74,143) 43%, rgb(52,112,217) 72%);
 66+ background-image: -moz-linear-gradient(bottom, rgb(10,74,143) 43%, rgb(52,112,217) 72%);
 67+ background-image: -webkit-linear-gradient(bottom, rgb(10,74,143) 43%, rgb(52,112,217) 72%);
 68+ background-image: -ms-linear-gradient(bottom, rgb(10,74,143) 43%, rgb(52,112,217) 72%);
 69+ background-image: -webkit-gradient(
 70+ linear,
 71+ left bottom,
 72+ left top,
 73+ color-stop(0.43, rgb(10,74,143)),
 74+ color-stop(0.72, rgb(52,112,217))
 75+ );
 76+
 77+ color:white;
 78+}
 79+
 80+.collapsed div, .collapsed dl, .collapsed dt, .collapsed dd{
 81+ display:none;
 82+}
 83+
 84+
 85+/** Second-level of style... the dts **/
 86+#apEx dt h3, #apEx dt dd{
 87+ border-color: #080;
 88+ box-shadow: 4px 4px 4px #888888;
 89+}
 90+#apEx dt h3{
 91+ padding:5px;
 92+
 93+ background-image: linear-gradient(bottom, rgb(40,230,30) 48%, rgb(133,248,156) 74%);
 94+ background-image: -o-linear-gradient(bottom, rgb(40,230,30) 48%, rgb(133,248,156) 74%);
 95+ background-image: -moz-linear-gradient(bottom, rgb(40,230,30) 48%, rgb(133,248,156) 74%);
 96+ background-image: -webkit-linear-gradient(bottom, rgb(40,230,30) 48%, rgb(133,248,156) 74%);
 97+ background-image: -ms-linear-gradient(bottom, rgb(40,230,30) 48%, rgb(133,248,156) 74%);
 98+
 99+ background-image: -webkit-gradient(
 100+ linear,
 101+ left bottom,
 102+ left top,
 103+ color-stop(0.48, rgb(40,230,30)),
 104+ color-stop(0.74, rgb(133,248,156))
 105+ );
 106+}
Property changes on: trunk/extensions/ApiExplorer/apiExplorer.css
___________________________________________________________________
Added: svn:eol-style
1107 + native
Index: trunk/extensions/ApiExplorer/expand.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/ApiExplorer/expand.png
___________________________________________________________________
Added: svn:mime-type
2108 + image/png
Index: trunk/extensions/ApiExplorer/apiExplorer.js
@@ -0,0 +1,138 @@
 2+/**
 3+ * @author Sean Colombo
 4+ * @date 20110917
 5+ *
 6+ * Javascript app that uses the API on the current wiki to dynamically generate browsable documentation
 7+ * for the same API.
 8+ */
 9+
 10+$().log("== API Explorer ==");
 11+
 12+if(typeof ApiExplorer == "undefined"){
 13+ ApiExplorer = function(){
 14+ var self = this;
 15+
 16+ /**
 17+ * Called on document ready to make the first call to the API to build the list of available functions.
 18+ */
 19+ this.run = function(){
 20+ $().log("Starting API Explorer...");
 21+
 22+ // Show the loading indicator until the API request has returned & the new page-content is set-up.
 23+ $('#apEx_loading').show();
 24+
 25+ // Make a request to the API to get the list of available modules, querymodules, and formats.
 26+ Mediawiki.paraminfo('modules', 'paraminfo', function(result){
 27+ var params = result.paraminfo.modules[0].parameters;
 28+ for(var index in params){
 29+ var param = params[index];
 30+ if(param.type){
 31+ var allTypes = [];
 32+ for(var typeIndex in param.type){
 33+ var t = param.type[typeIndex];
 34+ allTypes.push(t);
 35+ }
 36+ allTypes = allTypes.sort();
 37+
 38+ // There are three specific top-level lists we care about.
 39+ var names = {
 40+ 'modules': 'modules',
 41+ 'querymodules': 'querymodules',
 42+ 'formatmodules': 'formatmodules'
 43+ };
 44+ if(typeof names[param.name] != 'undefined'){
 45+ var name = param.name;
 46+ $('#apEx div.'+name+' h2.name').prepend( param.name ).data('module-name', param.name);
 47+ $('#apEx div.'+name+' div.description').html( param.description );
 48+ for(var typeIndex in allTypes){
 49+ var t = allTypes[typeIndex];
 50+ $('#apEx div.'+name+' dl').append("<dt class='collapsible collapsed'><h3 data-param-name='"+t+"'>" + t + "<span class='toggleIcon'></span></h3></dt>");
 51+ }
 52+
 53+ // Add click-handlers to each dt to get more info on that function.
 54+ $('#apEx div.'+name+' dt.collapsible').click( function(e){
 55+ // If already expanded, just collapse.
 56+ if(!$( e.currentTarget ).hasClass('collapsed')){
 57+ $( e.currentTarget ).addClass('collapsed');
 58+ } else {
 59+ // Toggle dt (yes, T) state to be expanded
 60+ $( e.currentTarget ).removeClass('collapsed');
 61+
 62+ var paramName = $(e.currentTarget).parents('div.paramName').data('param-name');
 63+
 64+ // Make call to API to get info for that dt... ONLY if the dd is still empty.
 65+ var ddContent = $(e.currentTarget).find('dd').html();
 66+ if(ddContent == "" || ddContent == null){
 67+ // The dd (definition) for this dt (term) is not loaded yet. Use the API to load it.
 68+
 69+ // TODO: Put a loading-indicator in the h3 and then remove when finished loading?
 70+ // TODO: Put a loading-indicator in the h3 and then remove when finished loading?
 71+
 72+ var dtName = $(e.currentTarget).find('h3').data('param-name');
 73+ $().log("Content is empty, loading info from API for " + paramName + '=' + dtName);
 74+
 75+ Mediawiki.paraminfo(paramName, dtName, function(result){
 76+ $().log("Got info for "+ paramName + '=' + dtName);
 77+ var modulesArray = result.paraminfo[paramName];
 78+
 79+ var module = modulesArray[0];
 80+ //var rawData = JSON.stringify( module );
 81+ var html = self.objToHtml( module );
 82+ $(e.currentTarget).append("<dd class='paramContent'>" + html + "</dd>");
 83+ });
 84+ }
 85+ }
 86+ });
 87+ }
 88+ }
 89+ }
 90+
 91+ // Make the top-level divs (modules, querymodules, formatmodules) collapsible.
 92+ $('#apEx div.collapsible h2').click(function( e ){
 93+ $( e.currentTarget ).parent('div').toggleClass('collapsed');
 94+ });
 95+
 96+ $('#apEx_loading').hide();
 97+ $('#apEx').show();
 98+ }, function(err){
 99+ $().log("ERROR GETTING LIST OF MODULES FROM THE API.\nMake sure this wiki is a Wikia wiki or running v1.18+ of MediaWiki.");
 100+ $().log(err);
 101+ });
 102+ };
 103+
 104+ /**
 105+ * Converts a JS object to HTML (mostly nested lists), but tweaked for the specific use-case of
 106+ * the API Explorer's results from the API... so it will make the result more readable, but this function
 107+ * isn't as simple/portable as it would be otherwise.
 108+ */
 109+ this.objToHtml = function( obj ){
 110+ var html = "";
 111+ var html = "<ul>";
 112+
 113+ // TODO: Customize this for better display of parameters, errors, etc. (they're too nested right now).
 114+
 115+ for(var key in obj){
 116+ var value = obj[key];
 117+ if(typeof value == 'object'){
 118+ value = self.objToHtml( value );
 119+ }
 120+ html += "<li><strong>"+key+"</strong>: " + value + "</li>";
 121+ }
 122+ html += "</ul>";
 123+ return html;
 124+ };
 125+
 126+ this.clicked_modules = function(e){ return self.dtClicked( e, 'modules'); }
 127+ this.clicked_querymodules = function(e){ return self.dtClicked( e, 'querymodules'); }
 128+ this.clicked_formatmodules = function(e){ return self.dtClicked( e, 'formatmodules'); }
 129+ this.dtClicked = function(e, paramName){
 130+ $().log("Got a click on " + paramName);
 131+ $().log(e);
 132+ };
 133+ };
 134+}
 135+
 136+$(document).ready(function(){
 137+ var apEx = new ApiExplorer();
 138+ apEx.run();
 139+});
Property changes on: trunk/extensions/ApiExplorer/apiExplorer.js
___________________________________________________________________
Added: svn:eol-style
1140 + native
Index: trunk/extensions/ApiExplorer/collapse.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/ApiExplorer/collapse.png
___________________________________________________________________
Added: svn:mime-type
2141 + image/png
Index: trunk/extensions/ApiExplorer/ApiExplorer.i18n.php
@@ -0,0 +1,26 @@
 2+<?php
 3+/**
 4+ * Internationalisation file for extension ApiExplorer.
 5+ *
 6+ * @addtogroup Extensions
 7+*/
 8+
 9+$messages = array();
 10+
 11+/** English
 12+ * @author Sean Colombo
 13+ */
 14+$messages['en'] = array(
 15+ 'apiexplorer' => 'API Explorer',
 16+ 'apiexplorer-desc' => 'Page for interactively exploring the documentation of the live version of the MediaWiki API running on this installation.',
 17+ 'apiexplorer-intro' => 'This page shows documentation for the current wiki\'s API. Please click on a function below to see more info about what parameters can be used in each call. For the general MediaWiki API documentation, see $1',
 18+
 19+ 'apiexplorer-loading' => 'Loading functions...',
 20+);
 21+
 22+/** Message documentation (Message documentation)
 23+ */
 24+$messages['qqq'] = array(
 25+ 'apiexplorer-desc' => "Description of the extension (for Special:Version, etc.).",
 26+ 'apiexplorer-intro' => "The intro at the top of the page to acquiant new users with what the page does."
 27+);
Property changes on: trunk/extensions/ApiExplorer/ApiExplorer.i18n.php
___________________________________________________________________
Added: svn:eol-style
128 + native

Follow-up revisions

RevisionCommit summaryAuthorDate
r98793Add ApiExlorer (r98191) to translatewiki.netraymond18:32, 3 October 2011
r99214Follow-up r98191:...siebrand14:23, 7 October 2011
r99229Followup to r98191. Added alias file, changed to be a special page, and fixed...sean_colombo18:42, 7 October 2011

Comments

#Comment by Bawolff (talk | contribs)   22:54, 26 September 2011

Should perhaps be combined with the ApiSandbox extension. I think they do similar things.

#Comment by SColombo~mediawikiwiki (talk | contribs)   23:41, 26 September 2011

I talked to MaxSem (who wrote ApiSandbox) and checked out the code just a bit. Unfortunately, I think the potential for code-reuse is fairly low since we approached it differently. ApiSandbox builds forms server-side to submit requests, while ApiExplorer uses introspection from the API (some of which is new) to build out the documentation (and hopefully to later use example urls for requests).

It's kind of a bummer that the approaches aren't very compatible with each other.

Thanks for the tip though :)

#Comment by Siebrand (talk | contribs)   14:21, 7 October 2011

This extension uses some very outdated concepts and should be updated.

#Comment by SColombo~mediawikiwiki (talk | contribs)   18:43, 7 October 2011

Updated in r99229. Thanks for the i18n fixes and the @fixmes :)

Status & tagging log