Enabling wikitext in MediaWiki sidebar - mediawiki

In recent MediaWiki it does not seem possible to enable wikitext in the Sidebar, at least in Vector Skin. Wikitext allows more advanced formatting and insertion of images.
The only solution I have found is to install the CustomNavBlocks extension ( http://www.mediawiki.org/wiki/Extension:CustomNavBlocks ) but in MonoBook skin this forces images inside each box inside the Sidebar.
Is there a better way to enable wikitext globally for all skins?

The Sidebar is entirely the responsibility of the skin, so there is really no guaranteed way of modifying its behaviour across all skins.
However, most skins (and all built in ones) call the SkinBuildSidebar hook at the end of rendering the sidebar, so one way to build an extensions for modifying the sidebar would be to add some parser there, that handles some extra markup that you invent for that very purpose.

$wgHooks["SkinBuildSidebar"][] = "fnSidebarMultiLevel";
function fnSidebarMultiLevel(Skin $skin, &$bar) {
global $wgOut;
$title = Title::makeTitle(NS_MEDIAWIKI, "Sidebar-Custom");
if ( !$title->exists() )
return true;
$text = WikiPage::factory($title)->getContent()->mText;
$firstValue = reset($bar);
$firstKey = key($bar);
unset($bar[$firstKey]);
$bar = array(
$firstKey => $firstValue,
"Custom" => $wgOut->parse($text)
) + $bar;
return true;
}
Add wikitext to wiki.com/wiki/MediaWiki:Sidebar-Custom. "Custom" block will be second.

Related

How to change the location of the TOC in a MediaWiki skin

I'm woking on a MediaWiki skin for my site. For the page content I'm using <?= $this->html( 'bodycontent' ); ?> to output it all. Part of this, on longer pages, is the Table of Contents (TOC).
I would like to move the location of the TOC out of the body an into the sidebar but I'm not sure how to prevent the TOC from showing in the bodycontent or where to get the raw data to display it in the sidebar.
I'm hoping there is a data time similar to $this->data['sidebar']['navigation'] that I can use to format it how I want.
How can I turn off the TOC in the bodycontent?
Is there a $this->data location that has the TOC data?
The MediaWiki skinning system is not really designed for this, but someone created an extension to make your work easier: https://www.mediawiki.org/wiki/Extension:DeToc
Using that extension you would do something like this (inside function execute()):
$body = $this->data['bodycontent'];
$new_body = DeToc::RemoveToc($body, $extracted_toc);
/* Print body */
echo $new_body;
/* Print TOC somewhere else */
echo $extracted_toc;
Alternatively you could just turn off the TOC, using $parser->mShowToc = false;, and then create the TOC yourself. MediaWiki internally uses a regex like this to find all sections: '/^\={2,5}(.*?)\={2,5}$/m'

Automatically add text / content to MediaWiki short pages

We run mediawiki internally for all of our documentation. The problem we have is that some users create blank pages, as they do not really know how mediawiki works and also dont have deletion rights.
What i want to do is Automatically add this:
:''This page is unnecessary since it has remained blank since creation''
'''Unless more content is added, this page should be marked for deletion.
[[Category:ForDeletion]]
To all pages that are less than 84 bytes (short pages) or blank pages.
Is this in anyway possible in an easy way.?
That could be solved by using the PageContentSave hook. Here is a short example doing it in an extension:
<?php
public static function onPageContentSave( WikiPage &$wikiPage, User &$user, Content &$content, &$summary,
$isMinor, $isWatch, $section, &$flags, Status&$status
) {
// check, if the content model is wikitext to avoid adding wikitext to pages, that doesn't handle wikitext (e.g.
// some JavaScript/JSON/CSS pages)
if ( $content->getModel() === CONTENT_MODEL_WIKITEXT ) {
// check the size of the _new_ content
if ( $content->getSize() <= 84 ) {
// getNativeData returns the plain wikitext, which this Content object represent..
$data = $content->getNativeData();
// ..so it's possible to simply add/remove/replace wikitext like you want
$data .= ":''This page is unnecessary since it has remained blank since creation'' '''Unless more content is added, this page should be marked for deletion.";
$data .= "[[Category:ForDeletion]]";
// the new wikitext ahs to be saved as a new Content object (Content doesn't support to replace/add content by itself)
$content = new WikitextContent( $data );
}
} else {
// this could be omitted, but would log a debug message to the debug log, if the page couldn't be checked for a smaller edit as 84 bytes.
// Maybe you want to add some more info (Title and/or content model of the Content object
wfDebug( 'Could not check for empty or small remaining size after edit. False content model' );
}
return true;
}
You need to register this hook handler in your extension setup file:
$wgHooks['PageContentSave'][] = 'ExtensionHooksClass::onPageContentSave';
I hope that helps, but please consider the problem, that there are (maybe) pages, that are ok without having more then 84 bytes and the above implementation doesn't allow a any exceptions now ;)

Prevent tinymce from deleting span tags

I've come across multiple solutions that all use filters and hooks to prevent tinymce from stripping certain elements from within the post editor 'visual' editor.
I have a plugin that allows the user to place custom <span></span> tags into a new post, but when the user switches over to the 'visual' tab, tinymce then deletes the span tags they have placed.
I have one function here that is from 2009, which doesn't seem to work.
function my_change_mce_options( $init ) {
// Command separated string of extended elements
$ext = 'span[id|name|class|style]';
// Add to extended_valid_elements if it alreay exists
if ( isset( $init['extended_valid_elements'] ) ) {
$init['extended_valid_elements'] .= ',' . $ext;
} else {
$init['extended_valid_elements'] = $ext;
}
// Super important: return $init!
return $init;
}
add_filter('tiny_mce_before_init', 'my_change_mce_options');
I tried adding that to my plugins core files, but does not work. Apparently the newer version of tinymce doesn't do this, but WordPress 3.7.1 is still using an older version for stability reasons I guess.
Any ideas?
Upgrade to Version 4, its confirmed that the bug doesn't exist in that one.

Can I create links with 'target="_blank"' in Markdown?

Is there a way to create a link in Markdown that opens in a new window? If not, what syntax do you recommend to do this? I'll add it to the markdown compiler I use. I think it should be an option.
As far as the Markdown syntax is concerned, if you want to get that detailed, you'll just have to use HTML.
Hello, world!
Most Markdown engines I've seen allow plain old HTML, just for situations like this where a generic text markup system just won't cut it. (The StackOverflow engine, for example.) They then run the entire output through an HTML whitelist filter, regardless, since even a Markdown-only document can easily contain XSS attacks. As such, if you or your users want to create _blank links, then they probably still can.
If that's a feature you're going to be using often, it might make sense to create your own syntax, but it's generally not a vital feature. If I want to launch that link in a new window, I'll ctrl-click it myself, thanks.
Kramdown supports it. It's compatible with standard Markdown syntax, but has many extensions, too. You would use it like this:
[link](url){:target="_blank"}
I don't think there is a markdown feature, although there may be other options available if you want to open links which point outside your own site automatically with JavaScript.
Array.from(javascript.links)
.filter(link => link.hostname != window.location.hostname)
.forEach(link => link.target = '_blank');
jsFiddle.
If you're using jQuery:
$(document.links).filter(function() {
return this.hostname != window.location.hostname;
}).attr('target', '_blank');
jsFiddle.
With Markdown v2.5.2, you can use this:
[link](URL){:target="_blank"}
So, it isn't quite true that you cannot add link attributes to a Markdown URL. To add attributes, check with the underlying markdown parser being used and what their extensions are.
In particular, pandoc has an extension to enable link_attributes, which allow markup in the link. e.g.
[Hello, world!](http://example.com/){target="_blank"}
For those coming from R (e.g. using rmarkdown, bookdown, blogdown and so on), this is the syntax you want.
For those not using R, you may need to enable the extension in the call to pandoc with +link_attributes
Note: This is different than the kramdown parser's support, which is one the accepted answers above. In particular, note that kramdown differs from pandoc since it requires a colon -- : -- at the start of the curly brackets -- {}, e.g.
[link](http://example.com){:hreflang="de"}
In particular:
# Pandoc
{ attribute1="value1" attribute2="value2"}
# Kramdown
{: attribute1="value1" attribute2="value2"}
^
^ Colon
One global solution is to put <base target="_blank">
into your page's <head> element. That effectively adds a default target to every anchor element. I use markdown to create content on my Wordpress-based web site, and my theme customizer will let me inject that code into the top of every page. If your theme doesn't do that, there's a plug-in
Not a direct answer, but may help some people ending up here.
If you are using GatsbyJS there is a plugin that automatically adds target="_blank" to external links in your markdown.
It's called gatsby-remark-external-links and is used like so:
yarn add gatsby-remark-external-links
plugins: [
{
resolve: `gatsby-transformer-remark`,
options: {
plugins: [{
resolve: "gatsby-remark-external-links",
options: {
target: "_blank",
rel: "noopener noreferrer"
}
}]
}
},
It also takes care of the rel="noopener noreferrer".
Reference the docs if you need more options.
For ghost markdown use:
[Google](https://google.com" target="_blank)
Found it here:
https://cmatskas.com/open-external-links-in-a-new-window-ghost/
I'm using Grav CMS and this works perfectly:
Body/Content:
Some text[1]
Body/Reference:
[1]: http://somelink.com/?target=_blank
Just make sure that the target attribute is passed first, if there are additional attributes in the link, copy/paste them to the end of the reference URL.
Also work as direct link:
[Go to this page](http://somelink.com/?target=_blank)
You can do this via native javascript code like so:
var pattern = /a href=/g;
var sanitizedMarkDownText = rawMarkDownText.replace(pattern,"a target='_blank' href=");
JSFiddle Code
In my project I'm doing this and it works fine:
[Link](https://example.org/ "title" target="_blank")
Link
But not all parsers let you do that.
There's no easy way to do it, and like #alex has noted you'll need to use JavaScript. His answer is the best solution but in order to optimize it, you might want to filter only to the post-content links.
<script>
var links = document.querySelectorAll( '.post-content a' );
for (var i = 0, length = links.length; i < length; i++) {
if (links[i].hostname != window.location.hostname) {
links[i].target = '_blank';
}
}
</script>
The code is compatible with IE8+ and you can add it to the bottom of your page. Note that you'll need to change the ".post-content a" to the class that you're using for your posts.
As seen here: http://blog.hubii.com/target-_blank-for-links-on-ghost/
If someone is looking for a global rmarkdown (pandoc) solution.
Using Pandoc Lua Filter
You could write your own Pandoc Lua Filter which adds target="_blank" to all links:
Write a Pandoc Lua Filter, name it for example links.lua
function Link(element)
if
string.sub(element.target, 1, 1) ~= "#"
then
element.attributes.target = "_blank"
end
return element
end
Then update your _output.yml
bookdown::gitbook:
pandoc_args:
- --lua-filter=links.lua
Inject <base target="_blank"> in Header
An alternative solution would be to inject <base target="_blank"> in the HTML head section using the includes option:
Create a new HTML file, name it for example links.html
<base target="_blank">
Then update your _output.yml
bookdown::gitbook:
includes:
in_header: links.html
Note: This solution may also open new tabs for hash (#) pointers/URLs. I have not tested this solution with such URLs.
In Laravel I solved it this way:
$post->text= Str::replace('<a ', '<a target="_blank"', $post->text);
Not works for a specific link. Edit all links in the Markdown text. (In my case it's fine)
I ran into this problem when trying to implement markdown using PHP.
Since the user generated links created with markdown need to open in a new tab but site links need to stay in tab I changed markdown to only generate links that open in a new tab. So not all links on the page link out, just the ones that use markdown.
In markdown I changed all the link output to be <a target='_blank' href="..."> which was easy enough using find/replace.
I do not agree that it's a better user experience to stay within one browser tab. If you want people to stay on your site, or come back to finish reading that article, send them off in a new tab.
Building on #davidmorrow's answer, throw this javascript into your site and turn just external links into links with target=_blank:
<script type="text/javascript" charset="utf-8">
// Creating custom :external selector
$.expr[':'].external = function(obj){
return !obj.href.match(/^mailto\:/)
&& (obj.hostname != location.hostname);
};
$(function(){
// Add 'external' CSS class to all external links
$('a:external').addClass('external');
// turn target into target=_blank for elements w external class
$(".external").attr('target','_blank');
})
</script>
You can add any attributes using {[attr]="[prop]"}
For example [Google] (http://www.google.com){target="_blank"}
For completed alex answered (Dec 13 '10)
A more smart injection target could be done with this code :
/*
* For all links in the current page...
*/
$(document.links).filter(function() {
/*
* ...keep them without `target` already setted...
*/
return !this.target;
}).filter(function() {
/*
* ...and keep them are not on current domain...
*/
return this.hostname !== window.location.hostname ||
/*
* ...or are not a web file (.pdf, .jpg, .png, .js, .mp4, etc.).
*/
/\.(?!html?|php3?|aspx?)([a-z]{0,3}|[a-zt]{0,4})$/.test(this.pathname);
/*
* For all link kept, add the `target="_blank"` attribute.
*/
}).attr('target', '_blank');
You could change the regexp exceptions with adding more extension in (?!html?|php3?|aspx?) group construct (understand this regexp here: https://regex101.com/r/sE6gT9/3).
and for a without jQuery version, check code below:
var links = document.links;
for (var i = 0; i < links.length; i++) {
if (!links[i].target) {
if (
links[i].hostname !== window.location.hostname ||
/\.(?!html?)([a-z]{0,3}|[a-zt]{0,4})$/.test(links[i].pathname)
) {
links[i].target = '_blank';
}
}
}
Automated for external links only, using GNU sed & make
If one would like to do this systematically for all external links, CSS is no option. However, one could run the following sed command once the (X)HTML has been created from Markdown:
sed -i 's|href="http|target="_blank" href="http|g' index.html
This can be further automated by adding above sed command to a makefile. For details, see GNU make or see how I have done that on my website.
If you just want to do this in a specific link, just use the inline attribute list syntax as others have answered, or just use HTML.
If you want to do this in all generated <a> tags, depends on your Markdown compiler, maybe you need an extension of it.
I am doing this for my blog these days, which is generated by pelican, which use Python-Markdown. And I found an extension for Python-Markdown Phuker/markdown_link_attr_modifier, it works well. Note that an old extension called newtab seems not work in Python-Markdown 3.x.
For React + Markdown environment:
I created a reusable component:
export type TargetBlankLinkProps = {
label?: string;
href?: string;
};
export const TargetBlankLink = ({
label = "",
href = "",
}: TargetBlankLinkProps) => (
<a href={href} target="__blank">
{label}
</a>
);
And I use it wherever I need a link that open in a new window.
For "markdown-to-jsx" with MUI v5
This seem to work for me:
import Markdown from 'markdown-to-jsx';
...
const MarkdownLink = ({ children, ...props }) => (
<Link {...props}>{children}</Link>
);
...
<Markdown
options={{
forceBlock: true,
overrides: {
a: {
component: MarkdownLink,
props: {
target: '_blank',
},
},
},
}}
>
{description}
</Markdown>
This works for me: [Page Link](your url here "(target|_blank)")

How to self-identify the position of a script's tag in the DOM?

There may be a better way of doing this, but currently I have an nicely encapsulated, JavaScript object which can have some configurable options. I include a chunk of HTML code on a page (via Dreamweaver 'snippets'), and on page load my JS file runs through the DOM and identifies any of those code chunks as a particular functionality, and goes ahead and sets that functionality up.
That's all fine up until I wish to add more than one object on a page, and have them be configurable. The important point here is that you can add as many of these objects onto a page as you like with my current setup - because they're generic at that point, and have no 'id' attribute.
Now I wish to configure these objects, so I thought, "How about an external file containing the config settings, which these objects check for and apply if a config object is found". This works fine too, but the config data feels a bit 'removed' now. I imagine this will be annoying for my other colleagues eventually, it's just another Thing To Remember.
So to my question, I'm happy to insert these code blocks which will still trigger self-instantiating objects on page load - but what I'd like to try is also inserting a script block which contains the config settings. I need a means of that inserted code block knowing that its parent element is the context for configuration.
Example code:
<div class="snippet">
<_contents of this 'snippet'_/>
<script type="text/javascript">
new Snippet().init({
rootElement: REFERENCE_TO_THIS_SCRIPT_TAGS_PARENT_NODE,
configOptionA: true,
configOptionB: false
});
</script>
</div>
Note: The <div class="snippet"> has no 'id' attribute on purpose, because I want to allow for more than one of these to be dropped onto a page.
Other solutions welcome, so long as they adhere to my few restrictions!
My other related question (now answered) addresses this now, essentially I ended up with:
<div class="snippet">
<elements... />
<script type="text/javascript">
var tmpVarName = 'configOb_'+Math.floor(Math.random()*1111111) ;
document[tmpVarName] = {
remainVisible:true,
hoverBehaviour:false
};
</script>
</div>
...and then in a script loaded on every page, which scans for any appropriate elements to instantiate and config:
var snippets = yd.getElementsBy(function(el){
return yd.hasClass(el, "snippet");
},null,document );
for( var i=0; i<snippets.length;i++ )
{
var s = snippets[i] ;
yd.generateId(s, '_snippet_'+i );
var tag = yd.getElementBy(function(el){
return true;
},'script',s );
var ob = new Snippet();
ob.setId( s.id );
ob.init( eval( tag.innerHTML ) );
}
For a more complete context of the above code;
yd = YAHOO.util.Dom
Snippet.init() and Snippet.setId() are exposed methods on a Module object called Snippet()
Now that my inserted 'chunks' of content have no id attribute, and dynamically evaluated, contextual config objects - I am free to add as many variants as I like. My only real concern is performance if a whole bunch of these Snippet() objects are added to my page (not likely).