Yurik has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/162850

Change subject: Added private-wiki-only lua extensions
......................................................................

Added private-wiki-only lua extensions

* Several functions that expose MW API and access to request object
* Abilitiy to override output to raw format
* global var that controls all this $wgEnableSpecialLuaAccess

Change-Id: Ifecbbb6e989f1ca510fedeb42cd90d18ea218ff9
---
M ZeroPortal.php
A includes/LuaLibrary.lua
A includes/LuaLibrary.php
M includes/PortalSpecialPage.php
4 files changed, 354 insertions(+), 8 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/ZeroPortal 
refs/changes/50/162850/1

diff --git a/ZeroPortal.php b/ZeroPortal.php
index b9249ad..a75eb5e 100644
--- a/ZeroPortal.php
+++ b/ZeroPortal.php
@@ -31,20 +31,22 @@
        'url'  =>  'https://www.mediawiki.org/wiki/Extension:ZeroPortal',
 );
 
-$cwd = __DIR__ . DIRECTORY_SEPARATOR;
-$wgMessagesDirs['ZeroPortal'] = $cwd . 'i18n';
-$wgExtensionMessagesFiles['ZeroPortal'] = $cwd . 'ZeroPortal.i18n.php';
+$zpDir = __DIR__ . DIRECTORY_SEPARATOR;
+$wgMessagesDirs['ZeroPortal'] = $zpDir . 'i18n';
+$wgExtensionMessagesFiles['ZeroPortal'] = $zpDir . 'ZeroPortal.i18n.php';
 
 // autoload extension classes
-$cwd .= 'includes' . DIRECTORY_SEPARATOR;
+$zpDir .= 'includes' . DIRECTORY_SEPARATOR;
 foreach ( array(
                  'ApiZeroPortal',
                  'ConfigPageHooks',
-                 'ZeroConfigView',
+                 'LuaLibrary',
                  'PortalSpecialPage',
+                 'ZeroConfigView',
           ) as $key => $class ) {
-       $wgAutoloadClasses['ZeroPortal\\' . ( is_string( $key ) ? $key : $class 
)] = $cwd . $class . '.php';
+       $wgAutoloadClasses['ZeroPortal\\' . ( is_string( $key ) ? $key : $class 
)] = $zpDir . $class . '.php';
 }
+unset( $zpDir );
 
 $wgResourceModules['zeroportal.config'] = array(
        'dependencies' => 'zerobanner.config.styles', // defined in ZeroBanner 
ext, used for sample banners
@@ -56,6 +58,12 @@
 
 $wgAPIModules['zeroportal'] = 'ZeroPortal\ApiZeroPortal';
 $wgHooks['BeforePageDisplay'][] = 
'ZeroPortal\ConfigPageHooks::onBeforePageDisplay';
+$wgHooks['ScribuntoExternalLibraries'][] = function( $engine, array 
&$extraLibraries ) {
+       if( $engine == 'lua' ) {
+               $extraLibraries['mw.zeroportal'] = 'ZeroPortal\LuaLibrary';
+       }
+       return true;
+};
 
 // Set our own view class for the JsonZeroConfig model, and configure local 
storage
 $wgJsonConfigModels['JsonZeroConfig']['view'] = 'ZeroPortal\ZeroConfigView';
@@ -66,3 +74,4 @@
 $wgExtensionMessagesFiles['ZeroPortalAlias'] = __DIR__ . DIRECTORY_SEPARATOR . 
'ZeroPortal.alias.php';
 
 $wgZeroPortalImpersonateUser = false;
+$wgEnableSpecialLuaAccess = false;
diff --git a/includes/LuaLibrary.lua b/includes/LuaLibrary.lua
new file mode 100644
index 0000000..21a7d47
--- /dev/null
+++ b/includes/LuaLibrary.lua
@@ -0,0 +1,66 @@
+local p = {}
+local php
+
+function p.api( data )
+    return php.api( data )
+end
+
+function p.getAccountsForUser()
+    return php.getAccountsForUser()
+end
+
+function p.getCheck( name )
+    return php.getCheck( name )
+end
+
+function p.getUsername()
+    return php.getUsername()
+end
+
+function p.getVal( name, default )
+    return php.getVal( name, default )
+end
+
+function p.isAdmin()
+    return php.isAdmin()
+end
+
+function p.jsonDecode( value )
+    return php.jsonDecode( value )
+end
+
+function p.jsonEncode( value, escapeHtml )
+    return php.jsonEncode( value, escapeHtml )
+end
+
+function p.register()
+    return php.register()
+end
+
+function p.setCache( mode, seconds )
+    return php.setCache( mode, seconds )
+end
+
+function p.setResult( result, mime )
+    return php.setResult( result, mime )
+end
+
+function p.wasPosted()
+    return php.wasPosted()
+end
+
+
+function p.setupInterface( options )
+       -- Boilerplate
+       p.setupInterface = nil
+       php = mw_interface
+       mw_interface = nil
+
+       -- Register this library in the "mw" global
+       mw = mw or {}
+       mw.zeroportal = p
+
+       package.loaded['mw.zeroportal'] = p
+end
+
+return p
diff --git a/includes/LuaLibrary.php b/includes/LuaLibrary.php
new file mode 100644
index 0000000..e7b18ac
--- /dev/null
+++ b/includes/LuaLibrary.php
@@ -0,0 +1,257 @@
+<?php
+
+namespace ZeroPortal;
+
+use ApiMain;
+use DerivativeContext;
+use FormatJson;
+use Scribunto_LuaError;
+use Scribunto_LuaLibraryBase;
+use IContextSource;
+use User;
+use ZeroBanner\ZeroConfig;
+
+class LuaLibrary extends Scribunto_LuaLibraryBase {
+
+       const luaNamespace = 'mw.zeroportal.';
+
+       /** @var IContextSource */
+       private static $context = null;
+
+       /** @var mixed */
+       private static $result = null;
+
+       /** @var string */
+       private static $mime = 'text/plain';
+
+       /** @var int */
+       private static $cacheMaxAge = 0;
+
+       /** @var string */
+       private static $cacheMode = 'private';
+
+       public function register() {
+               $functions = array();
+               foreach ( array(
+                                 'api',
+                                 'getAccountsForUser',
+                                 'getCheck',
+                                 'getUsername',
+                                 'getVal',
+                                 'isAdmin',
+                                 'jsonDecode',
+                                 'jsonEncode',
+                                 'register',
+                                 'setCache',
+                                 'setResult',
+                                 'wasPosted',
+                         ) as $f ) {
+                       $functions[$f] = array( $this, $f );
+               }
+               $moduleFileName = __DIR__ . DIRECTORY_SEPARATOR . 
'LuaLibrary.lua';
+               $this->getEngine()->registerInterface( $moduleFileName, 
$functions, array() );
+       }
+
+       /**
+        * @param IContextSource $context
+        */
+       public static function setContext( $context ) {
+               self::$context = $context;
+       }
+
+       /**
+        * @return IContextSource
+        * @throws Scribunto_LuaError
+        */
+       public static function getContext() {
+               if ( !self::$context ) {
+                       throw new Scribunto_LuaError( 'This function is only 
available in Special:ZeroPortal' );
+               }
+               return self::$context;
+       }
+
+       /**
+        * Finishes up the invocation, clean up context, and overrides output 
if needed
+        */
+       public static function endInvoke() {
+               $ctx = self::getContext();
+               $out = $ctx->getOutput();
+               $resp = $ctx->getRequest()->response();
+               self::setContext( null );
+
+               //
+               // FIXME:  Need to review all of these headers
+               //
+
+               $expiryUnixTime = ( self::$cacheMaxAge == 0 ? 1 : time() + 
self::$cacheMaxAge );
+               $resp->header( 'Expires: ' . wfTimestamp( TS_RFC2822, 
$expiryUnixTime ) );
+
+               $cacheMode = self::$cacheMode === 'public' ? 'public' : 
'private';
+               $resp->header( 'Cache-Control: ' . $cacheMode . ', 
must-revalidate, max-age=0' );
+
+               if ( self::$result ) {
+                       $out->disable();
+                       $resp->header( 'Content-Type: ' . self::$mime . '; 
charset=UTF-8' );
+                       echo self::$result;
+                       return true;
+               } else {
+                       return false;
+               }
+       }
+
+       /**
+        * Make an API call
+        * !!!SECURITY!!! This gives access to the entire (write-enabled) API 
as an impersonation user (admin)!
+        * @param array $data MW API parameters
+        * @return mixed[]
+        * @throws Scribunto_LuaError
+        */
+       public function api( $data = null ) {
+               $this->incrementExpensiveFunctionCount();
+
+               $ctx = new DerivativeContext( self::getContext() );
+
+               global $wgZeroPortalImpersonateUser;
+               if ( $wgZeroPortalImpersonateUser ) {
+                       $ctx->setUser( User::newFromName( 
$wgZeroPortalImpersonateUser ) );
+               }
+
+               $req = self::getContext()->getRequest();
+               $ctx->setRequest( new \DerivativeRequest( $req, $data, 
$req->wasPosted() ) );
+
+               $api = new ApiMain( $ctx, true );
+               $api->execute();
+               return array( $api->getResult()->getData() );
+       }
+
+       /**
+        * Returns true if the value was passed as a parameter
+        * @param string $name
+        * @return bool[]
+        * @throws Scribunto_LuaError
+        */
+       public function getCheck( $name = null ) {
+               $this->checkType( self::luaNamespace . __FUNCTION__, 1, $name, 
'string' );
+               return array( self::getContext()->getRequest()->getCheck( $name 
) );
+       }
+
+       /**
+        * Returns a list of Zero config titles that this user has been 
assigned to as an admin
+        * @return string[]|false[]
+        */
+       public function getAccountsForUser() {
+               $configs = array();
+               list( $name ) = $this->getUsername();
+               if ( $name ) {
+                       ApiZeroPortal::iterateAllConfigs( false,
+                               function ( ZeroConfig $content, $title ) use ( 
$name, &$configs ) {
+                                       if ( $name === true || in_array( $name, 
$content->admins() ) ) {
+                                               $configs[] = $title;
+                                       }
+                                       return false;
+                               } );
+               }
+               return array( $configs );
+       }
+
+       /**
+        * Return name of the logged in user, or false if anonymous
+        * @return string[]|bool[]
+        * @throws Scribunto_LuaError
+        */
+       public function getUsername() {
+               $user = self::getContext()->getUser();
+               if ( $user->isAnon() ) {
+                       return array( false );
+               } else {
+                       return array( $user->getName() );
+               }
+       }
+
+       /**
+        * Get a value from the request (GET or POST).
+        * !!!SECURITY!!! This gives access to user's cookies
+        * @param string $name
+        * @param mixed $default
+        * @return mixed[]
+        * @throws Scribunto_LuaError
+        */
+       public function getVal( $name = null, $default = null ) {
+               $this->checkType( self::luaNamespace . __FUNCTION__, 1, $name, 
'string' );
+               return array( self::getContext()->getRequest()->getVal( $name, 
$default ) );
+       }
+
+       /**
+        * Return true if this is an admin user
+        * @return bool[]
+        * @throws Scribunto_LuaError
+        */
+       public function isAdmin() {
+               return array( self::getContext()->getUser()->isAllowed( 
'zero-edit' ) );
+       }
+
+       /**
+        * Decode value as a JSON string. This function is enabled for all 
modules
+        * @param mixed $value
+        * @throws Scribunto_LuaError
+        * @return mixed[]
+        */
+       public function jsonDecode( $value = null ) {
+               $this->checkType( self::luaNamespace . __FUNCTION__, 1, $value, 
'string' );
+               return array( FormatJson::decode( $value, true ) );
+       }
+
+       /**
+        * Encode value as a JSON string. This function is enabled for all 
modules
+        * @param mixed $value
+        * @param bool $escapeHtml
+        * @throws Scribunto_LuaError
+        * @return string
+        */
+       public function jsonEncode( $value = null, $escapeHtml = null ) {
+               $this->checkTypeOptional( self::luaNamespace . __FUNCTION__, 2, 
$escapeHtml, 'boolean', false );
+               $result = FormatJson::encode( $value, false, $escapeHtml ? 
FormatJson::UTF8_OK : FormatJson::ALL_OK );
+               if ( $result === false ) {
+                       throw new Scribunto_LuaError( 'Unable to encode value' 
);
+               }
+               return array( $result );
+       }
+
+       /**
+        * Set raw result of the special page for the given caching type 
(public, private), and duration
+        * @param string $mode
+        * @param int $seconds
+        * @throws Scribunto_LuaError
+        */
+       public function setCache( $mode = null, $seconds = null ) {
+               $this->checkType( self::luaNamespace . __FUNCTION__, 1, $mode, 
'string' );
+               $this->checkType( self::luaNamespace . __FUNCTION__, 2, 
$seconds, 'number' );
+               self::getContext(); // ensure special page is enabled
+               self::$cacheMode = $mode;
+               self::$cacheMaxAge = $seconds;
+       }
+
+       /**
+        * Set raw result of the special page for a given mime type
+        * @param mixed $result
+        * @param string $mime
+        * @throws Scribunto_LuaError
+        */
+       public function setResult( $result = null, $mime = null ) {
+               self::getContext(); // ensure special page is enabled
+               $this->checkType( self::luaNamespace . __FUNCTION__, 1, 
$result, 'string' );
+               $this->checkTypeOptional( self::luaNamespace . __FUNCTION__, 2, 
$mime, 'string', 'text/plain' );
+               self::$result = $result;
+               self::$mime = $mime;
+       }
+
+       /**
+        * Returns true if the present request was reached by a POST operation, 
false otherwise
+        * @return bool[]
+        * @throws Scribunto_LuaError
+        */
+       public function wasPosted() {
+               return array( self::getContext()->getRequest()->wasPosted() );
+       }
+
+}
diff --git a/includes/PortalSpecialPage.php b/includes/PortalSpecialPage.php
index d5d5b67..07a1a37 100644
--- a/includes/PortalSpecialPage.php
+++ b/includes/PortalSpecialPage.php
@@ -29,9 +29,17 @@
        public function execute( $par ) {
                $out = $this->getOutput();
 
+               global $wgEnableSpecialLuaAccess;
+               if ( $wgEnableSpecialLuaAccess !== true ) {
+                       $out->addWikiText( '$wgEnableSpecialLuaAccess is off' );
+                       return;
+               }
+
+
 //             $out->addModules( 'zeroportal.special.scripts' );
 //             $out->addModuleStyles( 'zeroportal.special.styles' );
 
+               // todo: delete all parameter handling once we start using lua 
extension functions
                $user = $this->getUser();
                if ( !$user->isAnon() ) {
                        // Include all zero-config ids if this is an admin
@@ -52,9 +60,15 @@
                $configs = self::encodeParameter( $configs );
                $state = self::encodeParameter( $this->getRequest()->getVal( 
's' ) );
                $user = self::encodeParameter( $user->isAnon() ? false : 
$user->getName() );
-               $out->addWikiText( 
"{{#invoke:Portal|main|$configs|$state|$user}}" );
 
-               if ( $this->getRequest()->getCheck( 'raw' ) ) {
+
+               LuaLibrary::setContext( $this->getContext() );
+               $out->addWikiText( 
"{{#invoke:Portal|main|$configs|$state|$user}}" );
+               $done = LuaLibrary::endInvoke();
+
+
+               // todo: delete all this once we start using lua extension 
functions
+               if ( !$done && $this->getRequest()->getCheck( 'raw' ) ) {
                        // Output
                        $text = html_entity_decode( $out->getHTML(), ENT_QUOTES 
| ENT_HTML5 );
 

-- 
To view, visit https://gerrit.wikimedia.org/r/162850
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ifecbbb6e989f1ca510fedeb42cd90d18ea218ff9
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/ZeroPortal
Gerrit-Branch: master
Gerrit-Owner: Yurik <yu...@wikimedia.org>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to