Merge branch 'dmrewrite' of ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/VisualEditor into dmrewrite

This commit is contained in:
Inez Korczynski 2012-06-18 22:23:15 -07:00
commit 5e845979b8
11 changed files with 124 additions and 103 deletions

View file

@ -9,30 +9,36 @@ class ApiVisualEditor extends ApiBase {
$params = $this->extractRequestParams();
$page = Title::newFromText( $params['page'] );
if ($params['paction'] === 'parse') {
$parsed = Http::get(
$parsoid.$page
);
if ( $params['paction'] === 'parse' ) {
if ( $page->exists() ) {
$parsed = Http::get(
$parsoid . $page->getPrefixedDBkey()
);
if ( $parsed ) {
$result = array(
'result' => 'success',
'parsed' => $parsed
);
if ( $parsed ) {
$result = array(
'result' => 'success',
'parsed' => $parsed
);
} else {
$result = array(
'result' => 'error'
);
}
} else {
$result = array(
'result' => 'error'
);
$result = array( 'result' => 'success', 'parsed' => '' );
}
} elseif ($params['paction'] === 'save') {
} elseif ( $params['paction'] === 'save' ) {
// API Posts HTML to Parsoid Service, receives Wikitext,
// API Saves Wikitext to page.
$wikitext = Http::post( $parsoid.$page, array( 'postData' => array( 'content' => $params['html'] ) ) );
$wikitext = Http::post( $parsoid . $page->getPrefixedDBkey(),
array( 'postData' => array( 'content' => $params['html'] ) )
);
if ( $wikitext ) {
/* Save Page */
$flags = $params['minor'] === 'true' ? EDIT_MINOR : EDIT_UPDATE;
$flags = $params['minor'] === 'true' ? EDIT_MINOR : 0;
$wikiPage = WikiPage::factory( $page );
$status = $wikiPage->doEdit(

View file

@ -38,26 +38,4 @@ class VisualEditorHooks {
);
return true;
}
/**
* Prevents editing in the VisualEditor namespace by non-sysop users.
*
* This is attached to the MediaWiki 'userCan' hook.
*/
public static function onUserCan( &$title, &$user, $action, &$result ) {
global $wgUser, $wgNamespaceProtection;
if ( array_key_exists( $title->mNamespace, $wgNamespaceProtection ) ) {
$nsProt = $wgNamespaceProtection[$title->mNamespace];
if ( !is_array( $nsProt ) ) {
$nsProt = array( $nsProt );
}
foreach( $nsProt as $right ) {
if ( $right != '' && !$user->isAllowed( $right ) ) {
$result = false;
}
}
}
return true;
}
}

View file

@ -108,6 +108,7 @@ $wgResourceModules += array(
'copyrightwarning',
'copyrightpage',
'edit',
'create',
'accesskey-ca-edit',
'tooltip-ca-edit',
'viewsource'
@ -283,5 +284,4 @@ $wgAPIModules['ve-parsoid'] = 'ApiVisualEditor';
// Integration Hooks
$wgAutoloadClasses['VisualEditorHooks'] = $dir . 'VisualEditor.hooks.php';
$wgHooks['BeforePageDisplay'][] = 'VisualEditorHooks::onBeforePageDisplay';
$wgHooks['userCan'][] = 'VisualEditorHooks::onUserCan';
$wgHooks['MakeGlobalVariablesScript'][] = 'VisualEditorHooks::onMakeGlobalVariablesScript';

View file

@ -599,7 +599,7 @@ return;
from + sourceNode.model.getOuterLength()
);
tx = ve.dm.Transaction.newFromRemoval( this.documentView.model, range );
this.model.transact( tx );
this.model.change( tx );
}
} else {
// selection removal

View file

@ -373,6 +373,10 @@ ve.dm.Converter.prototype.getDataFromDom = function( domElement, annotations, da
if ( dataElement ) {
data.push( { 'type': '/' + dataElement.type } );
}
// Don't return an empty document
if ( branchType === 'document' && data.length === 0 ) {
return [{ 'type': 'paragraph' }, { 'type': '/paragraph' }];
}
return data;
};

View file

@ -8,7 +8,7 @@
*/
ve.init.ViewPageTarget = function() {
// Inheritance
ve.init.Target.call( this, mw.config.get( 'wgPageName' ) );
ve.init.Target.call( this, mw.config.get( 'wgRelevantPageName' ) );
// Properties
this.$surface = $( '<div class="ve-surface"></div>' );
@ -21,9 +21,25 @@ ve.init.ViewPageTarget = function() {
this.activating = false;
this.deactivating = false;
this.scrollTop = null;
this.section = null;
this.proxiedOnSurfaceModelTransact = ve.proxy( this.onSurfaceModelTransact, this );
this.surfaceOptions = { 'toolbars': { 'top': { 'float': !this.isMobileDevice } } };
this.currentUri = new mw.Uri( window.location.toString() );
this.section = this.currentUri.query.vesection || null;
this.namespaceName = mw.config.get( 'wgCanonicalNamespace' );
this.viewUri = new mw.Uri( mw.util.wikiGetlink( this.pageName ) );
this.editUri = new mw.Uri( this.viewUri.toString() );
this.editUri.extend( { 'action': 'edit' } );
this.veEditUri = new mw.Uri( this.viewUri.toString() );
this.veEditUri.extend( { 'veaction': 'edit' } );
this.isViewPage = (
this.namespaceName === 'VisualEditor' &&
mw.config.get( 'wgAction' ) === 'view' &&
this.currentUri.query.diff === undefined
);
this.canBeActivated = (
this.namespaceName === 'VisualEditor' ||
this.pageName.substr( 0, 13 ) === 'VisualEditor:'
);
// Events
this.addListenerMethods( this, {
@ -34,21 +50,16 @@ ve.init.ViewPageTarget = function() {
} );
// Initialization
var uri = new mw.Uri( window.location.toString() );
if ( mw.config.get( 'wgCanonicalNamespace' ) === 'VisualEditor' ) {
if ( mw.config.get( 'wgAction' ) === 'view' && uri.query.diff === undefined ) {
this.setupSkinTabs();
this.setupSectionEditLinks();
if ( this.canBeActivated ) {
this.setupSkinTabs();
this.setupSectionEditLinks();
if ( this.isViewPage ) {
this.setupToolbarSaveButton();
this.setupSaveDialog();
if ( uri.query.veaction === 'edit' ) {
if ( this.currentUri.query.veaction === 'edit' ) {
this.activate();
}
} else {
this.setupSkinTabs();
}
} else if ( mw.config.get( 'wgRelevantPageName' ).substr( 0, 13 ) === 'VisualEditor:' ) {
this.setupSkinTabs();
}
};
@ -109,9 +120,10 @@ ve.init.ViewPageTarget.prototype.activate = function() {
*
* @method
*/
ve.init.ViewPageTarget.prototype.deactivate = function() {
ve.init.ViewPageTarget.prototype.deactivate = function( override ) {
if ( this.active && !this.deactivating ) {
if (
override ||
!this.surface.getModel().getHistory().length ||
confirm( 'Are you sure you want to go back to view mode without saving first?' )
) {
@ -166,7 +178,7 @@ ve.init.ViewPageTarget.prototype.onLoadError = function( response, status, error
ve.init.ViewPageTarget.prototype.onSave = function( html ) {
this.hideSaveDialog();
this.replacePageContent( html );
this.deactivate();
this.deactivate( true );
};
/**
@ -201,7 +213,7 @@ ve.init.ViewPageTarget.prototype.onEditTabClick = function( event ) {
* @param {Event} event DOM event
*/
ve.init.ViewPageTarget.prototype.onEditSectionLinkClick = function( event ) {
this.saveEditingSection( $( event.target ).closest( 'h1, h2, h3, h4, h5, h6' )[0] );
this.saveEditSection( $( event.target ).closest( 'h1, h2, h3, h4, h5, h6' )[0] );
this.activate();
// Prevent the edit tab's normal behavior
event.preventDefault();
@ -332,10 +344,12 @@ ve.init.ViewPageTarget.prototype.setupSkinTabs = function() {
// Only sysop users will have an edit tab in this namespace, so we might need to add one
if ( $( '#ca-edit' ).length === 0 ) {
// Add edit tab
var action = Number( mw.config.get( 'wgArticleId', 0 ) ) === 0 ?
'create' : 'edit';
mw.util.addPortletLink(
'p-views',
'#',
mw.msg( 'edit' ),
mw.msg( action ), // 'edit' or 'create'
'ca-edit',
mw.msg( 'tooltip-ca-edit' ),
mw.msg( 'accesskey-ca-edit' ),
@ -358,33 +372,27 @@ ve.init.ViewPageTarget.prototype.setupSkinTabs = function() {
// Sysop users will need a new edit source tab since we are highjacking the edit tab
mw.util.addPortletLink(
'p-cactions',
$( '#ca-edit a' ).attr( 'href' ),
this.editUri,
'Edit Source', // TODO: i18n
'ca-editsource'
);
}
var uri = new mw.Uri( window.location.toString() );
if (
mw.config.get( 'wgCanonicalNamespace' ) === 'VisualEditor' &&
mw.config.get( 'wgAction' ) === 'view' &&
uri.query.diff === undefined
) {
if ( this.isViewPage ) {
// Allow instant-editing
$( '#ca-edit a' ).click( ve.proxy( this.onEditTabClick, this ) );
$( '#ca-view a, #ca-nstab-visualeditor a' ).click( ve.proxy( this.onViewTabClick, this ) );
} else {
var editUri = new mw.Uri( $( '#ca-edit a' ).attr( 'href' ) );
delete editUri.query.action;
editUri.query.veaction = 'edit';
$( '#ca-edit a' ).attr( 'href', editUri );
// Route edits through the view page
$( '#ca-edit a' ).attr( 'href', this.veEditUri );
}
// Source editing shouldn't highlight the edit tab
if ( mw.config.get( 'wgAction' ) === 'edit' ) {
$( '#p-views' ).find( 'li.selected' ).removeClass( 'selected' );
}
// Fix the URL if there was a veaction param in it
if ( uri.query.veaction === 'edit' && window.history.replaceState ) {
if ( this.currentUri.query.veaction === 'edit' && window.history.replaceState ) {
var title = $( 'head title' ).text();
window.history.replaceState( null, title, $( '#ca-view a' ).attr( 'href' ) );
window.history.replaceState( null, title, this.viewUri );
}
};
@ -394,7 +402,18 @@ ve.init.ViewPageTarget.prototype.setupSkinTabs = function() {
* @method
*/
ve.init.ViewPageTarget.prototype.setupSectionEditLinks = function() {
$( '#mw-content-text .editsection a' ).click( ve.proxy( this.onEditSectionLinkClick, this ) );
var $links = $( '#mw-content-text .editsection a' );
if ( this.isViewPage ) {
$links.click( ve.proxy( this.onEditSectionLinkClick, this ) );
} else {
var veEditUri = this.veEditUri;
$links.each( function() {
var veSectionEditUri = new mw.Uri( veEditUri.toString() ),
sectionEditUri = new mw.Uri( $(this).attr( 'href' ) );
veSectionEditUri.extend( { 'vesection': sectionEditUri.query.section } );
$(this).attr( 'href', veSectionEditUri );
} );
}
};
/**
@ -549,7 +568,10 @@ ve.init.ViewPageTarget.prototype.hideSpinner = function() {
* @method
*/
ve.init.ViewPageTarget.prototype.showPageContent = function() {
$( '#bodyContent' ).children().not( '#siteSub' ).show().fadeTo( 0, 1 );
$( '#bodyContent .ve-init-viewPageTarget-content:not(#siteSub)' )
.removeClass( 've-init-viewPageTarget-content' )
.show()
.fadeTo( 0, 1 );
};
/**
@ -558,7 +580,9 @@ ve.init.ViewPageTarget.prototype.showPageContent = function() {
* @method
*/
ve.init.ViewPageTarget.prototype.mutePageContent = function() {
$( '#bodyContent' ).children().not( '#siteSub' ).fadeTo( 'fast', 0.25 );
$( '#bodyContent :visible:not(#siteSub)' )
.addClass( 've-init-viewPageTarget-content' )
.fadeTo( 'fast', 0.6 );
};
/**
@ -567,7 +591,9 @@ ve.init.ViewPageTarget.prototype.mutePageContent = function() {
* @method
*/
ve.init.ViewPageTarget.prototype.hidePageContent = function() {
$( '#bodyContent' ).children().not( '#siteSub' ).hide();
$( '#bodyContent :visible:not(#siteSub)' )
.addClass( 've-init-viewPageTarget-content' )
.hide();
};
/**
@ -667,8 +693,7 @@ ve.init.ViewPageTarget.prototype.transformPageTitle = function() {
* @method
*/
ve.init.ViewPageTarget.prototype.mutePageTitle = function() {
$( '#firstHeading' ).fadeTo( 'fast', 0.25 );
$( '#siteSub' ).fadeTo( 'fast', 0.25 );
$( '#firstHeading, #siteSub' ).fadeTo( 'fast', 0.6 );
};
/**
@ -677,8 +702,7 @@ ve.init.ViewPageTarget.prototype.mutePageTitle = function() {
* @method
*/
ve.init.ViewPageTarget.prototype.restorePageTitle = function() {
$( '#firstHeading' ).fadeTo( 'fast', 1 );
$( '#siteSub' ).fadeTo( 'fast', 1 );
$( '#firstHeading, #siteSub' ).fadeTo( 'fast', 1 );
setTimeout( function() {
$( '#firstHeading' ).removeClass( 've-init-viewPageTarget-pageTitle' );
}, 1000 );
@ -739,19 +763,26 @@ ve.init.ViewPageTarget.prototype.detachSurface = function() {
* @method
* @param {HTMLElement} heading Heading element of section
*/
ve.init.ViewPageTarget.prototype.saveEditSection = function( heading ) {
var $page = $( '#mw-content-text' );
tocHeading = $page.find( '#toc h2' )[0];
ve.init.ViewPageTarget.prototype.getEditSection = function( heading ) {
var $page = $( '#mw-content-text' ),
section = 0;
$page.find( 'h1, h2, h3, h4, h5, h6' ).each( function() {
$page.find( 'h1, h2, h3, h4, h5, h6' ).not( '#toc h2' ).each( function() {
section++;
if ( this === heading ) {
return false;
}
if ( this !== tocHeading ) {
section++;
}
} );
this.section = section;
return section;
};
/**
* Gets the numeric index of a section in the page.
*
* @method
* @param {HTMLElement} heading Heading element of section
*/
ve.init.ViewPageTarget.prototype.saveEditSection = function( heading ) {
this.section = this.getEditSection( heading );
};
/**
@ -761,20 +792,20 @@ ve.init.ViewPageTarget.prototype.saveEditSection = function( heading ) {
* @param {Number} section Section to move cursor to
*/
ve.init.ViewPageTarget.prototype.restoreEditSection = function() {
if ( this.section ) {
if ( this.section !== null ) {
var surfaceView = this.surface.getView(),
surfaceModel = surfaceView.getModel();
this.$surface
.find( '.ve-ce-documentNode' )
.find( 'h1, h2, h3, h4, h5, h6' )
.eq( this.section )
.eq( this.section - 1 )
.each( function() {
var headingNode = $(this).data( 'node' );
if ( headingNode ) {
var offset = surfaceModel.getDocument().getNearestContentOffset(
headingNode.getModel().getOffset()
);
surfaceModel.setSelection( new ve.Range( offset, offset ) );
surfaceModel.change( null, new ve.Range( offset, offset ) );
surfaceView.showSelection( surfaceModel.getSelection() );
}
} );

View file

@ -3,14 +3,14 @@
*
* @class
* @constructor
* @param {String} title Page title of target
* @param {String} pageName Name of target page
*/
ve.init.Target = function( title ) {
ve.init.Target = function( pageName ) {
// Inheritance
ve.EventEmitter.call( this );
// Properties
this.title = title;
this.pageName = pageName;
this.editToken = mw.user.tokens.get( 'editToken' );
this.apiUrl = mw.util.wikiScript( 'api' );
this.modules = ['ext.visualEditor.core'];
@ -101,7 +101,7 @@ ve.init.Target.onLoadError = function( response, text, exception ) {
ve.init.Target.onSave = function( response, status ) {
this.saving = false;
var data = response['ve-parsoid'];
if ( !response ) {
if ( !data ) {
this.emit( 'saveError', 'Invalid response from server' );
} else if ( data.result !== 'success' ) {
this.emit( 'saveError', 'Unsuccessful request: ' + data.result );
@ -161,7 +161,7 @@ ve.init.Target.prototype.load = function( callback ) {
'data': {
'action': 've-parsoid',
'paction': 'parse',
'page': this.title,
'page': this.pageName,
'format': 'json'
},
'dataType': 'json',
@ -211,7 +211,7 @@ ve.init.Target.prototype.save = function( dom, options, callback ) {
'format': 'json',
'action': 've-parsoid',
'paction': 'save',
'page': this.title,
'page': this.pageName,
'html': $( dom ).html(),
'token': this.editToken,
'summary': options.summary,

View file

@ -74,7 +74,7 @@ ve.FormatDropdownTool.prototype.onSelect = function( item ) {
item.type,
item.attributes
);
model.transact ( txs );
model.change( txs );
surfaceView.showSelection( selection );
};

View file

@ -50,7 +50,7 @@ ve.ui.IndentationButtonTool.prototype.indent = function( listItems ) {
'styles',
styles.concat( styles[styles.length - 1] )
);
surface.model.transact( tx );
surface.model.change( tx );
}
}
surface.emitCursor();
@ -69,7 +69,7 @@ ve.ui.IndentationButtonTool.prototype.outdent = function( listItems ) {
'styles',
styles.slice( 0, styles.length - 1 )
);
surface.model.transact( tx );
surface.model.change( tx );
}
}
surface.emitCursor();

View file

@ -83,7 +83,7 @@ ve.ui.ListButtonTool.prototype.unlist = function( node ){
[]
);
model.transact ( tx );
model.change( tx );
};
ve.ui.ListButtonTool.prototype.onClick = function() {

View file

@ -35,17 +35,19 @@ test( 'setSelection', 1, function() {
surface.setSelection( new ve.Range( 1, 1 ) );
} );
test( 'transact', 1, function() {
test( 'change', 2, function() {
var surface = new ve.dm.SurfaceStub();
var tx = new ve.dm.Transaction();
surface.on( 'transact', function() {
ok( true, 'transact was emitted' );
} );
surface.transact( tx );
surface.on( 'change', function() {
ok( true, 'change was emitted' );
} );
surface.change( tx );
} );
test('annotate', 1, function(){
test( 'annotate', 1, function() {
var surface,
cases = [
{
@ -105,4 +107,4 @@ test('annotate', 1, function(){
);
}
});
} );