Příručka:Rozšíření značek (tagů)

From mediawiki.org
This page is a translated version of the page Manual:Tag extensions and the translation is 100% complete.
Rozšíření MediaWiki

Jednotlivé projekty často považují za užitečné rozšířit vestavěné značení wiki o další možnosti, ať už jde o jednoduché zpracování řetězců nebo plnohodnotné vyhledávání informací. Rozšíření značek umožňují uživatelům vytvářet nové vlastní značky, které to dělají. Například lze použít rozšíření značky k zavedení jednoduché značky ‎<donation />, která na stránku vloží darovací formulář. Rozšíření, včetně funkcí parseru a háčků, jsou nejúčinnějším způsobem, jak změnit nebo vylepšit funkčnost MediaWiki. Před zahájením práce na rozšíření byste měli vždy zkontrolovat, zda někdo jiný neudělal přesně to, o co se pokoušíte.

Jednoduché rozšíření tagu se skládá z funkce callback, která je zaháčkováním připojena k analyzátoru, takže když analyzátor běží, najde a nahradí všechny instance konkrétní značky, voláním odpovídající funkce zpětného volání k vykreslení skutečného HTML.

Příklad

V extension.json nastavte háčky:

...
  "Hooks": {
    "ParserFirstCallInit": "ExampleExtension::onParserFirstCallInit"
   },
...

A přidejte háček do souboru PHP

<?php

class ExampleExtension {
	// Zaregistrujte všechna zpětná volání vykreslení pomocí analyzátoru
	public static function onParserFirstCallInit( Parser $parser ) {
		// Když analyzátor uvidí značku <sample>, spustí renderTagSample (viz níže)
		$parser->setHook( 'sample', [ self::class, 'renderTagSample' ] );
	}

	// Render <sample>
	public static function renderTagSample( $input, array $args, Parser $parser, PPFrame $frame ) {
		// Není zde nic vzrušujícího, stačí uniknout vstupu poskytnutému uživatelem a znovu ho vyhodit (jako příklad)
		return htmlspecialchars( $input );
	}
}

Tento příklad registruje funkci zpětného volání pro značku ‎<sample>. Když uživatel přidá tuto značku na stránku, jako je tato: <sample arg1="xxx" arg2="xxx">...input...</sample>, analyzátor zavolá funkci renderTagSample() a předá čtyři argumenty:

$input
Vstup mezi značky ‎<sample> a ‎</sample> nebo null, pokud je značka "zavřená", tj. ‎<sample />
$args
Argumenty značek, které se zadávají jako atributy značek HTML. Toto je asociativní pole indexované podle názvu atributu.
$parser
Nadřazený analyzátor (objekt analyzátoru). Pokročilejší rozšíření to používají k získání kontextového názvu, analýze wiki textu, rozbalení složených závorek, registraci vztahů a závislostí odkazů atd.
$frame
Rodičovský rámec (objekt PPFrame). To se používá spolu s $parser k poskytnutí analyzátoru úplnější informace o kontextu, ve kterém bylo rozšíření voláno.

Propracovanější příklad viz Příklad rozšíření značky

Atributy

Podívejme se na další příklad:

<?php

$wgHooks['ParserFirstCallInit'][] = 'onParserFirstCallInit';

function onParserFirstCallInit( Parser $parser ) {
	$parser->setHook( 'sample', 'wfSampleRender' );
}

function wfSampleRender( $input, array $args, Parser $parser, PPFrame $frame ) {
	$attr = [];    
	// Tentokrát vytvořte seznam atributů a jejich hodnot a vypište je spolu se vstupem uživatele
	foreach( $args as $name => $value ) {
		$attr[] = '<strong>' . htmlspecialchars( $name ) . '</strong> = ' . htmlspecialchars( $value );
	}
	return implode( '<br />', $attr ) . "\n\n" . htmlspecialchars( $input );

/**
 * K přímému získání hodnot proměnných lze použít následující řádky:
 * $to = $args['to'] ;
 * $email = $args['email'] ;
 */
}

Tento příklad vypíše atributy předané značce spolu s jejich hodnotami. Je zcela zřejmé, že to umožňuje flexibilní specifikaci nových vlastních značek. Můžete například definovat rozšíření značky, které umožňuje uživateli vložit kontaktní formulář na svou uživatelskou stránku pomocí něčeho jako <emailform to="User" email="user@foo.com" />.

Pro MediaWiki je k dispozici nepřeberné množství rozšíření značek, z nichž některá jsou uvedena na tomto webu. Ostatní lze najít pomocí rychlého vyhledávání na webu. Zatímco řada z nich je pro svůj případ použití zcela specializovaná, existuje velké množství oblíbených a dobře používaných rozšíření poskytujících různé stupně funkčnosti.

Konvence

Viz Příručka:Vývoj rozšíření pro obecné rozložení a nastavení rozšíření.

Publikování vašich rozšíření

  1. Vytvořte na této wiki novou stránku s názvem Extension:<extension_name> s informacemi o vašem rozšíření, jak jej nainstalovat a se snímky obrazovky, které používá. Pro tyto informace byla vytvořena pohodlná šablona s názvem Template:Extension . Další informace naleznete na stránce šablony. Měli byste také přidat co nejvíce podrobností do těla stránky a je moudré je poměrně pravidelně kontrolovat, abyste odpověděli na dotazy uživatelů na související diskusní stránce. Také se ujistěte, že stránka patří Kategorie:Rozšíření .
  2. Rozšíření, která vytvářejí nové háčky v rámci kódu rozšíření, by je měla zaregistrovat do kategorie háčků rozšíření.
  3. Upozorněte na mediawiki-l mailing list.

Viz také publikování vašeho rozšíření.

FAQ

Bezpečnostní obavy

Výše si všimnete, že vstup ve výše uvedených příkladech je před vrácením escapován pomocí htmlspecialchars(). Je životně důležité, aby byl veškerý uživatelský vstup ošetřen tímto způsobem, než jej odešle zpět klientům, aby se předešlo zavádění vektorů pro libovolné HTML injection, což může vést k cross-site skriptování zranitelnosti.

Načítání modulů

Správný způsob, jak přidat moduly pro vaše rozšíření, je připojit je k ParserOutput spíše než k $wgOut. Seznam modulů bude poté automaticky převzat z objektu ParserOutput a přidán do $wgOut, i když je vykreslování stránky předem uloženo do mezipaměti. Pokud moduly přidáváte přímo do $wgOut, nemusí být uloženy do mezipaměti ve výstupu analyzátoru.

function myCoolHook( $text, array $params, Parser $parser, PPFrame $frame ) {
	// ... vytvoří ...
	$parser->getOutput()->addModules( 'ext.mycoolext' );
	$parser->getOutput()->addModuleStyles( 'ext.mycoolext.styles' );
	// ... vytvoří další ...
}

Časování a rozšíření

Pokud změníte kód rozšíření, všechny stránky, které rozšíření používají, teoreticky okamžitě odrážejí výsledky nového kódu. Technicky vzato to znamená, že váš kód je spuštěn pokaždé, když je vykreslena stránka obsahující rozšíření.

V praxi tomu tak často není, kvůli ukládání stránek do mezipaměti – buď softwarem MediaWiki, prohlížečem nebo zprostředkujícími proxy či firewallem.

Chcete-li obejít mezipaměť analyzátoru MediaWiki a zajistit vygenerování nové verze stránky, klikněte na upravit, nahraďte "action=edit" v adrese URL zobrazené v adresním řádku vašeho prohlížeče "action=purge" a odešlete novou adresu URL. Stránka a všechny šablony, na které odkazuje, budou znovu vygenerovány, přičemž budou ignorována všechna data uložená v mezipaměti. Akce čištění je nutná, pokud není upravena samotná hlavní stránka, ale změnil se způsob jejího vykreslení (bylo upraveno rozšíření nebo byla upravena pouze odkazovaná šablona).

Pokud to k získání nové kopie stránky nestačí, můžete obvykle obejít mezipaměti přidáním "&rand=somerandomtext" na konec výše uvedené adresy URL. Ujistěte se, že 'somerandomtext' je pokaždé jiný.

Jak deaktivuji ukládání do mezipaměti pro stránky používající mé rozšíření?

Od MediaWiki 1.5 je analyzátor předán jako třetí parametr rozšíření. Tento analyzátor lze použít ke zneplatnění mezipaměti takto:

function wfSampleSomeHookFunction( $text, array $args, Parser $parser, PPFrame $frame ) {
	$parser->disableCache();
	// ...
}

Obnovení stránky při úpravě jiné stránky

Možná nechcete ukládání do mezipaměti úplně zakázat, jen chcete, aby se stránka znovu vygenerovala při každé úpravě jiné stránky, podobně jako je tomu u transkluzí šablon. To lze provést pomocí objektu analyzátoru, který je předán vaší funkci zavěšení. Následující metoda byla zrušena od CoreParserFunctions.php a zdá se, že pro tento účel funguje.

/** Zajistěte, aby analyzovaná stránka byla závislá na $page prostřednictvím tabulky templatelinks. 
*/
function wfSampleaddTemplateLinkDependency( Parser $parser, $page )  {
	$title = Title::newFromText( $page );
	$rev = Revision::newFromTitle( $title );
	$id = $rev ? $rev->getPage() : 0;
	// Register dependency in templatelinks
	$parser->getOutput()->addTemplate( $title, $id, $rev ? $rev->getId() : 0 );
}

Jemná úprava chování při ukládání do mezipaměti

Jemně zrnité ukládání do mezipaměti můžete pro své rozšíření použít pomocí klíčů mezipaměti k rozlišení mezi různými verzemi výstupu rozšíření. Při vykreslování můžete přidat klíče mezipaměti pro každou funkci přidáním metody addExtraKey do funkce háčku, např.:

function wfSampleSomeHookFunction( $text, array $args, Parser $parser, PPFrame $frame ) {
	$userOptionsLookup = MediaWikiServices::getInstance()->getUserOptionsLookup();
	$setting1= (int)$userOptionsLookup->getOption( $parser->getUserIdentity(), 'setting1' );
	$parser->getOptions()->optionUsed( 'setting1' );
	$setting2= (int)$userOptionsLookup->getOption( $parser->getUserIdentity(), 'setting2' );
	$parser->getOptions()->optionUsed( 'setting2' );
	...
}

Úprava $parser->getOptions() během analýzy však znamená, že při pokusu o získání stránky uložené v mezipaměti nejsou zahrnuty další klíče voleb, ale pouze při vykreslování stránky pro přechod do mezipaměti, takže můžete použít háček PageRenderingHash k nastavení dalších možností. PageRenderingHash se spouští jak při vkládání stránky do mezipaměti, tak při jejím získávání, takže je důležité přidávat nové klíče do hash pouze v případě, že tam ještě nejsou. Například:

$wgHooks['PageRenderingHash'][] = 'wfMyExtOnPageRenderingHash';

function wfMyExtOnPageRenderingHash( &$confstr, $user, $optionsUsed ) {
	$userOptionsLookup = MediaWikiServices::getInstance()->getUserOptionsLookup();
	if ( in_array( 'setting1', $optionsUsed ) ) {
		$confstr .= "!setting1=" . $userOptionsLookup->getOption( $user, 'setting1' );
	}
	if ( in_array( 'setting2', $optionsUsed ) ) {
		$confstr .= "!setting2=" . $userOptionsLookup->getOption( $user, 'setting2' );
	}
}

Některé důležité poznámky:

  • Použití "!setting1=$value" místo pouhého "!$value" v confstr zajistí, že se mezipaměť analyzátoru nepokazí, pokud jsou nainstalována různá rozšíření nebo se změní jejich pořadí načítání. ! se používá jako oddělovač pro různé možnosti vykreslování
  • Někteří lidé místo $parser->getOptions()->optionUsed() používají $parser->getOptions()->addExtraKey(). Upozorňujeme, že addExtraKey neříká mezipaměti analyzátoru, že se používá extra klíč, a proto může snadno vést k prolomení mezipaměti, pokud si nedáte pozor.

Jak vykreslím wikitext v mém rozšíření?

Od verze 1.16

Verze MediaWiki:
1.16

Funkce háčku analyzátoru jsou předány jako odkaz na objekt analyzátoru a objekt frame. Ty by měly být použity k analýze wikitextu.

function wfSampleWonderfulHook( $text, array $args, Parser $parser, PPFrame $frame ) {
	$output = $parser->recursiveTagParse( $text, $frame );
	return '<div class="wonderful">' . $output . '</div>';
}

Parser::recursiveTagParse() existuje již od verze 1.8. Mezi jeho výhody patří jednoduchost (bere pouze jeden argument a vrací řetězec) a skutečnost, že analyzuje značky rozšíření v $text, takže můžete značky rozšíření vnořovat.

Druhý parametr recursiveTagParse, $frame, je volitelný argument zavedený v MW 1.16 alpha (r55682).

  • Pokud je zadán $frame (s použitím hodnoty $frame předané vašemu rozšíření), pak budou všechny parametry šablony v $text rozšířeny. Jinými slovy, obsah jako {{{1}}} bude rozpoznán a převeden na příslušnou hodnotu.
  • Pokud není zadáno $frame (např. $parser->recursiveTagParse( $text )) nebo pokud je $frame nastaveno na false, pak parametry šablony nebudou rozbaleny. {{{1}}} se nezmění. Ačkoli je nepravděpodobné, že by to bylo žádoucí chování, byla to jediná možnost dostupná před MW 1,16.

Jeden krok analýzy, který je u značek stále přeskočen, i když používáte recursiveTagParse, je Parser::preSaveTransform. preSaveTransform je prvním krokem analýzy, který je zodpovědný za provádění trvalých změn v právě uloženém wikitextu, jako například:

  • Převod podpisů (~~~, ~~~~, ~~~~~)
  • Rozšíření štítků odkazů, známé také jako pipe-trick (např. změna [[Help:Contents|]] na [[Help:Contents|Obsah]]). Bez tohoto kroku jsou zkrácené odkazy jako [[Help:Contents|]] považovány za neplatné a při analýze jsou ponechány ve formě wikitextu.
  • Rozšíření {{subst:}} šablon.

Původní volání preSaveTransform záměrně přeskakuje takové konverze ve všech značkách rozšíření. Pokud potřebujete provést transformaci před uložením, měli byste místo toho zvážit použití funkce parseru. Všechna rozšíření značek lze také volat jako funkci analyzátoru pomocí {{#tag:tagname|input|attribute_name=value}}, která bude mít použitou transformaci před uložením.

Jak mohu předat parametry ve stylu XML do své značky rozšíření?

Od verze 1.5

Od MediaWiki 1.5 jsou podporovány parametry ve stylu XML (atributy značek). Parametry jsou předány jako druhý parametr funkci háčku, jako asociativní pole. Hodnotové řetězce již pro vás dekódovaly znakové entity HTML, takže pokud je odesíláte zpět do HTML, nezapomeňte použít htmlspecialchars( $codeToEncode, ENT_QUOTES ), abyste se vyhnuli riziku vložení HTML .

Jak se mohu vyhnout úpravám výstupu HTML mého rozšíření?

Vrácená hodnota rozšíření značky je považována za téměř analyzovaný text, což znamená, že se s ním nezachází jako s čistým html, ale přesto se mírně upravuje. Existují dvě hlavní věci, které se provádějí na výstupu rozšíření značky (spolu s několika dalšími drobnými věcmi):

  • Nahraďte značky strip. Značky stripů jsou určité položky, které se vkládají v různých fázích zpracování wikitextu, aby fungovaly jako značka pro opětovné pozdější vložení odstraněného obsahu. To není něco, o co by se rozšíření obvykle musela starat.
  • Parser::doBlockLevels, která změní * na seznamy a promění jakýkoli řádek začínající úvodní mezerou na <pre> mimo jiné. To může být někdy problém v některých rozšířeních.

Rozšíření značek také podporují vrácení pole namísto pouhého řetězce (podobně jako funkce analyzátoru), aby se změnil způsob interpretace návratové hodnoty. 0. hodnota pole musí být HTML. Klíč "markerType" lze nastavit na nowiki, aby se zastavila další analýza. Provedení něčeho jako return [ $html, 'markerType' => 'nowiki' ]; by zajistilo, že hodnota $html nebude dále upravována a nebude s ní zacházeno jako s pouhým html.

Jak dosáhnu toho, aby se mé rozšíření zobrazovalo na Special:Version?

Aby se vaše rozšíření zobrazilo na stránce MediaWiki Special:Version, musíte v kódu PHP přiřadit kredity rozšíření.

Chcete-li to provést, přidejte proměnnou $wgExtensionCredits jako první spustitelný řádek kódu před definicí háčkového řádku nebo funkce.

Příkladem rozšíření kreditu je:

<?php
/**
 * ExampleExtension - this extension is an example that does nothing
 *
 * To activate this extension, add the following into your LocalSettings.php file:
 * require_once('$IP/extensions/Example.php');
 *
 * @ingroup Extensions
 * @author John Doe <john.doe@example.com>
 * @version 1.0
 * @link https://www.mediawiki.org/wiki/Extension:MyExtension Documentation
 * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
 */

/**
 * Protect against register_globals vulnerabilities.
 * This line must be present before any global variable is referenced.
 */
if( !defined( 'MEDIAWIKI' ) ) {
	echo( "This is an extension to the MediaWiki package and cannot be run standalone.\n" );
	die( -1 );
}

// Extension credits that will show up on Special:Version    
$wgExtensionCredits['validextensionclass'][] = array(
	'path'           => __FILE__,
	'name'           => 'Example',
	'version'        => '1.0',
	'author'         => 'John Doe', 
	'url'            => 'https://www.mediawiki.org/wiki/Extension:MyExtension',
	'descriptionmsg' => 'example-desc', // Message key in i18n file.
	'description'    => 'This extension is an example and performs no discernible function'
);

$wgExtensionMessagesFiles[] = __DIR__ . '/Example.i18n.php';

// Here is where we set up our extension
function wfExample(){
	// ...
}

Nahraďte validextensionclass jedním z následujících (pokud vaše rozšíření nespadá do více tříd — pak vytvořte kredit pro každou třídu):

  • 'specialpage' — rezervováno pro přidání na speciální stránky MediaWiki;
  • 'parserhook' — používá se, pokud vaše rozšíření upravuje, doplňuje nebo nahrazuje funkce analyzátoru v MediaWiki;
  • 'variable' — rozšíření které přidává do MediaWiki více funkcí;
  • 'media' — používá se, pokud je vaše rozšíření nějakým druhem manipulátoru médií
  • 'other' — všechny ostatní přípony.

myextensionmsg je název rozhraní/zprávy i18n, která popisuje vaše rozšíření, které bude nutné definovat v souboru i18n.php vašeho rozšíření. Pokud toto pole vynecháte, použije se místo něj pole description.

Získání názvu značky uvnitř zpětného volání

Předpokládejme, že máte několik značek ‎<foo> a ‎<bar>, které sdílejí stejné zpětné volání, a uvnitř funkce zpětného volání chcete získat jméno značky, která vyvolala zpětné volání.

$wgHooks['ParserFirstCallInit'][] = 'onParserFirstCallInit';

# ...

public function onParserFirstCallInit( Parser $parser ) {
	$parser->setHook( 'foo', 'sharedFunctionality' );
	$parser->setHook( 'bar', 'sharedFunctionality' );
}

# ...

public function sharedFunctionality( $input, array $args, Parser $parser, PPFrame $frame ) {
	// Jak rozlišit mezi hovory 'foo' a 'bar'?
}

Krátká odpověď zní: Název značky (foo nebo bar) není přítomen v žádném z argumentů zpětného volání. Ale můžete to obejít dynamickým vytvořením samostatného zpětného volání pro každou značku:

$wgHooks['ParserFirstCallInit'][] = 'onParserFirstCallInit';

# ...

public function onParserFirstCallInit( Parser $parser ) {
	// Pro každý název značky
	foreach ( [ 'foo', 'bar' ] as $tagName ) {
		// Dynamicky vytvořte funkci zpětného volání
		$callback = function( $input, $args, $parser, $frame ) use ( $tagName ) {
			// Zpětné volání vyvolá sdílenou funkci.
			// Všimněte si, že nyní předáváme název značky jako parametr.
			return sharedFunctionality( $input, $args, $parser, $frame, $tagName );
		};
		// Přiřaďte zpětné volání ke značce
		$parser->setHook( $tagName, $callback );
	}
}

# ...

public function sharedFunctionality( $input, array $args, Parser $parser, PPFrame $frame, $tagName) {
	// Nyní můžeme načíst název značky a provádět vlastní akce pro tuto značku
	switch ( $tagName ) {
		//...
	}
}

Tlačítka panelu nástrojů

Rozšíření:WikiEditor poskytuje panel nástrojů pro úpravy, který uživatelům umožňuje přidávat značky do editoru pouhým kliknutím na tlačítko. Pokud chcete pro svůj nový tag tlačítko na panelu nástrojů, vytvořte ve složce resources vašeho rozšíření soubor s názvem něco jako toolbar-button.js. Soubor by měl vypadat takto:

var customizeToolbar = function () {
    $('#wpTextbox1').wikiEditor('addToToolbar', {
        section: 'main',
        group: 'format',
        tools: {
            "ExtensionName": { // nahradit názvem svého rozšíření
                label: 'TagName', // nahradit štítkem, který by se měl objevit při podržení tlačítka
                type: 'button',
                icon: "extensions/ExtensionName/images/button-image.svg", // cesta k obrázku, která by měla jít na tlačítko
                action: {
                    type: 'encapsulate',
                    options: {
                        pre: "<tagName>", // tagy, které se vloží po kliknutí na tlačítko
                        post: "</tagName>"
                    }
                }
            }
        }
    });
};

/* Zkontrolujte, zda je zobrazení v režimu úprav a zda jsou k dispozici požadované moduly. Poté přizpůsobte panel nástrojů… */
if ( [ 'edit', 'submit' ].indexOf( mw.config.get( 'wgAction' ) ) !== -1 ) {
    mw.loader.using( 'user.options' ).then( function () {
        // Může to být řetězec "0", pokud uživatel předvolbu zakázal ([[phab:T54542#555387]])
        if ( mw.user.options.get( 'usebetatoolbar' ) == 1 ) {
            $.when(
                mw.loader.using( 'ext.wikiEditor' ), $.ready
            ).then( customizeToolbar );
        }
    } );
}

Další podrobnosti o přizpůsobení tohoto souboru naleznete zde. Jakmile soubor vytvoříte, musíte jej zaregistrovat u ResourceLoader , aby byl doručen návštěvníkům. To se provede úpravou vašich extension.json:

"Hooks": {
    "BeforePageDisplay": "ExtensionName::onBeforePageDisplay"
}
"ResourceModules": {
    "ext.ExtensionName": {
        "scripts": ["toolbarButton.js"]
    }
}

Poté ve vašem souboru PHP:

public static function onBeforePageDisplay( OutputPage $out ) {
    $out->addModules( [ 'ext.ExtensionName' ] );
}

Související odkazy