Opened 11 years ago

Closed 10 years ago

#1410 closed New Feature (fixed)

Category Management within FCKeditor

Reported by: David Barker 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 (1)

1410.patch (15.5 KB) - added by Deimon 10 years ago.

Download all attachments as: .zip

Change History (13)

comment:1 Changed 11 years ago by barns

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.

comment:2 Changed 11 years ago by barns

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

comment:3 Changed 11 years ago by Wojciech Olchawa

Keywords: Confirmed added

comment:4 Changed 10 years ago by Mike Sullivan

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.
  1. 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.
  1. 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?

comment:5 Changed 10 years ago by Deimon

Owner: set to Deimon
Status: newassigned

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.

comment:6 Changed 10 years ago by Artur Formella

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

comment:7 Changed 10 years ago by Deimon

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

comment:8 Changed 10 years ago by Artur Formella

Summary: Category Management within FCKeditorf 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.

comment:9 Changed 10 years ago by Artur Formella

Summary: f notCategory Management within FCKeditor

Sorry. I changed summary by accident.

Changed 10 years ago by Deimon

Attachment: 1410.patch added

comment:10 Changed 10 years ago by Deimon

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

comment:11 Changed 10 years ago by Artur Formella

Keywords: HasPatch added

Thank you one more time:)

comment:12 Changed 10 years ago by Wiktor Walc

Resolution: fixed
Status: assignedclosed

Thanks! Fixed with [3191].

Note: See TracTickets for help on using tickets.
© 2003 – 2019 CKSource – Frederico Knabben. All rights reserved. | Terms of use | Privacy policy