mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-11-14 01:57:02 +00:00
916 lines
34 KiB
PHP
916 lines
34 KiB
PHP
<?php
|
|
/**
|
|
* VisualEditor extension hooks
|
|
*
|
|
* @file
|
|
* @ingroup Extensions
|
|
* @copyright 2011-2015 VisualEditor Team and others; see AUTHORS.txt
|
|
* @license The MIT License (MIT); see LICENSE.txt
|
|
*/
|
|
|
|
class VisualEditorHooks {
|
|
/**
|
|
* Initialise the 'VisualEditorAvailableNamespaces' setting, and add content
|
|
* namespaces to it. This will run after LocalSettings.php is processed.
|
|
*/
|
|
public static function onRegistration() {
|
|
global $wgVisualEditorAvailableNamespaces, $wgContentNamespaces;
|
|
|
|
foreach ( $wgContentNamespaces as $contentNamespace ) {
|
|
if ( !isset( $wgVisualEditorAvailableNamespaces[$contentNamespace] ) ) {
|
|
$wgVisualEditorAvailableNamespaces[$contentNamespace] = true;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
public static function VisualEditorApiFactory( $main, $name ) {
|
|
$config = ConfigFactory::getDefaultInstance()->makeConfig( 'visualeditor' );
|
|
$class = $name === 'visualeditor' ? 'ApiVisualEditor' : 'ApiVisualEditorEdit';
|
|
return new $class( $main, $name, $config );
|
|
}
|
|
|
|
|
|
/**
|
|
* Adds VisualEditor JS to the output.
|
|
*
|
|
* This is attached to the MediaWiki 'BeforePageDisplay' hook.
|
|
*
|
|
* @param OutputPage $output
|
|
* @param Skin $skin
|
|
* @return boolean
|
|
*/
|
|
public static function onBeforePageDisplay( OutputPage &$output, Skin &$skin ) {
|
|
$output->addModules( array(
|
|
'ext.visualEditor.desktopArticleTarget.init',
|
|
'ext.visualEditor.targetLoader'
|
|
) );
|
|
$output->addModuleStyles( array( 'ext.visualEditor.desktopArticleTarget.noscript' ) );
|
|
// add scroll offset js variable to output
|
|
$veConfig = ConfigFactory::getDefaultInstance()->makeConfig( 'visualeditor' );
|
|
$skinsToolbarScrollOffset = $veConfig->get( 'VisualEditorSkinToolbarScrollOffset' );
|
|
$toolbarScrollOffset = 0;
|
|
$skinName = $skin->getSkinName();
|
|
if ( isset( $skinsToolbarScrollOffset[$skinName] ) ) {
|
|
$toolbarScrollOffset = $skinsToolbarScrollOffset[$skinName];
|
|
}
|
|
$output->addJsConfigVars( 'wgVisualEditorToolbarScrollOffset', $toolbarScrollOffset );
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Decide whether to bother showing the wikitext editor at all.
|
|
* If not, we expect the VE initialisation JS to activate.
|
|
* @param $article Article
|
|
* @param $user User
|
|
* @return bool Whether to show the wikitext editor or not.
|
|
*/
|
|
public static function onCustomEditor( Article $article, User $user ) {
|
|
$req = RequestContext::getMain()->getRequest();
|
|
$veConfig = ConfigFactory::getDefaultInstance()->makeConfig( 'visualeditor' );
|
|
|
|
if (
|
|
!$user->getOption( 'visualeditor-enable' ) ||
|
|
$user->getOption( 'visualeditor-betatempdisable' ) ||
|
|
$user->getOption( 'visualeditor-autodisable' ) ||
|
|
$user->getOption( 'visualeditor-tabs' ) === 'preferwt' ||
|
|
( $veConfig->get( 'VisualEditorDisableForAnons' ) && $user->isAnon() ) ||
|
|
false // TODO: Detect incompatibility - P2373
|
|
) {
|
|
return true;
|
|
}
|
|
|
|
$title = $article->getTitle();
|
|
|
|
$availableNamespaces = $veConfig->get( 'VisualEditorAvailableNamespaces' );
|
|
|
|
$params = $req->getValueNames();
|
|
|
|
if ( $user->isAnon() ) {
|
|
$editor = $req->getCookie(
|
|
'VEE',
|
|
'',
|
|
User::getDefaultOption( 'visualeditor-editor' )
|
|
);
|
|
} else {
|
|
$editor = $user->getOption( 'visualeditor-editor' );
|
|
}
|
|
return $req->getVal( 'action' ) !== 'edit' ||
|
|
!$veConfig->get( 'VisualEditorUseSingleEditTab' ) ||
|
|
$editor === 'wikitext' ||
|
|
!$title->inNamespaces( array_keys( array_filter( $availableNamespaces ) ) ) ||
|
|
$title->getContentModel() !== CONTENT_MODEL_WIKITEXT ||
|
|
// check for parameters that VE does not handle
|
|
in_array( 'preload', $params ) ||
|
|
in_array( 'editintro', $params ) ||
|
|
in_array( 'veswitched', $params );
|
|
}
|
|
|
|
/**
|
|
* Convert the content model of messages that are actually JSON to JSON.
|
|
* This only affects validation and UI when saving and editing, not
|
|
* loading the content.
|
|
*
|
|
* @param Title $title
|
|
* @param string $model
|
|
* @return bool
|
|
*/
|
|
public static function onContentHandlerDefaultModelFor( Title $title, &$model ) {
|
|
$messages = array(
|
|
'Visualeditor-cite-tool-definition.json',
|
|
'Visualeditor-quick-access-characters.json'
|
|
);
|
|
|
|
if ( $title->inNamespace( NS_MEDIAWIKI ) && in_array( $title->getText(), $messages ) ) {
|
|
$model = CONTENT_MODEL_JSON;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Changes the Edit tab and adds the VisualEditor tab.
|
|
*
|
|
* This is attached to the MediaWiki 'SkinTemplateNavigation' hook.
|
|
*
|
|
* @param SkinTemplate $skin
|
|
* @param array $links Navigation links
|
|
* @return boolean
|
|
*/
|
|
public static function onSkinTemplateNavigation( SkinTemplate &$skin, array &$links ) {
|
|
$config = ConfigFactory::getDefaultInstance()->makeConfig( 'visualeditor' );
|
|
|
|
// Exit if there's no edit link for whatever reason (e.g. protected page)
|
|
if ( !isset( $links['views']['edit'] ) ) {
|
|
return true;
|
|
}
|
|
|
|
// Exit if we're using the single edit tab.
|
|
if (
|
|
$config->get( 'VisualEditorUseSingleEditTab' ) &&
|
|
$skin->getUser()->getOption( 'visualeditor-tabs' ) !== 'multi-tab'
|
|
) {
|
|
$dbr = wfGetDB( DB_SLAVE );
|
|
$user = RequestContext::getMain()->getUser();
|
|
if (
|
|
$config->get( 'VisualEditorUseSingleEditTab' ) &&
|
|
!$user->isAnon() &&
|
|
!$user->getOption( 'visualeditor-hidetabdialog' ) &&
|
|
$user->getOption( 'visualeditor-tabs' ) === 'remember-last' &&
|
|
$dbr->select(
|
|
'revision',
|
|
'1',
|
|
array(
|
|
'rev_user' => $user->getId(),
|
|
'rev_timestamp < ' . $dbr->addQuotes(
|
|
$config->get( 'VisualEditorSingleEditTabSwitchTime' )
|
|
)
|
|
),
|
|
__METHOD__,
|
|
array( 'LIMIT' => 1 )
|
|
)->numRows() === 1
|
|
) {
|
|
wfDebugLog( 'debug', 'ok' );
|
|
$links['views']['edit']['class'] .= ' visualeditor-showtabdialog';
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Exit if the user doesn't have VE enabled
|
|
if (
|
|
!$skin->getUser()->getOption( 'visualeditor-enable' ) ||
|
|
$skin->getUser()->getOption( 'visualeditor-betatempdisable' ) ||
|
|
$skin->getUser()->getOption( 'visualeditor-autodisable' ) ||
|
|
( $config->get( 'VisualEditorDisableForAnons' ) && $skin->getUser()->isAnon() )
|
|
) {
|
|
return true;
|
|
}
|
|
|
|
$availableNamespaces = $config->get( 'VisualEditorAvailableNamespaces' );
|
|
$title = $skin->getRelevantTitle();
|
|
$namespaceEnabled = $title->inNamespaces( array_keys( array_filter( $availableNamespaces ) ) );
|
|
$pageContentModel = $title->getContentModel();
|
|
// Don't exit if this page isn't VE-enabled, since we should still
|
|
// change "Edit" to "Edit source".
|
|
$isAvailable = (
|
|
$namespaceEnabled &&
|
|
$pageContentModel === CONTENT_MODEL_WIKITEXT
|
|
);
|
|
|
|
// HACK: Exit if we're in the Education Program namespace (even though it's content)
|
|
if ( defined( 'EP_NS' ) && $title->inNamespace( EP_NS ) ) {
|
|
return true;
|
|
}
|
|
|
|
$tabMessages = $config->get( 'VisualEditorTabMessages' );
|
|
// Rebuild the $links['views'] array and inject the VisualEditor tab before or after
|
|
// the edit tab as appropriate. We have to rebuild the array because PHP doesn't allow
|
|
// us to splice into the middle of an associative array.
|
|
$newViews = array();
|
|
foreach ( $links['views'] as $action => $data ) {
|
|
if ( $action === 'edit' ) {
|
|
// Build the VisualEditor tab
|
|
$existing = $title->exists() || (
|
|
$title->inNamespace( NS_MEDIAWIKI ) &&
|
|
$title->getDefaultMessageText() !== false
|
|
);
|
|
$action = $existing ? 'edit' : 'create';
|
|
$veParams = $skin->editUrlOptions();
|
|
unset( $veParams['action'] ); // Remove action=edit
|
|
$veParams['veaction'] = 'edit'; // Set veaction=edit
|
|
$veTabMessage = $tabMessages[$action];
|
|
$veTabText = $veTabMessage === null ? $data['text'] :
|
|
$skin->msg( $veTabMessage )->text();
|
|
$veTab = array(
|
|
'href' => $title->getLocalURL( $veParams ),
|
|
'text' => $veTabText,
|
|
'primary' => true,
|
|
'class' => '',
|
|
);
|
|
|
|
// Alter the edit tab
|
|
$editTab = $data;
|
|
if (
|
|
$title->inNamespace( NS_FILE ) &&
|
|
WikiPage::factory( $title ) instanceof WikiFilePage &&
|
|
!WikiPage::factory( $title )->isLocal()
|
|
) {
|
|
$editTabMessage = $tabMessages[$action . 'localdescriptionsource'];
|
|
} else {
|
|
$editTabMessage = $tabMessages[$action . 'source'];
|
|
}
|
|
|
|
if ( $editTabMessage !== null ) {
|
|
$editTab['text'] = $skin->msg( $editTabMessage )->text();
|
|
}
|
|
if ( $isAvailable ) {
|
|
// Inject the VE tab before or after the edit tab
|
|
if ( $config->get( 'VisualEditorTabPosition' ) === 'before' ) {
|
|
$editTab['class'] .= ' collapsible';
|
|
$newViews['ve-edit'] = $veTab;
|
|
$newViews['edit'] = $editTab;
|
|
} else {
|
|
$veTab['class'] .= ' collapsible';
|
|
$newViews['edit'] = $editTab;
|
|
$newViews['ve-edit'] = $veTab;
|
|
}
|
|
} else {
|
|
// Don't add ve-edit, but do update the edit tab (e.g. "Edit source").
|
|
$newViews['edit'] = $editTab;
|
|
}
|
|
} else {
|
|
// Just pass through
|
|
$newViews[$action] = $data;
|
|
}
|
|
}
|
|
$links['views'] = $newViews;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Called when the normal wikitext editor is shown.
|
|
* Inserts a 'veswitched' hidden field if requested by the client
|
|
*
|
|
* @param $editPage EditPage
|
|
* @param $output OutputPage
|
|
* @return boolean true
|
|
*/
|
|
public static function onEditPageShowEditFormFields( EditPage $editPage, OutputPage $output ) {
|
|
$request = $output->getRequest();
|
|
if ( $request->getBool( 'veswitched' ) ) {
|
|
$output->addHTML( Xml::input( 'veswitched', false, '1', array( 'type' => 'hidden' ) ) );
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Called when an edit is saved
|
|
* Adds 'visualeditor-switched' tag to the edit if requested
|
|
*
|
|
* @param RecentChange $rc
|
|
* @return boolean true
|
|
*/
|
|
public static function onRecentChange_save( RecentChange $rc ) {
|
|
$request = RequestContext::getMain()->getRequest();
|
|
if ( $request->getBool( 'veswitched' ) && $rc->mAttribs['rc_this_oldid'] ) {
|
|
ChangeTags::addTags( 'visualeditor-switched',
|
|
$rc->mAttribs['rc_id'], $rc->mAttribs['rc_this_oldid'] );
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Changes the section edit links to add a VE edit link.
|
|
*
|
|
* This is attached to the MediaWiki 'SkinEditSectionLinks' hook.
|
|
*
|
|
* @param $skin Skin
|
|
* @param $title Title
|
|
* @param $section string
|
|
* @param $tooltip string
|
|
* @param $result array
|
|
* @param $lang Language
|
|
* @return bool true
|
|
*/
|
|
public static function onSkinEditSectionLinks( Skin $skin, Title $title, $section,
|
|
$tooltip, &$result, $lang
|
|
) {
|
|
$config = ConfigFactory::getDefaultInstance()->makeConfig( 'visualeditor' );
|
|
|
|
// Exit if we're using the single edit tab.
|
|
if (
|
|
$config->get( 'VisualEditorUseSingleEditTab' ) &&
|
|
$skin->getUser()->getOption( 'visualeditor-tabs' ) !== 'multi-tab'
|
|
) {
|
|
return true;
|
|
}
|
|
|
|
// Exit if we're in parserTests
|
|
if ( isset( $GLOBALS[ 'wgVisualEditorInParserTests' ] ) ) {
|
|
return true;
|
|
}
|
|
|
|
// Exit if the user doesn't have VE enabled
|
|
if (
|
|
!$skin->getUser()->getOption( 'visualeditor-enable' ) ||
|
|
$skin->getUser()->getOption( 'visualeditor-betatempdisable' ) ||
|
|
$skin->getUser()->getOption( 'visualeditor-autodisable' ) ||
|
|
( $config->get( 'VisualEditorDisableForAnons' ) && $skin->getUser()->isAnon() )
|
|
) {
|
|
return true;
|
|
}
|
|
|
|
// Exit if we're on a foreign file description page
|
|
if (
|
|
$title->inNamespace( NS_FILE ) &&
|
|
WikiPage::factory( $title ) instanceof WikiFilePage &&
|
|
!WikiPage::factory( $title )->isLocal()
|
|
) {
|
|
return true;
|
|
}
|
|
|
|
$tabMessages = $config->get( 'VisualEditorTabMessages' );
|
|
$sourceEditSection = $tabMessages['editsectionsource'] !== null ?
|
|
$tabMessages['editsectionsource'] : 'editsection';
|
|
|
|
$result['editsection']['text'] = $skin->msg( $sourceEditSection )->inLanguage( $lang )->text();
|
|
|
|
$availableNamespaces = $config->get( 'VisualEditorAvailableNamespaces' );
|
|
|
|
// add VE edit section in VE available namespaces
|
|
if ( $title->inNamespaces( array_keys( array_filter( $availableNamespaces ) ) ) ) {
|
|
$veEditSection = $tabMessages['editsection'] !== null ?
|
|
$tabMessages['editsection'] : 'editsection';
|
|
$veLink = array(
|
|
'text' => $skin->msg( $veEditSection )->inLanguage( $lang )->text(),
|
|
'targetTitle' => $title,
|
|
'attribs' => $result['editsection']['attribs'] + array(
|
|
'class' => 'mw-editsection-visualeditor'
|
|
),
|
|
'query' => array( 'veaction' => 'edit', 'vesection' => $section ),
|
|
'options' => array( 'noclasses', 'known' )
|
|
);
|
|
|
|
$result['veeditsection'] = $veLink;
|
|
if ( $config->get( 'VisualEditorTabPosition' ) === 'before' ) {
|
|
krsort( $result );
|
|
// TODO: This will probably cause weird ordering if any other extensions added something
|
|
// already.
|
|
// ... wfArrayInsertBefore?
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Convert a namespace index to the local text for display to the user.
|
|
*
|
|
* @param $nsIndex int
|
|
* @return string
|
|
*/
|
|
private static function convertNs( $nsIndex ) {
|
|
global $wgLang;
|
|
if ( $nsIndex ) {
|
|
return $wgLang->convertNamespace( $nsIndex );
|
|
} else {
|
|
return wfMessage( 'blanknamespace' )->text();
|
|
}
|
|
}
|
|
|
|
public static function onGetPreferences( User $user, array &$preferences ) {
|
|
global $wgLang;
|
|
if ( !class_exists( 'BetaFeatures' ) ) {
|
|
$namespaces = ConfigFactory::getDefaultInstance()
|
|
->makeConfig( 'visualeditor' )
|
|
->get( 'VisualEditorAvailableNamespaces' );
|
|
$onNamespaces = array_keys( array_filter( $namespaces ) );
|
|
|
|
$enablePreference = array(
|
|
'type' => 'toggle',
|
|
'label-message' => array(
|
|
'visualeditor-preference-enable',
|
|
$wgLang->commaList( array_map(
|
|
array( 'self', 'convertNs' ),
|
|
$onNamespaces
|
|
) ),
|
|
count( $onNamespaces )
|
|
),
|
|
'section' => 'editing/editor'
|
|
);
|
|
if ( $user->getOption( 'visualeditor-autodisable' ) ) {
|
|
$enablePreference['default'] = false;
|
|
}
|
|
$preferences['visualeditor-enable'] = $enablePreference;
|
|
}
|
|
$preferences['visualeditor-betatempdisable'] = array(
|
|
'type' => 'toggle',
|
|
'label-message' => 'visualeditor-preference-betatempdisable',
|
|
'section' => 'editing/editor',
|
|
'default' => $user->getOption( 'visualeditor-betatempdisable' ) ||
|
|
$user->getOption( 'visualeditor-autodisable' )
|
|
);
|
|
|
|
$config = ConfigFactory::getDefaultInstance()->makeConfig( 'visualeditor' );
|
|
if ( $config->get( 'VisualEditorUseSingleEditTab' ) ) {
|
|
$preferences['visualeditor-tabs'] = array(
|
|
'type' => 'select',
|
|
'label-message' => 'visualeditor-preference-tabs',
|
|
'section' => 'editing/editor',
|
|
'options' => array(
|
|
wfMessage( 'visualeditor-preference-tabs-remember-last' )->escaped() => 'remember-last',
|
|
wfMessage( 'visualeditor-preference-tabs-prefer-ve' )->escaped() => 'prefer-ve',
|
|
wfMessage( 'visualeditor-preference-tabs-prefer-wt' )->escaped() => 'prefer-wt',
|
|
wfMessage( 'visualeditor-preference-tabs-multi-tab' )->escaped() => 'multi-tab'
|
|
)
|
|
);
|
|
}
|
|
|
|
$api = array( 'type' => 'api' );
|
|
$preferences['visualeditor-autodisable'] = $api;
|
|
$preferences['visualeditor-editor'] = $api;
|
|
$preferences['visualeditor-hidebetawelcome'] = $api;
|
|
$preferences['visualeditor-hidetabdialog'] = $api;
|
|
$preferences['visualeditor-hideusered'] = $api;
|
|
$preferences['visualeditor-findAndReplace-findText'] = $api;
|
|
$preferences['visualeditor-findAndReplace-replaceText'] = $api;
|
|
$preferences['visualeditor-findAndReplace-regex'] = $api;
|
|
$preferences['visualeditor-findAndReplace-matchCase'] = $api;
|
|
$preferences['visualeditor-findAndReplace-word'] = $api;
|
|
return true;
|
|
}
|
|
|
|
public static function onGetBetaPreferences( User $user, array &$preferences ) {
|
|
$coreConfig = RequestContext::getMain()->getConfig();
|
|
$iconpath = $coreConfig->get( 'ExtensionAssetsPath' ) . "/VisualEditor";
|
|
|
|
$veConfig = ConfigFactory::getDefaultInstance()->makeConfig( 'visualeditor' );
|
|
$preferences['visualeditor-enable'] = array(
|
|
'version' => '1.0',
|
|
'label-message' => 'visualeditor-preference-core-label',
|
|
'desc-message' => 'visualeditor-preference-core-description',
|
|
'screenshot' => array(
|
|
'ltr' => "$iconpath/betafeatures-icon-VisualEditor-ltr.svg",
|
|
'rtl' => "$iconpath/betafeatures-icon-VisualEditor-rtl.svg",
|
|
),
|
|
'info-message' => 'visualeditor-preference-core-info-link',
|
|
'discussion-message' => 'visualeditor-preference-core-discussion-link',
|
|
'requirements' => array(
|
|
'javascript' => true,
|
|
'blacklist' => $veConfig->get( 'VisualEditorBrowserBlacklist' ),
|
|
'skins' => $veConfig->get( 'VisualEditorSupportedSkins' ),
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Implements the PreferencesFormPreSave hook, to remove the 'autodisable' flag
|
|
* when the user it was set on explicitly enables VE.
|
|
* @param array $data User-submitted data
|
|
* @param PreferencesForm $form A ContextSource
|
|
* @param User $user User with new preferences already set
|
|
* @param bool &$result Success or failure
|
|
*/
|
|
public static function onPreferencesFormPreSave( $data, $form, $user, &$result ) {
|
|
$veConfig = ConfigFactory::getDefaultInstance()->makeConfig( 'visualeditor' );
|
|
// On a wiki where enable is hidden and set to 1, if user sets betatempdisable=0
|
|
// then set autodisable=0
|
|
// On a wiki where betatempdisable is hidden and set to 0, if user sets enable=1
|
|
// then set autodisable=0
|
|
if (
|
|
$user->getOption( 'visualeditor-autodisable' ) &&
|
|
$user->getOption( 'visualeditor-enable' ) &&
|
|
!$user->getOption( 'visualeditor-betatempdisable' )
|
|
) {
|
|
$user->setOption( 'visualeditor-autodisable', false );
|
|
} elseif (
|
|
// On a wiki where betatempdisable is hidden and set to 0, if user sets enable=0,
|
|
// then set autodisable=1
|
|
$veConfig->get( 'VisualEditorTransitionDefault' ) &&
|
|
!$user->getOption( 'visualeditor-betatempdisable' ) &&
|
|
!$user->getOption( 'visualeditor-enable' ) &&
|
|
!$user->getOption( 'visualeditor-autodisable' )
|
|
) {
|
|
$user->setOption( 'visualeditor-autodisable', true );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implements the ListDefinedTags and ChangeTagsListActive hooks, to populate
|
|
* core Special:Tags with the change tags in use by VisualEditor.
|
|
*
|
|
* @param array $tags
|
|
* @return bool true
|
|
*/
|
|
public static function onListDefinedTags( &$tags ) {
|
|
$tags[] = 'visualeditor';
|
|
$tags[] = 'visualeditor-needcheck'; // No longer in active use
|
|
$tags[] = 'visualeditor-switched';
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Adds extra variables to the page config.
|
|
*/
|
|
public static function onMakeGlobalVariablesScript( array &$vars, OutputPage $out ) {
|
|
$pageLanguage = $out->getTitle()->getPageLanguage();
|
|
|
|
$vars['wgVisualEditor'] = array(
|
|
'pageLanguageCode' => $pageLanguage->getHtmlCode(),
|
|
'pageLanguageDir' => $pageLanguage->getDir(),
|
|
'usePageImages' => defined( 'PAGE_IMAGES_INSTALLED' ),
|
|
'usePageDescriptions' => defined( 'WBC_VERSION' ),
|
|
);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Adds extra variables to the global config
|
|
*/
|
|
public static function onResourceLoaderGetConfigVars( array &$vars ) {
|
|
$coreConfig = RequestContext::getMain()->getConfig();
|
|
$defaultUserOptions = $coreConfig->get( 'DefaultUserOptions' );
|
|
$thumbLimits = $coreConfig->get( 'ThumbLimits' );
|
|
$veConfig = ConfigFactory::getDefaultInstance()->makeConfig( 'visualeditor' );
|
|
$availableNamespaces = $veConfig->get( 'VisualEditorAvailableNamespaces' );
|
|
$enabledNamespaces = array_keys( array_filter( $availableNamespaces ) );
|
|
|
|
$vars['wgVisualEditorConfig'] = array(
|
|
'disableForAnons' => $veConfig->get( 'VisualEditorDisableForAnons' ),
|
|
'preferenceModules' => $veConfig->get( 'VisualEditorPreferenceModules' ),
|
|
'namespaces' => $enabledNamespaces,
|
|
'signatureNamespaces' => array_values(
|
|
array_filter( $enabledNamespaces, 'MWNamespace::wantSignatures' )
|
|
),
|
|
'pluginModules' => array_merge(
|
|
ExtensionRegistry::getInstance()->getAttribute( 'VisualEditorPluginModules' ),
|
|
$veConfig->get( 'VisualEditorPluginModules' ) // @todo deprecate the global setting
|
|
),
|
|
'defaultUserOptions' => array(
|
|
'defaultthumbsize' => $thumbLimits[ $defaultUserOptions['thumbsize'] ]
|
|
),
|
|
'blacklist' => $veConfig->get( 'VisualEditorBrowserBlacklist' ),
|
|
'skins' => $veConfig->get( 'VisualEditorSupportedSkins' ),
|
|
'tabPosition' => $veConfig->get( 'VisualEditorTabPosition' ),
|
|
'tabMessages' => $veConfig->get( 'VisualEditorTabMessages' ),
|
|
'singleEditTab' => $veConfig->get( 'VisualEditorUseSingleEditTab' ),
|
|
'showBetaWelcome' => $veConfig->get( 'VisualEditorShowBetaWelcome' ),
|
|
'enableTocWidget' => $veConfig->get( 'VisualEditorEnableTocWidget' ),
|
|
'svgMaxSize' => $coreConfig->get( 'SVGMaxSize' ),
|
|
'namespacesWithSubpages' => $coreConfig->get( 'NamespacesWithSubpages' ),
|
|
'specialBooksources' => urldecode( SpecialPage::getTitleFor( 'Booksources' )->getPrefixedURL() ),
|
|
'restbaseUrl' => $coreConfig->get( 'VisualEditorRestbaseURL' ),
|
|
'fullRestbaseUrl' => $coreConfig->get( 'VisualEditorFullRestbaseURL' ),
|
|
);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Conditionally register the jquery.uls.data and jquery.i18n modules, in case they've already
|
|
* been registered by the UniversalLanguageSelector extension or the TemplateData extension.
|
|
*
|
|
* @param ResourceLoader $resourceLoader
|
|
* @return boolean true
|
|
*/
|
|
public static function onResourceLoaderRegisterModules( ResourceLoader &$resourceLoader ) {
|
|
$resourceModules = $resourceLoader->getConfig()->get( 'ResourceModules' );
|
|
|
|
$veResourceTemplate = array(
|
|
'localBasePath' => __DIR__,
|
|
'remoteExtPath' => 'VisualEditor',
|
|
);
|
|
|
|
// Only pull in VisualEditor core's local version of jquery.uls.data if it hasn't been
|
|
// installed locally already (presumably, by the UniversalLanguageSelector extension).
|
|
if (
|
|
!isset( $resourceModules[ 'jquery.uls.data' ] ) &&
|
|
!$resourceLoader->isModuleRegistered( 'jquery.uls.data' )
|
|
) {
|
|
$resourceLoader->register( array(
|
|
'jquery.uls.data' => $veResourceTemplate + array(
|
|
'scripts' => array(
|
|
'lib/ve/lib/jquery.uls/src/jquery.uls.data.js',
|
|
'lib/ve/lib/jquery.uls/src/jquery.uls.data.utils.js',
|
|
),
|
|
'targets' => array( 'desktop', 'mobile' ),
|
|
) ) );
|
|
}
|
|
|
|
// Register ext.visualEditor.mwreference here, as it depends on the new
|
|
// Cite CSS style module ext.cite.style only if the Cite extension is
|
|
// present.
|
|
// This is a temporary hack, once the Cite extension uses the new CSS
|
|
// for everything (and it takes care of loading ext.cite.style itself),
|
|
// it can be removed from here and put back in extension.json.
|
|
$mwreferenceModule = $veResourceTemplate + array(
|
|
'scripts' => array(
|
|
'modules/ve-mw/ui/widgets/ve.ui.MWReferenceGroupInputWidget.js',
|
|
'modules/ve-mw/ui/widgets/ve.ui.MWReferenceSearchWidget.js',
|
|
'modules/ve-mw/ui/widgets/ve.ui.MWReferenceResultWidget.js',
|
|
|
|
'modules/ve-mw/ui/commands/ve.ui.MWUseExistingReferenceCommand.js',
|
|
|
|
'modules/ve-mw/ui/dialogs/ve.ui.MWCitationDialog.js',
|
|
'modules/ve-mw/ui/dialogs/ve.ui.MWReferencesListDialog.js',
|
|
'modules/ve-mw/ui/dialogs/ve.ui.MWReferenceDialog.js',
|
|
|
|
'modules/ve-mw/ui/tools/ve.ui.MWReferenceDialogTool.js',
|
|
'modules/ve-mw/ui/tools/ve.ui.MWCitationDialogTool.js',
|
|
|
|
'modules/ve-mw/ui/contextitems/ve.ui.MWReferenceContextItem.js',
|
|
'modules/ve-mw/ui/contextitems/ve.ui.MWReferencesListContextItem.js',
|
|
'modules/ve-mw/ui/contextitems/ve.ui.MWCitationContextItem.js',
|
|
'modules/ve-mw/ui/actions/ve.ui.MWCitationAction.js',
|
|
),
|
|
'styles' => array(
|
|
'modules/ve-mw/ui/styles/contextitems/ve.ui.MWReferenceContextItem.css',
|
|
'modules/ve-mw/ui/styles/widgets/ve.ui.MWReferenceGroupInputWidget.css',
|
|
'modules/ve-mw/ui/styles/widgets/ve.ui.MWReferenceResultWidget.css',
|
|
'modules/ve-mw/ui/styles/widgets/ve.ui.MWReferenceSearchWidget.css',
|
|
),
|
|
'dependencies' => array(
|
|
'ext.visualEditor.mwreference.core',
|
|
'ext.visualEditor.mwtransclusion',
|
|
'ext.visualEditor.mediawiki'
|
|
),
|
|
'messages' => array(
|
|
'visualeditor-dialog-reference-editing-reused',
|
|
'visualeditor-dialog-reference-options-group-label',
|
|
'visualeditor-dialog-reference-options-group-placeholder',
|
|
'visualeditor-dialog-reference-options-name-label',
|
|
'visualeditor-dialog-reference-options-section',
|
|
'visualeditor-dialog-reference-title',
|
|
'visualeditor-dialog-reference-useexisting-full-label',
|
|
'visualeditor-dialog-reference-useexisting-label',
|
|
'visualeditor-dialog-reference-useexisting-tool',
|
|
'visualeditor-dialog-referenceslist-contextitem-description-general',
|
|
'visualeditor-dialog-referenceslist-contextitem-description-named',
|
|
'visualeditor-dialog-referenceslist-title',
|
|
'visualeditor-dialogbutton-citation-educationpopup-title',
|
|
'visualeditor-dialogbutton-citation-educationpopup-text',
|
|
'visualeditor-dialogbutton-reference-full-label',
|
|
'visualeditor-dialogbutton-reference-tooltip',
|
|
'visualeditor-dialogbutton-reference-title',
|
|
'visualeditor-dialogbutton-referenceslist-tooltip',
|
|
'visualeditor-reference-input-placeholder',
|
|
),
|
|
'targets' => array( 'desktop', 'mobile' ),
|
|
);
|
|
|
|
if ( isset( $resourceModules['ext.cite.style'] ) ||
|
|
$resourceLoader->isModuleRegistered( 'ext.cite.style' ) ) {
|
|
$mwreferenceModule['dependencies'][] = 'ext.cite.style';
|
|
}
|
|
$resourceLoader->register( array( 'ext.visualEditor.mwreference' => $mwreferenceModule ) );
|
|
return true;
|
|
}
|
|
|
|
public static function onResourceLoaderTestModules(
|
|
array &$testModules,
|
|
ResourceLoader &$resourceLoader
|
|
) {
|
|
$testModules['qunit']['ext.visualEditor.test'] = array(
|
|
'styles' => array(
|
|
// jsdifflib
|
|
'lib/ve/lib/jsdifflib/diffview.css',
|
|
),
|
|
'scripts' => array(
|
|
// MW config preload
|
|
'modules/ve-mw/tests/mw-preload.js',
|
|
// jsdifflib
|
|
'lib/ve/lib/jsdifflib/diffview.js',
|
|
'lib/ve/lib/jsdifflib/difflib.js',
|
|
// QUnit plugin
|
|
'lib/ve/tests/ve.qunit.js',
|
|
// VisualEditor Tests
|
|
'lib/ve/tests/ve.test.utils.js',
|
|
'modules/ve-mw/tests/ve.test.utils.js',
|
|
'lib/ve/tests/ve.test.js',
|
|
'lib/ve/tests/ve.Document.test.js',
|
|
'lib/ve/tests/ve.Node.test.js',
|
|
'lib/ve/tests/ve.BranchNode.test.js',
|
|
'lib/ve/tests/ve.LeafNode.test.js',
|
|
// VisualEditor DataModel Tests
|
|
'lib/ve/tests/dm/ve.dm.example.js',
|
|
'lib/ve/tests/dm/ve.dm.AnnotationSet.test.js',
|
|
'lib/ve/tests/dm/ve.dm.NodeFactory.test.js',
|
|
'lib/ve/tests/dm/ve.dm.Node.test.js',
|
|
'lib/ve/tests/dm/ve.dm.Converter.test.js',
|
|
'lib/ve/tests/dm/ve.dm.BranchNode.test.js',
|
|
'lib/ve/tests/dm/ve.dm.LeafNode.test.js',
|
|
'lib/ve/tests/dm/ve.dm.LinearData.test.js',
|
|
'lib/ve/tests/dm/nodes/ve.dm.TextNode.test.js',
|
|
'modules/ve-mw/tests/dm/nodes/ve.dm.MWTransclusionNode.test.js',
|
|
'lib/ve/tests/dm/ve.dm.Document.test.js',
|
|
'modules/ve-mw/tests/dm/ve.dm.Document.test.js',
|
|
'lib/ve/tests/dm/ve.dm.DocumentSynchronizer.test.js',
|
|
'lib/ve/tests/dm/ve.dm.IndexValueStore.test.js',
|
|
'lib/ve/tests/dm/ve.dm.InternalList.test.js',
|
|
'modules/ve-mw/tests/dm/ve.dm.InternalList.test.js',
|
|
'lib/ve/tests/dm/ve.dm.Transaction.test.js',
|
|
'modules/ve-mw/tests/dm/ve.dm.Transaction.test.js',
|
|
'lib/ve/tests/dm/ve.dm.TransactionProcessor.test.js',
|
|
'lib/ve/tests/dm/ve.dm.APIResultsQueue.test.js',
|
|
'lib/ve/tests/dm/ve.dm.Surface.test.js',
|
|
'lib/ve/tests/dm/ve.dm.SurfaceFragment.test.js',
|
|
'modules/ve-mw/tests/dm/ve.dm.SurfaceFragment.test.js',
|
|
'lib/ve/tests/dm/ve.dm.ModelRegistry.test.js',
|
|
'lib/ve/tests/dm/ve.dm.MetaList.test.js',
|
|
'lib/ve/tests/dm/lineardata/ve.dm.FlatLinearData.test.js',
|
|
'lib/ve/tests/dm/lineardata/ve.dm.ElementLinearData.test.js',
|
|
'lib/ve/tests/dm/lineardata/ve.dm.MetaLinearData.test.js',
|
|
'modules/ve-mw/tests/dm/ve.dm.mwExample.js',
|
|
'modules/ve-mw/tests/dm/ve.dm.Converter.test.js',
|
|
'modules/ve-mw/tests/dm/ve.dm.MWImageModel.test.js',
|
|
// VisualEditor ContentEditable Tests
|
|
'lib/ve/tests/ce/ve.ce.test.js',
|
|
'lib/ve/tests/ce/ve.ce.Document.test.js',
|
|
'lib/ve/tests/ce/ve.ce.Surface.test.js',
|
|
'modules/ve-mw/tests/ce/ve.ce.Surface.test.js',
|
|
'lib/ve/tests/ce/ve.ce.NodeFactory.test.js',
|
|
'lib/ve/tests/ce/ve.ce.Node.test.js',
|
|
'lib/ve/tests/ce/ve.ce.BranchNode.test.js',
|
|
'lib/ve/tests/ce/ve.ce.ContentBranchNode.test.js',
|
|
'modules/ve-mw/tests/ce/ve.ce.ContentBranchNode.test.js',
|
|
'lib/ve/tests/ce/ve.ce.LeafNode.test.js',
|
|
'lib/ve/tests/ce/nodes/ve.ce.TextNode.test.js',
|
|
// VisualEditor Actions Tests
|
|
'lib/ve/tests/ui/actions/ve.ui.AnnotationAction.test.js',
|
|
'lib/ve/tests/ui/actions/ve.ui.FormatAction.test.js',
|
|
'modules/ve-mw/tests/ui/actions/ve.ui.FormatAction.test.js',
|
|
'lib/ve/tests/ui/actions/ve.ui.IndentationAction.test.js',
|
|
'lib/ve/tests/ui/actions/ve.ui.LinkAction.test.js',
|
|
'modules/ve-mw/tests/ui/actions/ve.ui.MWLinkAction.test.js',
|
|
'lib/ve/tests/ui/actions/ve.ui.ListAction.test.js',
|
|
// VisualEditor DataTransferHandler tests
|
|
'lib/ve/tests/ui/datatransferhandlers/ve.ui.DSVFileTransferHandler.test.js',
|
|
'lib/ve/tests/ui/datatransferhandlers/ve.ui.UrlStringTransferHandler.test.js',
|
|
'modules/ve-mw/tests/ui/datatransferhandlers/ve.ui.MWWikitextStringTransferHandler.test.js',
|
|
'modules/ve-mw/tests/ui/datatransferhandlers/ve.ui.UrlStringTransferHandler.test.js',
|
|
// VisualEditor initialization Tests
|
|
'lib/ve/tests/init/ve.init.Platform.test.js',
|
|
'modules/ve-mw/tests/init/targets/ve.init.mw.DesktopArticleTarget.test.js',
|
|
// IME tests
|
|
'lib/ve/tests/ce/ve.ce.TestRunner.js',
|
|
'lib/ve/tests/ce/ve.ce.imetests.test.js',
|
|
'lib/ve/tests/ce/imetests/backspace-chromium-ubuntu-none.js',
|
|
'lib/ve/tests/ce/imetests/backspace-firefox-ubuntu-none.js',
|
|
'lib/ve/tests/ce/imetests/backspace-ie9-win7-none.js',
|
|
'lib/ve/tests/ce/imetests/home-firefox-win7-none.js',
|
|
'lib/ve/tests/ce/imetests/input-chrome-mac-native-japanese-hiragana.js',
|
|
'lib/ve/tests/ce/imetests/input-chrome-mac-native-japanese-katakana.js',
|
|
'lib/ve/tests/ce/imetests/input-chrome-win7-chinese-traditional-handwriting.js',
|
|
'lib/ve/tests/ce/imetests/input-chrome-win7-greek.js',
|
|
'lib/ve/tests/ce/imetests/input-chrome-win7-polish.js',
|
|
'lib/ve/tests/ce/imetests/input-chrome-win7-welsh.js',
|
|
'lib/ve/tests/ce/imetests/input-chromium-ubuntu-ibus-chinese-cantonese.js',
|
|
'lib/ve/tests/ce/imetests/input-chromium-ubuntu-ibus-japanese-anthy--hiraganaonly.js',
|
|
'lib/ve/tests/ce/imetests/input-chromium-ubuntu-ibus-japanese-mozc.js',
|
|
'lib/ve/tests/ce/imetests/input-chromium-ubuntu-ibus-korean-korean.js',
|
|
'lib/ve/tests/ce/imetests/input-chromium-ubuntu-ibus-malayalam-swanalekha.js',
|
|
'lib/ve/tests/ce/imetests/input-firefox-mac-native-japanese-hiragana.js',
|
|
'lib/ve/tests/ce/imetests/input-firefox-mac-native-japanese-katakana.js',
|
|
'lib/ve/tests/ce/imetests/input-firefox-ubuntu-ibus-chinese-cantonese.js',
|
|
'lib/ve/tests/ce/imetests/input-firefox-ubuntu-ibus-japanese-anthy--hiraganaonly.js',
|
|
'lib/ve/tests/ce/imetests/input-firefox-ubuntu-ibus-japanese-mozc.js',
|
|
'lib/ve/tests/ce/imetests/input-firefox-ubuntu-ibus-korean-korean.js',
|
|
'lib/ve/tests/ce/imetests/input-firefox-ubuntu-ibus-malayalam.swanalekha.js',
|
|
'lib/ve/tests/ce/imetests/input-firefox-win7-chinese-traditional-handwriting.js',
|
|
'lib/ve/tests/ce/imetests/input-firefox-win7-greek.js',
|
|
'lib/ve/tests/ce/imetests/input-firefox-win7-welsh.js',
|
|
'lib/ve/tests/ce/imetests/input-ie9-win7-chinese-traditional-handwriting.js',
|
|
'lib/ve/tests/ce/imetests/input-ie9-win7-greek.js',
|
|
'lib/ve/tests/ce/imetests/input-ie9-win7-korean.js',
|
|
'lib/ve/tests/ce/imetests/input-ie9-win7-welsh.js',
|
|
'lib/ve/tests/ce/imetests/input-ie11-win8.1-korean.js',
|
|
'lib/ve/tests/ce/imetests/input-safari-mac-native-japanese-hiragana.js',
|
|
'lib/ve/tests/ce/imetests/input-safari-mac-native-japanese-katakana.js',
|
|
'lib/ve/tests/ce/imetests/leftarrow-chromium-ubuntu-none.js',
|
|
'lib/ve/tests/ce/imetests/leftarrow-firefox-ubuntu-none.js',
|
|
'lib/ve/tests/ce/imetests/leftarrow-ie9-win7-none.js',
|
|
),
|
|
'dependencies' => array(
|
|
'unicodejs',
|
|
'ext.visualEditor.standalone',
|
|
'ext.visualEditor.core',
|
|
'ext.visualEditor.mwcore',
|
|
'ext.visualEditor.mwformatting',
|
|
'ext.visualEditor.mwlink',
|
|
'ext.visualEditor.mwgallery',
|
|
'ext.visualEditor.mwimage',
|
|
'ext.visualEditor.mwmeta',
|
|
'ext.visualEditor.mwreference',
|
|
'ext.visualEditor.mwtransclusion',
|
|
'ext.visualEditor.mwalienextension',
|
|
'ext.visualEditor.experimental',
|
|
'ext.visualEditor.desktopArticleTarget.init',
|
|
'ext.visualEditor.desktopArticleTarget',
|
|
),
|
|
'localBasePath' => __DIR__,
|
|
'remoteExtPath' => 'VisualEditor',
|
|
);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Ensures that we know whether we're running inside a parser test.
|
|
*/
|
|
public static function onParserTestGlobals( array &$settings ) {
|
|
$settings['wgVisualEditorInParserTests'] = true;
|
|
}
|
|
|
|
/**
|
|
* @param Array $redirectParams Parameters preserved on special page redirects
|
|
* to wiki pages
|
|
* @return bool Always true
|
|
*/
|
|
public static function onRedirectSpecialArticleRedirectParams( &$redirectParams ) {
|
|
array_push( $redirectParams, 'veaction', 'vesection' );
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* If the user has specified that they want to edit the page with VE, suppress any redirect.
|
|
* @param Title $title Title being used for request
|
|
* @param Article|null $article
|
|
* @param OutputPage $output
|
|
* @param User $user
|
|
* @param WebRequest $request
|
|
* @param MediaWiki $mediaWiki
|
|
* @return bool Always true
|
|
*/
|
|
public static function onBeforeInitialize(
|
|
Title $title, $article, OutputPage $output,
|
|
User $user, WebRequest $request, MediaWiki $mediaWiki
|
|
) {
|
|
if ( $request->getVal( 'veaction' ) === 'edit' ) {
|
|
$request->setVal( 'redirect', 'no' );
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Sets user preference to enable the VisualEditor account if the account's
|
|
* userID is matches modulo $wgVisualEditorNewAccountEnableProportion, if set.
|
|
* If set to '1', all new accounts would have VisualEditor enabled; at '2',
|
|
* 50% would; at '20', 5% would, and so on.
|
|
*
|
|
* To be removed once no longer needed.
|
|
*/
|
|
public static function onAddNewAccount( $user, $byEmail ) {
|
|
$x = RequestContext::getMain()->getConfig()->get( 'VisualEditorNewAccountEnableProportion' );
|
|
|
|
if (
|
|
$x &&
|
|
$user->isLoggedin() &&
|
|
( ( $user->getId() % $x ) === 0 )
|
|
) {
|
|
$user->setOption( 'visualeditor-enable', 1 );
|
|
$user->saveSettings();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Sets user preference to enable the VisualEditor account for new auto-
|
|
* created ('auth') accounts, if $wgVisualEditorAutoAccountEnable is set.
|
|
*
|
|
* To be removed once no longer needed.
|
|
*/
|
|
public static function onAuthPluginAutoCreate( $user ) {
|
|
if (
|
|
RequestContext::getMain()->getConfig()->get( 'VisualEditorAutoAccountEnable' ) &&
|
|
$user->isLoggedin()
|
|
) {
|
|
$user->setOption( 'visualeditor-enable', 1 );
|
|
$user->saveSettings();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
}
|