Provide VE-rebaser in MediaWiki as Special:CollabPad

Change-Id: I3ffe0e2b6af43d4018dcd79877f4f27ed7d6a7e7
This commit is contained in:
Ed Sanders 2016-10-25 13:44:26 -07:00 committed by Jforrester
parent 1ffb299f18
commit 574baf2e8e
10 changed files with 373 additions and 1 deletions

View file

@ -188,6 +188,10 @@ class ApiVisualEditor extends ApiBase {
$params = $this->extractRequestParams();
$title = Title::newFromText( $params['page'] );
if ( $title && $title->isSpecial( 'CollabPad' ) ) {
// Convert Special:CollabPad/MyPage to MyPage so we can parsefragment properly
$title = Title::newFromText( preg_replace( '`^([^/]+/)`', '', $params['page'] ) );
}
if ( !$title ) {
$this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $params['page'] ) ] );
}

View file

@ -792,6 +792,7 @@ class VisualEditorHooks {
'svgMaxSize' => $coreConfig->get( 'SVGMaxSize' ),
'namespacesWithSubpages' => $coreConfig->get( 'NamespacesWithSubpages' ),
'specialBooksources' => urldecode( SpecialPage::getTitleFor( 'Booksources' )->getPrefixedURL() ),
'rebaserUrl' => $coreConfig->get( 'VisualEditorRebaserURL' ),
'restbaseUrl' => $coreConfig->get( 'VisualEditorRestbaseURL' ),
'fullRestbaseUrl' => $coreConfig->get( 'VisualEditorFullRestbaseURL' ),
'feedbackApiUrl' => $veConfig->get( 'VisualEditorFeedbackAPIURL' ),

View file

@ -0,0 +1,23 @@
<?php
/**
* Aliases for Special:CollabPad
*
* @file
* @ingroup Extensions
*/
$specialPageAliases = [];
/** English
* @author "James D. Forrester"
*/
$specialPageAliases['en'] = [
'CollabPad' => [ 'CollabPad', 'Collab Pad' ],
];
/** Cyrmu
* @author "James D. Forrester"
*/
$specialPageAliases['cy'] = [
'CollabPad' => [ 'PadCydweithredu', 'Pad Cydweithredu' ],
];

View file

@ -31,6 +31,7 @@
"site",
"user"
],
"VisualEditorRebaserURL": false,
"VisualEditorRestbaseURL": false,
"VisualEditorFullRestbaseURL": false,
"VisualEditorFeedbackAPIURL": false,
@ -127,6 +128,9 @@
"modules/ve-wmf/i18n"
]
},
"ExtensionMessagesFiles": {
"VisualEditorAlias": "VisualEditor.i18n.alias.php"
},
"Hooks": {
"BeforePageDisplay": [
"VisualEditorHooks::onBeforePageDisplay"
@ -224,6 +228,16 @@
"mobile"
]
},
"socket.io": {
"scripts": [
"lib/ve/lib/socket.io-client/socket.io.min.js"
]
},
"dompurify": {
"scripts": [
"lib/ve/lib/dompurify/purify.js"
]
},
"unicodejs": {
"scripts": [
"lib/ve/lib/unicodejs/unicodejs.js"
@ -421,6 +435,32 @@
"mobile"
]
},
"ext.visualEditor.collabTarget": {
"scripts": [
"modules/ve-mw-collab/ve.init.mw.CollabTarget.js"
],
"dependencies": [
"ext.visualEditor.base",
"ext.visualEditor.mediawiki",
"ext.visualEditor.core.desktop",
"ext.visualEditor.mwextensions.desktop",
"ext.visualEditor.desktopArticleTarget"
],
"messages" : []
},
"ext.visualEditor.collabTarget.init": {
"scripts": [
"modules/ve-mw-collab/ve.init.mw.CollabTarget.init.js"
],
"dependencies": [
"oojs-ui"
]
},
"ext.visualEditor.collabTarget.init.styles": {
"styles": [
"modules/ve-mw-collab/ve.init.mw.CollabTarget.css"
]
},
"ext.visualEditor.ve": {
"scripts": "lib/ve/src/ve.js",
"targets": [
@ -1049,6 +1089,26 @@
"mobile"
]
},
"ext.visualEditor.rebase": {
"scripts": [
"lib/ve/src/dm/ve.dm.Change.js",
"lib/ve/src/dm/ve.dm.RebaseClient.js",
"lib/ve/src/dm/ve.dm.SurfaceSynchronizer.js",
"lib/ve/src/ui/widgets/ve.ui.AuthorListWidget.js"
],
"styles": [
"lib/ve/src/ui/styles/widgets/ve.ui.AuthorListWidget.css"
],
"dependencies": [
"ext.visualEditor.core",
"dompurify",
"socket.io",
"oojs-ui.styles.icons-alerts"
],
"messages": [
"visualeditor-rebase-client-author-name"
]
},
"ext.visualEditor.core.desktop": {
"scripts": [
"lib/ve/src/ui/contexts/ve.ui.DesktopContext.js",
@ -1058,7 +1118,8 @@
"lib/ve/src/ui/styles/ve.ui.DesktopContext.css"
],
"dependencies": [
"ext.visualEditor.core"
"ext.visualEditor.core",
"ext.visualEditor.rebase"
],
"targets": [
"desktop"
@ -2020,9 +2081,13 @@
"visualeditor-editor": "wikitext",
"visualeditor-hidetabdialog": 0
},
"SpecialPages": {
"CollabPad": "SpecialCollabPad"
},
"AutoloadClasses": {
"ApiVisualEditor": "ApiVisualEditor.php",
"ApiVisualEditorEdit": "ApiVisualEditorEdit.php",
"SpecialCollabPad": "modules/ve-mw-collab/SpecialCollabPad.php",
"VisualEditorHooks": "VisualEditor.hooks.php",
"VisualEditorDataModule": "VisualEditorDataModule.php",
"VisualEditorDesktopArticleTargetInitModule": "VisualEditorDesktopArticleTargetInitModule.php"

View file

@ -0,0 +1,75 @@
<?php
class SpecialCollabPad extends SpecialPage {
private $prefixes = [];
/**
* @var null|Title
*/
private $title = null;
/**
* @var null|ParserOutput
*/
private $output = null;
function __construct() {
parent::__construct( 'CollabPad' );
}
protected function getGroupName() {
return 'wiki';
}
public function userCanExecute( User $user ) {
global $wgVisualEditorRebaserURL;
return !!$wgVisualEditorRebaserURL && parent::userCanExecute( $user );
}
function isListed() {
global $wgVisualEditorRebaserURL;
return !!$wgVisualEditorRebaserURL;
}
function execute( $par ) {
$this->setHeaders();
$this->checkPermissions();
$request = $this->getRequest();
$output = $this->getOutput();
$output->addJsConfigVars( 'collabPadPageName', $par );
$output->addModuleStyles( 'ext.visualEditor.collabTarget.init.styles' );
$output->addModules( 'ext.visualEditor.collabTarget.init' );
$output->enableOOUI();
if ( $par ) {
$title = Title::newFromText( $par );
$output->setPageTitle( 'CollabPad: ' . $title->getPrefixedText() );
$output->addHTML( new OOUI\ProgressBarWidget( [
'classes' => [ 've-init-mw-collabTarget-loading' ]
] ) );
} else {
// Scripts only, styles already added above
$output->addModules( 'ext.visualEditor.collabTarget' );
// TODO: Output this "form" unconditionally so the user can
// navigate back to it without reloading the page.
$output->addHTML( new OOUI\ActionFieldLayout(
new OOUI\TextInputWidget( [
'classes' => [ 've-init-mw-collabTarget-nameInput' ],
'placeholder' => $this->msg( 'visualeditor-rebase-client-document-name' )->text(),
'autofocus' => true,
'infusable' => true
] ),
new OOUI\ButtonWidget( [
'classes' => [ 've-init-mw-collabTarget-nameButton' ],
'label' => $this->msg( 'visualeditor-rebase-client-document-create-edit' )->text(),
// Only enable once JS has loaded
'disabled' => true,
'infusable' => true
] ),
[ 'classes' => [ 've-init-mw-collabTarget-nameField' ] ] )
);
}
}
}

View file

@ -0,0 +1,28 @@
/*!
* VisualEditor MediaWiki Initialization CollabTarget styles.
*
* @copyright 2011-2017 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
.ve-init-mw-collabTarget-loading,
.ve-init-mw-collabTarget-nameField {
margin: 0 auto;
width: 40em;
margin-top: 2em !important; /* stylelint-disable-line declaration-no-important */
}
.ve-init-mw-collabTarget .ve-ui-authorListWidget {
display: inline-block;
margin: 0.3125em;
}
/* MW theme hack */
.ve-init-mw-collabTarget .ve-ui-authorListWidget-author.oo-ui-iconElement .oo-ui-iconElement-icon {
left: 0;
}
/* Hack for T165794 */
.ve-init-mw-collabTarget .ve-ui-authorListWidget-editName > .oo-ui-fieldLayout-body > .oo-ui-fieldLayout-header > .oo-ui-labelElement-label {
padding-top: 0.6em !important; /* stylelint-disable-line declaration-no-important */
}

View file

@ -0,0 +1,98 @@
/*!
* VisualEditor MediaWiki DesktopArticleTarget init.
*
* This file must remain as widely compatible as the base compatibility
* for MediaWiki itself (see mediawiki/core:/resources/startup.js).
* Avoid use of: ES5, SVG, HTML5 DOM, ContentEditable etc.
*
* @copyright 2011-2016 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
( function () {
var $content = $( '#mw-content-text' ),
conf = mw.config.get( 'wgVisualEditorConfig' ),
pageTitle = mw.Title.newFromText( mw.config.get( 'collabPadPageName' ) || '' ),
modules = [ 'ext.visualEditor.collabTarget' ]
// Add modules from $wgVisualEditorPluginModules
.concat( conf.pluginModules.filter( mw.loader.getState ) ),
loadingPromise = mw.loader.using( modules );
if ( !VisualEditorSupportCheck() ) {
// VE not supported - say something?
return;
}
function showPage( title ) {
$( '#firstHeading' ).text( 'CollabPad: ' + title.getPrefixedText() ); // TODO: i18n
$content.empty().append(
new OO.ui.ProgressBarWidget( {
classes: [ 've-init-mw-collabTarget-loading' ]
} ).$element
);
loadingPromise.then( function () {
var target = ve.init.mw.targetFactory.create( 'collab' );
$( 'body' ).addClass( 've-activated ve-active' );
$content.empty();
$( '#content' ).append( target.$element );
target.transformPage();
$( '#firstHeading' ).addClass( 've-init-mw-desktopArticleTarget-uneditableContent' );
target.documentReady( ve.createDocumentFromHtml( '' ) );
target.on( 'surfaceReady', function () {
var synchronizer = new ve.dm.SurfaceSynchronizer(
target.getSurface().getModel(),
title.toString(),
{ server: conf.rebaserUrl }
),
authorList = new ve.ui.AuthorListWidget( synchronizer );
target.getToolbar().$actions.append( authorList.$element );
target.getSurface().getView().setSynchronizer( synchronizer );
target.getSurface().getView().focus();
} );
} );
}
function showForm() {
var documentNameInput = OO.ui.infuse( $( '.ve-init-mw-collabTarget-nameInput' ) ),
submitButton = OO.ui.infuse( $( '.ve-init-mw-collabTarget-nameButton' ) );
documentNameInput.setValidation( function ( value ) {
var title = mw.Title.newFromText( value );
return !!title;
} );
function onSubmit() {
var href,
title = mw.Title.newFromText( documentNameInput.getValue() );
if ( title ) {
href = window.location.href + '/' + encodeURIComponent( title.toString() );
if ( history.pushState ) {
// TODO: Handle popstate
history.pushState( null, title.getMain(), href );
showPage( title );
} else {
window.location.href = href;
}
} else {
documentNameInput.focus();
}
}
submitButton.on( 'click', onSubmit );
documentNameInput.on( 'enter', onSubmit );
submitButton.setDisabled( false );
}
if ( pageTitle ) {
showPage( pageTitle );
} else {
showForm();
}
}() );

View file

@ -0,0 +1,76 @@
/*!
* VisualEditor MediaWiki Initialization CollabTarget class.
*
* @copyright 2011-2016 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* MediaWiki mobile article target.
*
* @class
* @extends ve.init.mw.Target
*
* @constructor
* @param {Object} [config] Configuration options
*/
ve.init.mw.CollabTarget = function VeInitMwCollabTarget( config ) {
config = config || {};
config.toolbarConfig = $.extend( {
shadow: true,
actions: true,
floatable: true
}, config.toolbarConfig );
// Parent constructor
ve.init.mw.CollabTarget.super.call( this, config );
this.$originalContent = $( '<div>' ).addClass( 've-init-mw-desktopArticleTarget-originalContent' );
this.$editableContent = $( '#mw-content-text' );
// Initialization
this.$element.addClass( 've-init-mw-articleTarget ve-init-mw-desktopArticleTarget ve-init-mw-collabTarget' ).append( this.$originalContent );
};
/* Inheritance */
OO.inheritClass( ve.init.mw.CollabTarget, ve.init.mw.Target );
/* Static Properties */
ve.init.mw.CollabTarget.static.name = 'collab';
ve.init.mw.CollabTarget.static.trackingName = 'collab';
/* Methods */
/**
* Page modifications after editor load.
*/
ve.init.mw.CollabTarget.prototype.transformPage = function () {
this.$originalContent.append( this.$element.siblings() );
};
/**
* @inheritdoc
*/
ve.init.mw.CollabTarget.prototype.attachToolbar = function () {
this.toolbar.$element.addClass( 've-init-mw-desktopArticleTarget-toolbar ve-init-mw-desktopArticleTarget-toolbar-opened' );
this.$element.prepend( this.toolbar.$element );
};
/**
* @inheritdoc
*/
ve.init.mw.CollabTarget.prototype.setSurface = function ( surface ) {
if ( surface !== this.surface ) {
this.$editableContent.after( surface.$element );
}
// Parent method
ve.init.mw.CollabTarget.super.prototype.setSurface.apply( this, arguments );
};
/* Registration */
ve.init.mw.targetFactory.register( ve.init.mw.CollabTarget );

View file

@ -58,6 +58,7 @@
"apierror-visualeditor-docserver-http-error": "$1",
"apierror-visualeditor-invaliddeflate": "Content provided is not properly deflated",
"apierror-visualeditor-latestnotfound": "Could not find latest revision for title",
"collabpad": "CollabPad",
"tooltip-ca-createsource": "Create the source code of this page",
"tooltip-ca-edit": "Edit this page using wikitext",
"tooltip-ca-editsource": "Edit the source code of this page",

View file

@ -71,6 +71,7 @@
"apierror-visualeditor-docserver-http-error": "{{doc-apierror}}\n\nParameters:\n* $1 - Error message, probably in English",
"apierror-visualeditor-invaliddeflate": "{{doc-apierror}}",
"apierror-visualeditor-latestnotfound": "{{doc-apierror}}",
"collabpad": "Name of CollabPad special page",
"tooltip-ca-createsource": "Tooltip of the {{msg-mw|Visualeditor-ca-createsource}} tab, used if the page does not exist.\n\nSee also:\n* {{msg-mw|Tooltip-ca-editsource}} - tooltip of the {{msg-mw|Visualeditor-ca-editsource}} tab, used if the page already exists",
"tooltip-ca-edit": "Over-ridden tooltip of the wikitext \"Edit source\" tab.",
"tooltip-ca-editsource": "Tooltip of the {{msg-mw|Visualeditor-ca-editsource}} tab, used if the page already exists.\n\nSee also:\n* {{msg-mw|Tooltip-ca-createsource}} - tooltip of the {{msg-mw|Visualeditor-ca-createsource}} tab, used if the page does not exist",