Modify MediaWiki Search Form - mediawiki

Is there a way to modify the MediaWiki search form in the page header other than by editing Vector.php?
Basically, I would like to change/extend the markup of the the HTML form and add a JavaScript listener for Ajax calls.
Unfortunately, I can't seem to be able to find an appropriate hook.

That's not easily possible, if you want to change the HTML. But to add a JavaScript listener you usually don't need to add something directly to the input where you want to listen for events.
You could, e.g., use jQuery to add a listener to the search input. For this you could create a new extension (read this manual for a quick start). In your extension, you create a new resource module:
{
"#comment": "Other configuration options may follow here"
"ResourceFileModulePaths": {
"localBasePath": "",
"remoteSkinPath": "ExampleExt"
},
"ResourceModules": {
"ext.ExampleExt.js": {
"scripts": [
"resources/ext.ExampleExt.js/script.js"
]
}
},
"#comment": "Other configuration options may follow here"
}
Now, you can add the script file, which you defined in the module:
( function ( $ ) {
$( '#searchInput' ).on( 'change', function () {
// do whatever you want when the input
// value changed
}
}( jQuery ) );
The code in the function (in the second parameter of the on() function) will run whenever the value of the search input changes.
Now, you only need to load your module when MediaWiki output's the page. The easiest way is to use the BeforePageDisplay hook:
Register the hook handler:
{
"#comment": "Other configuration options may follow here"
"Hooks": {
"BeforePageDisplay": [
"ExampleExtHooks::onBeforePageDisplay"
],
},
"#comment": "Other configuration options may follow here"
}
Handle the hook (in ExampleExtHooks class, which needs to be created and added to the Autoload classes):
public static function onBeforePageDisplay( OutputPage &$output, Skin &$skin ) {
$output->addModules( array(
'ext.ExampleExt.js',
) );
return true;
}

First, I added a hook:
$wgHooks['BeforePageDisplay'][] = 'MyNamespace\Hooks::onBeforePageDisplay';
The hook is pretty simple:
public static function onBeforePageDisplay( \OutputPage &$out, \Skin &$skin ) {
$skin->template = '\MyNamespace\Template';
}
Finally, the Template class overrides the renderNavigation() method, which renders the search form:
<?php
namespace XtxSearch;
class Template extends \VectorTemplate {
protected function renderNavigation( $elements ) {
...
}
}

Related

How do I create the onClick function for a button with Kotlinx.html?

I am using Kotlin's html library kotlinx.html for dynamic html building.
I want to create a button which triggers a function when clicked. This is my current code:
class TempStackOverflow(): Template<FlowContent> {
var counter: Int = 1
override fun FlowContent.apply() {
div {
button(type = ButtonType.button) {
onClick = "${clicked()}"
}
}
}
fun clicked() {
counter++
}
}
This results in the following source code:
<button type="button" onclick="kotlin.Unit">testkotlin.Unit</button>
Which gives this error when clicked (from Chrome developer console):
Uncaught ReferenceError: kotlin is not defined at HTMLButtonElement.onclick
I have tried several approves, and search for a solution - but could not find the proper documentation.
I am not a Kotlin expert, but it's perfectly possible to write event handlers using kotlinx. Rather than:
onClick = "${clicked()}"
have you tried using this?
onClickFunction = { clicked() }
If you really need a bit on Javascript here, you can type this:
unsafe {
+"<button onClick = console.log('!')>Test</button>"
}
Good for debugging and tests, but not very nice for production code.
Unfortunately, you're completely missing the point of the kotlinx.html library. It can only render HTML for you, it's not supposed to be dynamic kotlin->js bridge, like Vaadin or GWT. So, you just set result of clicked function converted to String to onClick button's property, which is effective kotlin.Unit, because kotlin.Unit is default return value of a function if you not specify another type directly.
fun clicked() {
counter++
}
is the same as
fun clicked():Unit {
counter++
}
and same as
fun clicked():Kotlin.Unit {
counter++
}
So, when you set "${clicked()}" to some property it actually exec function (your counter is incremented here) and return Koltin.Unit value, which is becomes "Kotlin.Unit" string when it rendered inside "${}" template

Quill Editor : Check for change in content in Angular?

I have implemented quill editor in Angular by creating new instance of quill and creating custom toolbar.
this.quill = new Quill('#editor-container', {
modules: {
toolbar: '#toolbar-container'
},
theme: 'snow' // or 'bubble'
});
I have a "update" button which would call the update API. I need to check if the contents of the Quill editor has changed. I am aware of quill.on('text-change'):
this.quill.on('text-change', function(delta, oldDelta, source) {
if (source == 'api') {
console.log('An API call triggered this change.');
} else if (source == 'user') {
console.log('A user action triggered this change.');
}
});
However, I am not sure where do I place this? NgOnInit? NgAfterViewInit? I have created the quill instance in ngAfterViewInit. I know this could be a dumb question.
Any help appreciated! thanks! :)
You can put it in the constructor of the module/component.

How to write custom sort logic on sort column event in ng2-smart-table

I'm looking to hook-up sort events performed on ng2-smart-table. Followed https://akveo.github.io/ng2-smart-table/#/documentation, I see bunch of events that are exposed like rowSelect, mouseover etc but I don't see sort events published/emitted by the library. I'm thinking of changing Ng2SmartTableComponent and emit an event when (sort) is called internally. May I know if anyone did it already or is there a hack I can rely upon.
The source of the sort in ng2-smart-table is shown on GitHub (link to code).
If you want to change the compare-Function (as used by default) you can add your own custom function in your ng2-smart-table-configuration:
columns: {
group_name: {
title: 'Groupname',
compareFunction(direction: any, a: any, b: any) => {
//your code
}
}
}
I was searching for an event to sort my data remotely and I have found a solution. Also I have some logic for page change event (remote paging). Here is what works for me.
ts
source: LocalDataSource = new LocalDataSource();
ngOnInit() {
this.source.onChanged().subscribe((change) => {
if (change.action === 'sort') {
this.sortingChange(change.sort);
}
else if (change.action === 'page') {
this.pageChange(change.paging.page);
}
});
}
html
<ng2-smart-table [settings]="settings" [source]="source"></ng2-smart-table>
This solution won't replace custom logic but it might help you solve your problem.

ReactJS adding onClick handler into element given an HTML string

I'm given an HTML string from an API:
<div><h1>something</h1><img src="something" /></div>
I would like to add an onClick handler onto the img tag. I thought about using regex replace, but it's highly advised against.
I'm new to React... how would you go about solving this problem?
Any links or pointing into the right direction would be highly appreciated!
EDIT
Is there a way to add a listener to all anchor tags in a react friendly way? I'm thinking then I can just check the anchor tag's children, and if there's an image element, then I can run my code block.
I think a more idiomatic React way of doing this would be to refactor this into a component, replete with it's own onClick handler, and then insert your image URL via props. Something like
class MyImg extends React.Component {
onClick() {
// foo
}
render() {
return (
<div onClick={this.onClick}>
<h1>{this.props.foo}</h1>
<img src={this.props.imgSrc} />
</div>
);
}
}
Can you update your API to return JSON instead of HTML? JSON is easier to manipulate and you can create react elements on the fly, for example, let's assume your API returns this:
{
component: 'div',
children: [
{ component: 'h1', text: 'Something here' },
{ component: 'img', src: 'something.jpg' },
],
}
If you have that kind of response is very easy to create React elements at run time, you can also add events when creating these components, for example:
class DynamicContent extends PureComponent {
onImageClick = () => {
// Do something here!
console.log('Testing click!');
}
render() {
const children = response.children;
return React.createElement(response.component, null,
React.createElement(children[0].component, null, children[0].text),
React.createElement(children[1].component, {
...children[1],
onClick: this.onImageClick, // <-- Adds the click event
}),
);
}
}
You might want to create an utility that dynamically walks through the response object and creates all the components that you need.

How to add a link in MediaWiki VisualEditor Toolbar?

I`m trying to insert a custom link to a special page in VisualEditor toolbar. See the image below.
See Image
I googled a lot but without success. Someone please give a path...
My answer is based on the following resources:
MediaWiki core JS doc (ooui-js)
VisualEditor JS doc (+ reading code of both repositories used for VE, mediawiki/extension/VisualEditor and VisualEditor)
Also, I'm pretty sure, that there is no documented way of adding a tool to the toolbar in VE, as far as I know. Although it's possible to add a tool to a group, which is already added, mostly used for the "Insert" tool group, like in Syntaxhighlight_GeSHi). There is, probably, a much easier or "better" way of doing this :)
First, VisualEditor provides a way to load additional modules (called plugins) when the main part of VE loads (mostly, when you click the "Edit" button). The modules needs to be registered via the global variable $wgVisualEditorPluginModules (or the equivalent in extension.json, if you're using the new extension registration). In your extension registration file, you should initialize a module (with your required script files to add the tool) and add it as a VE plugin.
Example PHP (old extension registration via PHP files):
// other setup...
$wgResourceModules['ext.extName.visualeditor'] = array(
'localBasePath' => __DIR__,
'remoteExtPath' => 'extName'
'dependencies' => array(
'ext.visualEditor.mwcore',
),
'scripts' => array(
'javascripts/ve.ui.ExtNameTool.js',
),
'messages' => array(
'extname-ve-toolname',
),
);
$wgVisualEditorPluginModules[] = 'ext.extName.visualeditor';
// other setup...
extension.json (new JSON-based extension registration):
// other setup...
"ResourceModules": {
"ext.geshi.visualEditor": {
"scripts": [
"javascripts/ve.ui.ExtNameTool.js"
],
"dependencies": [
"ext.visualEditor.mwcore"
],
"messages": [
"extname-ve-toolname"
]
}
},
"VisualEditorPluginModules": [
"ext.extName.visualeditor"
],
// other setup...
Now, if VE starts, it will load your module, named ext.extName.visualeditor in this example, with the script ve.ui.ExtNameTool.js. In this script, you can now do, what ever you want. As an example, this is a way to add a new module to the end of the toolgroup list in the toolbar:
Example of ve.ui.ExtNameTool.js:
( function () {
// create a new class, which will inherit ve.ui.Tool,
// which represents one tool
ve.ui.extNameTool = function extNameTool( toolGroup, config ) {
// parent constructor
ve.ui.extNameTool.super.apply( this, arguments );
// the tool should be enabled by default, enable it
this.setDisabled( false );
}
// inherit ve.ui.Tool
OO.inheritClass( ve.ui.extNameTool, ve.ui.Tool );
// every tool needs at least a name, or an icon
// (with the static property icon)
ve.ui.extNameTool.static.name = 'extname';
// don't add the tool to a named group automatically
ve.ui.extNameTool.static.autoAddToGroup = false;
// prevent this tool to be added to a catch-all group (*),
although this tool isn't added to a group
ve.ui.extNameTool.static.autoAddToCatchall = false;
// the title of the group (it's a message key,
// which should be added to the extensions i18n
// en.json file to be translateable)
// can be a string, too
ve.ui.extNameTool.static.title =
OO.ui.deferMsg( 'extname-ve-toolname' );
// onSelect is the handler for a click on the tool
ve.ui.extNameTool.prototype.onSelect = function () {
// show an alert box only, but you can do anything
alert( 'Hello' );
this.setActive( false );
}
// needs to be overwritten, but does nothing so far
ve.ui.extNameTool.prototype.onUpdateState = function () {
ve.ui.extNameTool.super.prototype.onUpdateState.apply( this, arguments );
}
// the tool needs to be registered to the toolFactory
// of the toolbar to be reachable with the given name
ve.ui.toolFactory.register( ve.ui.extNameTool );
// add this tool to the toolbar
ve.init.mw.Target.static.toolbarGroups.push( {
// this will create a new toolgroup with the tools
// named in this include directive. The naem is the name given
// in the static property of the tool
include: [ 'extname' ]
} );
} )();
After installing the extension in your LocalSettings.php and starting VE, you should see a new tool in the toolbar with the given name. Clicking it will show an alert box with content "Hello". Like written in the inline comments: In the click handler (onSelect) you can do whatever you want, e.g. open a link in a new tab, e.g. to a Special page. To get the link to a special page I would suggest to use mw.Title to get a localized namespace. For example:
var relativeUrl = mw.Title.newFromText( 'RecentChanges', -1 ).getUrl();
The first parameter of mw.Title.newFromText() is the name of the page, the second parameter is the ID of the namespace (-1 is the default for special pages and should always work).
I hope that helps!
I am not sure I understand your question entirely. It is as simple as selecting some text, clicking the chain icon, then clicking the External Link tab and pasting your link there.