Ticket #1410 (closed New Feature: fixed)

Opened 2 years ago

Last modified 12 months ago

Category Management within FCKeditor

Reported by: davidwilliambarker Owned by: Deimon
Priority: Normal Milestone:
Component: Project : MediaWiki+FCKeditor Version:
Keywords: Confirmed HasPatch Cc:

Description

I like fckeditor for Mediawiki a lot. What I would find useful (and so might others) is some sort of Category Manager. By this I simply mean the ability to assign/create a category for the current document from the menu. It would access the database and create a dropdown (or pop-up, doesn't matter which) which would allow the user to select (or create new) a category for the current document.

Thanks for the amazing hard work that went into the creation of fckeditor. I was afraid I was going to have to create something like it myself, and that would be way beyond me.

David Barker

Attachments

1410.patch Download (15.5 KB) - added by Deimon 14 months ago.

Change History

Changed 2 years ago by ycombarnous

This is already partly available: create a link with the link button, and type "Category:" + a part of the category name, you will get a list of available ones.

Changed 2 years ago by ycombarnous

I admit that having a checkbox like "search in categories" would be more user friendly.

Changed 2 years ago by w.olchawa

  • keywords Confirmed added

Changed 15 months ago by Msul01

The linking functionality works, but I find it confusing, even as someone who develops MediaWiki extensions.

I think the main problem is that there is no way to distinguish a 'normal' link from a categorization link.

A secondary problem is that there is simply a 'floating' category link at the bottom of the page. Most new users would probably assume the link was not supposed to be there, and simply delete it.

I propose the following solution:

1. Add a 'category' placeholder icon, like the placeholders used for templates and special tags. Use this to represent categories in the editor, rather than simply showing a link. Category links have very different behavior from standard links, and their representation should convey this.

2. The link tool will ALWAYS insert the leading ':' for categories and images. This ensures that a link always creates a link, and eliminates the need to educate users to use a colon.

3. Add a 'Add to category' button, which inserts the [[Category:Foo]] Wiki text. This will be represented in the editor by the category placeholder.

I might be willing to take a stab at implementing this... but does it seem like a valuable feature to have?

Changed 14 months ago by Deimon

  • owner set to Deimon
  • status changed from new to assigned

I have created such functionality.

Add this functions in extensions\FCKeditor\FCKeditorSajax.body.php

function wfSajaxSearchCategoryFCKeditor()
{
	global $wgContLang, $wgOut;
	$ns = NS_CATEGORY;
	$m_sql="SELECT tmpSelectCat1.cl_to AS title
FROM wiki_categorylinks AS tmpSelectCat1
LEFT JOIN wiki_page AS tmpSelectCatPage ON ( tmpSelectCat1.cl_to = tmpSelectCatPage.page_title
AND tmpSelectCatPage.page_namespace =14 ) 
LEFT JOIN wiki_categorylinks AS tmpSelectCat2 ON tmpSelectCatPage.page_id = tmpSelectCat2.cl_from
WHERE tmpSelectCat2.cl_from IS NULL 
GROUP BY tmpSelectCat1.cl_to";

	$db =& wfGetDB( DB_SLAVE );
	
	$res = $db->query($m_sql,__METHOD__ );

	$ret = "";
	$i=0;
	while ( ( $row = $db->fetchObject( $res ) ) ) {
		$ret .= $row->title ."\n";
		//$ret.=wfSajaxSearchCategoryChildrenFCKeditor($row->title);
		$sub = explode("\n",wfSajaxSearchCategoryChildrenFCKeditor($row->title));
		foreach($sub as $subrow)if(strlen($subrow)>0)$ret.=" ".$subrow."\n";
	}

	return $ret;
}

function wfSajaxSearchCategoryChildrenFCKeditor($m_root)
{
	global $wgContLang, $wgOut;
	$limit = 50;
	$ns = NS_CATEGORY;
    $m_root = str_replace("'","\'",$m_root);
    $m_sql = "      SELECT tmpSelectCatPage.page_title AS title
                        FROM wiki_categorylinks AS tmpSelectCat 
                        LEFT JOIN wiki_page AS tmpSelectCatPage ON tmpSelectCat.cl_from = tmpSelectCatPage.page_id 
                        WHERE tmpSelectCat.cl_to LIKE '$m_root' AND tmpSelectCatPage.page_namespace = 14";
	
	$db =& wfGetDB( DB_SLAVE );
	
	$res = $db->query($m_sql,__METHOD__ );

	$ret = "";
	$i=0;
	while ( ( $row = $db->fetchObject( $res ) ) ) {
		$ret .= $row->title ."\n";
		//$ret.=wfSajaxSearchCategoryChildrenFCKeditor($row->title);
		$sub = explode("\n",wfSajaxSearchCategoryChildrenFCKeditor($row->title));
		foreach($sub as $subrow)if(strlen($subrow)>0)$ret.=" ".$subrow."\n";
		
	}

	return $ret;
}

Add this in file extensions\FCKeditor\FCKeditor.php

$wgAjaxExportList[] = 'wfSajaxSearchCategoryFCKeditor';

Create file extensions\FCKeditor\plugins\mediawiki\dialogs\category.html as this

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<!--
  * Link dialog window.
-->
<html>
<head>
	<title>Link Properties</title>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
	<meta name="robots" content="noindex, nofollow" />
	<script type="text/javascript">

var oEditor		= window.parent.InnerDialogLoaded() ;
var FCK			= oEditor.FCK ;
var FCKLang		= oEditor.FCKLang ;
var FCKConfig	= oEditor.FCKConfig ;
var FCKRegexLib	= oEditor.FCKRegexLib ;
var FCKTools	= oEditor.FCKTools ;
var FCKBrowserInfo	= oEditor.FCKBrowserInfo ;
var EditorDocument = oEditor.FCK.EditorDocument ;

document.write( '<script src="' + FCKConfig.BasePath + 'dialog/common/fck_dialog_common.js" type="text/javascript"><\/script>' ) ;

	</script>
	<script type="text/javascript">

// oLink: The actual selected link in the editor.

window.onload = function()
{
	// Translate the dialog box texts.
	oEditor.FCKLanguageManager.TranslatePage(document) ;

	// Load the selected link information (if any).
	InitSelected() ;
	SetSearchMessage( 'loading categories...' ) ;
	oEditor.window.parent.sajax_request_type = 'GET' ;
	oEditor.window.parent.sajax_do_call( 'wfSajaxSearchCategoryFCKeditor', [], InitCategoryTree ) ;

	// Activate the "OK" button.
	window.parent.SetOkButton( true ) ;
	window.parent.SetAutoSize( true ) ;
}

var selectedCats;

function InitSelected()
{
	selectedCats=new Array();
	var node=EditorDocument;
    while(node){
		if(node.nodeType==1 && node.tagName.toLowerCase() == 'a'){
			// Get the actual Link href.
			var sHRef = node.getAttribute( '_fcksavedurl' ) ;
			if ( sHRef == null )sHRef = node.getAttribute( 'href' , 2 ) || '' ;
			if(sHRef.StartsWith('Category:')){
				var select = GetE('xCategories' ) ;
				var cat=sHRef.slice(9);
				SelectCategory(cat,-1);
			}
		}
        node=FCKTools.GetNextNode(node,EditorDocument);
    }
	

	// Make an Ajax search for the pages.
}

function SelectCategory(cat,catTreeRow)
{
	var row=parseInt(catTreeRow);
	if(row>=0){
		var select = GetE('xWikiResults' );
		cat=select.options[row].text;
		var lvl=0;
		while(cat.charAt(lvl)==placeholder)lvl++;
		cat=cat.slice(lvl);
		if(cat.charAt(0)=='[' && cat.charAt(cat.length-1)==']')cat=cat.substring(1,cat.length-1);
	}

	if(selectedCats[cat])delete selectedCats[cat];
	else selectedCats[cat]=cat;
	var select = GetE( 'xCategories' ) ;
	while (select.options.length > 0 )select.remove( 0 ); 
	for(var cat in selectedCats) FCKTools.AddSelectOption( select, cat, cat) ;
}


var catTree;

function InitCategoryTree( result )
{
	SetSearchMessage( '' ) ;

	catTree=new Object();
	var levelsHead=new Array('root');
	var levelsBody=new Array('');
	
	var results = result.responseText.Trim().split( '\n' ) ;
	var previousLvl=-1;
		for ( var i = 0 ; i < results.length ; i++ ){
			var lvl=0;
			while(results[i].charAt(lvl)==' ')lvl++;
			var t=results[i].slice(lvl);
			for(var j=previousLvl;j>lvl-1;j--){
			
				if(levelsBody[j+1]!='')catTree[levelsHead[j+1]]=levelsBody[j+1];
				delete levelsHead[j+1];
				delete levelsBody[j+1];
			}
			if(lvl>previousLvl)levelsBody[lvl]=t;
			else levelsBody[lvl]=levelsBody[lvl]+' '+t;
			levelsHead[lvl+1]=t;
			levelsBody[lvl+1]='';
			previousLvl=lvl;
		}
		for(var j=previousLvl;j>=-1;j--){
		if(levelsBody[j+1]!='')catTree[levelsHead[j+1]]=levelsBody[j+1];
		delete levelsHead[j+1];
		delete levelsBody[j+1];
	}

	ShowCategoriesSubTree(-1);
}

var placeholder='.';

//draw category subtree
function ShowCategoriesSubTree(rowInTree){
	var row=parseInt(rowInTree);
	var select = GetE('xWikiResults' ) ;
	var root='root';
	var lvl=-1;
	var prefix='';
	if(row>=0){
		root=select.options[row].text;
		lvl=0;
		while(root.charAt(lvl)==placeholder)lvl++;
		root=root.slice(lvl);
		if(root.charAt(0)=='[' && root.charAt(root.length-1)==']')
			root=root.substring(1,root.length-1);
		prefix=new Array(lvl+1+3).join(placeholder);
	}
	if(!catTree[root])return;
	
	var itCount=select.options.length;
	var itSkip=row+1;
	var opts=new Array();
	for(var i=row+1;i<itCount;i++){
		var t=select.options[i].text;
		var sublvl=0;
		while(t.charAt(sublvl)==placeholder)sublvl++;
		if(sublvl>lvl)itSkip=i+1;
		else break;
	}
	for(var i=itCount-1;i>row;i--){
		var t=select.options[i].text;
		//if(t==fullroot)break;
		if(i>=itSkip)opts.push(t);
		select.remove(i);
	}
	//while ( select.options.length > 0 )
	//	select.remove( 0 ) 
		if(itSkip==row+1){
			var cats = catTree[root].split(' ') ;

			for(var k in cats){
				var p=cats[k];
				if(catTree[cats[k]])p='['+p+']';
				var e=FCKTools.AddSelectOption( select, prefix+p, ++row) ;
				if(catTree[cats[k]])e.style.color='#00f';
		
			}	
		}
		for(var i=opts.length-1;i>=0;i--){
			var e=FCKTools.AddSelectOption( select, opts[i], ++row) ;
			if(opts[i].indexOf('[')>=0)e.style.color='#00f';
	}	
	
}

//draw filtered
function ShowFilteredCategories(filter){
	var select = GetE('xWikiResults' ) ;
	while (select.options.length > 0 )select.remove( 0 ); 
	//alert(filter);
	var found=new Object();
	if(filter.length==0){
		ShowCategoriesSubTree(-1);
		return;
	}
	filter=filter.toLowerCase();
	var row=-1;
	for(var folder in catTree){
		var cats = catTree[folder].split(' ') ;
		for(var k in cats){
			var p=cats[k].toLowerCase();
			if(p.indexOf(filter)>=0){
				if(found[cats[k]]);
				else{
					found[cats[k]]=cats[k];
					FCKTools.AddSelectOption( select, cats[k], ++row ) ;
				}
			}
		}
	}
}

function AddNew(){
	var select = GetE('txtUrl' );
	SelectCategory(select.value,-1)

}


//#### The OK button was hit.
function Ok()
{
	var nodes=new Array();
    var node=EditorDocument;
	var nodeNext;
	var s='';
	var i=0;
    while(node){
        nodeNext=FCKTools.GetNextNode(node,EditorDocument);
		if(node.nodeType==1 && node.tagName.toLowerCase() == 'a'){
			// Get the actual Link href.
			var sHRef = node.getAttribute( '_fcksavedurl' ) ;
			if ( sHRef == null )sHRef = node.getAttribute( 'href' , 2 ) || '' ;
			if(sHRef.StartsWith('Category:'))nodes[i++]=node;
		}
		node=nodeNext;
    }
	for(var i=0;i<nodes.length;i++)nodes[i].parentNode.removeChild(nodes[i]);

	for(var cat in selectedCats)AddCategoryLink(cat);
	return true;

}


function AddCategoryLink(cat){
	var sUri = 'Category:'+cat;
	var sInnerHtml ;

	// If no link is selected, create a new one (it may result in more than one link creation - #220).
	var aLinks = oEditor.FCK.CreateLink( sUri ) ;

	// If no selection, no links are created, so use the uri as the link text (by dom, 2006-05-26)
	var aHasSelection = ( aLinks.length > 0 ) ;
	if ( !aHasSelection )
	{
		sInnerHtml = sUri;

		var oLinkPathRegEx = new RegExp("//?([^?\"']+)([?].*)?$") ;
		var asLinkPath = oLinkPathRegEx.exec( sUri ) ;
		if (asLinkPath != null)
			sInnerHtml = asLinkPath[1];  // use matched path
		
		// Create a new (empty) anchor.
		aLinks = [ oEditor.FCK.InsertElement( 'a' ) ] ;
	}

	oEditor.FCKUndo.SaveUndoStep() ;

	for ( var i = 0 ; i < aLinks.length ; i++ )
	{
		oLink = aLinks[i] ;

		if ( aHasSelection )
			sInnerHtml = oLink.innerHTML ;		// Save the innerHTML (IE changes it if it is like an URL).

		oLink.href = sUri ;
		SetAttribute( oLink, '_fcksavedurl', sUri ) ;

		oLink.innerHTML = sInnerHtml ;		// Set (or restore) the innerHTML

	}

	// Select the (first) link.
	//oEditor.FCKSelection.SelectNode( aLinks[0] );

	return true ;

}

//////////////////////
var searchTimer ;

//#### Called while the user types the URL.
function OnUrlChange()
{
	var link =GetE('txtUrl').value.Trim();
ShowFilteredCategories(link);
return;
	if ( searchTimer )
		window.clearTimeout( searchTimer ) ;
		
	if ( link.StartsWith( '#' ) )
	{
		SetSearchMessage( 'anchor link... no search for it' ) ;
		return ;
	} 
	
	if ( link.StartsWith( 'mailto:' ) )
	{
		SetSearchMessage( 'e-mail link... no search for it' ) ;
		return ;
	} 
	
	if( /^\w+:\/\//.test( link ) )
	{
		SetSearchMessage( 'external link... no search for it' ) ;
		return ;
	}

	/*if ( link.length < 3  )
	{
		ClearSearch() ;

		if ( link.length == 0 )
			SetSearchMessage( 'start typing in the above field' ) ;
		else
			SetSearchMessage( 'too short... type more' ) ;
		return ;
	}
	*/
	SetSearchMessage( 'stop typing to search' ) ;
	//searchTimer = window.setTimeout( StartSearch, 500 ) ;
}

function SetSearchMessage( message )
{
	GetE('xWikiSearchStatus').innerHTML = message ;
}







	</script>
</head>
<body scroll="no" style="overflow: hidden">
	<div id="divInfo">
		<div id="divLinkTypeUrl">
			<span>Selected categories</span><br />
			<select id="xCategories" size="10" style="width: 100%; height:70px"  ondblclick="SelectCategory( this.value,-1);">
			</select><br />
			Search category<br />
			<input id="txtUrl" style="width: 80%" type="text" onkeyup="OnUrlChange();" />
			<input id="btnNew" style="width: 18%" type="button" onclick="AddNew();" value="Add new"/>
			<br />
			Category tree (<span id="xWikiSearchStatus">start typing in the above field</span>)<br />
			<select id="xWikiResults" size="10" style="width: 100%; height:300px" onclick="ShowCategoriesSubTree( this.value );" ondblclick="SelectCategory('', this.value );">
			</select>
		</div>
	</div>
</body>
</html>

Also you need to enable this new command. One way to do it is to add

tbButton = new FCKToolbarButton( 'Category', 'Categories', 'Insert/Edit categories' ) ;
tbButton.IconPath = FCKConfig.PluginsPath + 'mediawiki/images/tb_icon_category.gif' ;
FCKToolbarItems.RegisterItem( 'Category', tbButton ) ;

after

tbButton = new FCKToolbarButton( 'MW_Special', 'Special Tag', 'Insert/Edit Special Tag' ) ;
tbButton.IconPath = FCKConfig.PluginsPath + 'mediawiki/images/tb_icon_special.gif' ;
FCKToolbarItems.RegisterItem( 'MW_Special', tbButton ) ;

and add

FCKCommands.RegisterCommand( 'Category', new FCKDialogCommand( 'Category', 'Категории', FCKConfig.PluginsPath + 'mediawiki/dialogs/category.html', 400, 500 ) ) ;

after

FCKCommands.RegisterCommand( 'Link', new FCKDialogCommand( 'Link', FCKLang.DlgLnkWindowTitle, FCKConfig.PluginsPath + 'mediawiki/dialogs/link.html', 400, 250 ) ) ;

in extensions\FCKeditor\plugins\mediawiki\fckplugin.js

Also you need to create tb_icon_special.gif.

I'm new here. It's my first patch. Please correct the code above if it is not fit to coding standarts.

Changed 14 months ago by arczi

Good work, thank you :) Could you make a patch?

Changed 14 months ago by Deimon

Can you provide me with the link describing how to make patch?

Changed 14 months ago by arczi

  • summary changed from Category Management within FCKeditor to f not

If you have SVN installed - you can use  http://mediawiki.fckeditor.net/index.php/SVN_Instructions

If not - just provide version of the original files, I will make the patch.

Changed 14 months ago by arczi

  • summary changed from f not to Category Management within FCKeditor

Sorry. I changed summary by accident.

Changed 14 months ago by Deimon

Changed 14 months ago by Deimon

Ok, here is the patch. I've changed some code to be more standart (like tableName() etc).

Changed 14 months ago by arczi

  • keywords HasPatch added

Thank you one more time:)

Changed 12 months ago by wwalc

  • status changed from assigned to closed
  • resolution set to fixed

Thanks! Fixed with [3191].

Note: See TracTickets for help on using tickets.