/***************************************************************************

    supportware
    Copyright (C) 2001-2010 by c.holzheuer
    c.holzheuer@sourceworx.org

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

***************************************************************************/


#include <ddwebrenderer.h>
#include <ddwebpage.h>
#include <ddwebsite.h>
#include <ddstringset.h>
#include <ddwebview.h>


ddwebrenderer::ddwebrenderer()
: _pagelet( 0 )
{

		
	// Die Callbacks initialisieren.

	_callmap["sn_homepage_short"]   	= &ddwebrenderer::render_homepage_short;	
		
		
	_callmap["sn_jumplast"]	    			= &ddwebrenderer::render_jump_last;
	_callmap["sn_recipe_ing"] 				= &ddwebrenderer::render_recipe_ing;
	
	
	
	
	_callmap["sn_block_vote"]					= &ddwebrenderer::render_block_vote;


	//?? was wird da noch gebraucht?
	_callmap["sn_block_posting"]			= &ddwebrenderer::render_bbcode_block;
	_callmap["sn_block_posting_nnf"]	= &ddwebrenderer::render_bbcode_block;
	_callmap["sn_block_article"]			= &ddwebrenderer::render_bbcode_block;
	_callmap["sn_block_article_nnf"]	= &ddwebrenderer::render_bbcode_block;
	_callmap["sn_block_teaser"]				= &ddwebrenderer::render_bbcode_block;
	_callmap["sn_block_teaser_flat"]	= &ddwebrenderer::render_teaser_flat;
	_callmap["sn_block_teaser_short"]	= &ddwebrenderer::render_teaser_flat;
	
	_callmap["sn_ad_include"]					= &ddwebrenderer::render_ad_include;


	//??
	_callmap["xx_navi_jmp"]   				= &ddwebrenderer::render_navi_jmp;
	_callmap["xx_navi_com"]   				= &ddwebrenderer::render_navi_com;
	_callmap["xx_grouplink"]    			= &ddwebrenderer::render_group_link;
	_callmap["xx_grouplink_nl"]  			= &ddwebrenderer::render_group_link_nl;
	_callmap["xx_nicklink"]   				= &ddwebrenderer::render_member_link;
	_callmap["xx_nicklink_nl"]   			= &ddwebrenderer::render_member_link_nl;
	
	// meist neu und in gebrauch
	_callmap["xx_block_admin"]				= &ddwebrenderer::render_block_admin;	
	_callmap["xx_icon_link"]					= &ddwebrenderer::render_icon_link;		
	_callmap["xx_icon_link_faqs"]			= &ddwebrenderer::render_icon_link;		
	_callmap["xx_icon_link_news"]			= &ddwebrenderer::render_icon_link_news;		
	_callmap["xx_teaser_image"]				= &ddwebrenderer::render_teaser_image;		
	_callmap["xx_count"]							= &ddwebrenderer::render_count;	
	_callmap["xx_number"]							= &ddwebrenderer::render_number;	
	_callmap["xx_answercount"]				= &ddwebrenderer::render_answercount;	
	_callmap["xx_hitcount"]						= &ddwebrenderer::render_hitcount;	
	_callmap["xx_online"]							= &ddwebrenderer::render_member_is_online;
	_callmap["xx_online_icon"]				= &ddwebrenderer::render_member_is_online;
	_callmap["xx_online_img"]					= &ddwebrenderer::render_member_is_online;
	
	_callmap["xx_state_good"]					= &ddwebrenderer::render_state_bits;	
	_callmap["xx_state_killed"]				= &ddwebrenderer::render_state_bits;	
	_callmap["xx_state_solved"]				= &ddwebrenderer::render_state_bits;	
	_callmap["xx_state_alertet"]			= &ddwebrenderer::render_state_bits;	
	_callmap["xx_state_solved_icon"]	= &ddwebrenderer::render_state_bits;
	_callmap["xx_state_solved_hook"]	= &ddwebrenderer::render_state_bits;
	
	// fragwürdig, prüfen ob verwendet
	_callmap["xx_user_is_owner"]	 		= &ddwebrenderer::render_user_is_owner;	
	_callmap["xx_member_icon"]		 		= &ddwebrenderer::render_member_icon;	
	_callmap["xx_msgteaser"]	 				= &ddwebrenderer::render_msgteaser;
	_callmap["xx_threadlink"]					= &ddwebrenderer::render_threadlink;
	_callmap["xx_threadbase"]					= &ddwebrenderer::render_threadbase;	
	
	
	// KILLERPFUSCH!
	_callmap["xx_pager_indent1"]		 	= &ddwebrenderer::render_pager_indent;					
	_callmap["xx_pager_indent2"]		 	= &ddwebrenderer::render_pager_indent;					
	_callmap["xx_pager_indent3"]		 	= &ddwebrenderer::render_pager_indent;					
	_callmap["xx_pager_indent4"]		 	= &ddwebrenderer::render_pager_indent;					
	_callmap["xx_pager_indent5"]		 	= &ddwebrenderer::render_pager_indent;					
	
	
	_callmap["xx_pager_jump_last"]	 	= &ddwebrenderer::render_pager_jump_last;

	_callmap["xx_block_options"]			= &ddwebrenderer::render_block_options;

	
}


ddwebrenderer::~ddwebrenderer()
{

}

/**
	Die Cellpositionen ermitteln und speichern:
	tabellenzeile["id_user"] -> item["id_user"]
*/


void ddwebrenderer::init_positions( dditem& target, ddstringrow& source )
{
	// erstmal saubermachen ...
	_entries.clear();      
		
	const ddmapindex& index = target.get_index();
	ddmapindex::const_iterator pos = index.begin();
	
	// über alle vom item gewünschten felder ...
	for( ; pos != index.end(); ++pos )
	{
		size_t idx = (*pos).second;
		// wenns die Quelle hergibt ...		
		int src = source.index_of( (*pos).first );
		// ... oder ein 'handler' eingetragen ist ...
		ddwebrenderercall call = _callmap[ (*pos).first ];
		// ... dann eintragen
		if( src > -1 || call )
			_entries.push_back( ddwebrendererpos( idx, src, call ) );		
	}

}


/**
	Rendert eine Tabelle.
*/

ddstring ddwebrenderer::render( ddwebpage& pagelet, dditem& target, ddwebview& source, int from, int to )
{
	
	// jetzt wirds ernst
	clear_entries();
	clear_result();
	target.clear_value();
	
	_adincluderatio = pagelet.get_site().get_config()["main"].get_int( "ad_include_ratio", 2 );	
	_maxadincludes  = pagelet.get_site().get_config()["main"].get_int( "max_ad_includes", 2 );	
	_adincludecount = 0;
	_is_admin = pagelet.get_user_id().is_admin();
	
	// leere tables einfangen
	if( !source.size() )
	{	
		_value = "ddwebrenderer::render: Kein Eintrag!";
		target.from_string( _value );
		return _value;
	}
	
	if( !to || to > source.size() )	
		to = source.size();
	if( from >= to )
		throw runtime_error( "ddwebrenderer::render: out of range:" );

	_source = &source;
  
  // Gelöschte Einträge sind nur für admins sichtbar
  // Fragwürdig: Das wird hier gesteuert
  
	_visibleidx = from;
	for( int i=from; i < to; i++ )
	{
		
		// Gelöschte Sachen für gäste nicht anzeigen
		if( !_is_admin && source[i].is_removed() )
			continue;
		render_entry( pagelet, target, source[i], i );
		
		if( !source[i].is_removed() )
			_visibleidx++;
	
	}
	target.from_string( _value );
	
	return _value;
	
}


/**
	Rendert eine Tabellenzeile.
*/

ddstring ddwebrenderer::render_entry( ddwebpage& pagelet, dditem& target, dddatastaterow& row, int rowidx, bool doinit )
{
	// das steht hier weil der zeilenrenderer auch einzeln aufgerufen
	// werden kann, also muss die initialisierung sichergestellt sein.
	
	if( doinit || _entries.empty() )
		init_positions( target, row );
	
	_pagelet	= &pagelet;
	_target  	= &target;
	_realidx	= rowidx;
	_is_admin = pagelet.get_user_id().is_admin();
	
	for( size_t i = 0; i < _entries.size(); ++i )
	{
		// gibts es für dieses Feld einen call?
		// dann ausführen, sonst nur kopieren
		ddwebrendererpos& pos = _entries[i];
		
		if( pos.call )
		{			
			// row[ pos.src ] ist nicht unbedingt vorhanden
			ddstring entry;
			if( pos.src > -1 )
				entry = row[ pos.src ];
			(this->*pos.call)( target[pos.target], row, entry );
		}
		else
		{
			target[ pos.target ].from_string( row[ pos.src ]  );
		}
			
	}
	// das item in '_value' schreiben
	target.flush_content( pagelet, _value );	
	
	return _value;
	
}



// neu

/**
	Neue Sachen
*/



/**
	Baut die link-basis für einen Thread zusammen, etwa /recipesthread/23214
*/

ddstring ddwebrenderer::get_thread_link( dddatastaterow& src )
{
	
	// neu: 'threads' heisst jetzt 't'
	const ddstring& id = src["id_parent"];
	const ddstring& type = src["id_type"];
	
	ddstring base = "/threads/";
	if( type == "posting" || type == "reply" )
	{
		if( id.as_int() > DDLASTID )
			base = "/t/";
		return base + id;
	}	
	
	//??
	if( type != "reply" && type != "comments" )
		base = "/" + src["id_type"] + "thread/";
	
	// sonderbares verhalten: manchmal erscheint 'article' im type
	if( base == "/articlethread/" )
		base = "/threads/";
	
	return base + id;
}


ddstring	ddwebrenderer::get_image_url( dddatastaterow& src, const ddstring& blockname )
{
	
	if( !src.contains_key( blockname ) )
		return "";
	
	const ddstring& body = src[ blockname ];
	
	if( body.empty() )
		return "";
	
	size_t beg = body.find( "[img" );
	// tagende suchen
	if( beg != ddstring::npos )
		beg = body.find( ']', beg );
			
	if( beg != ddstring::npos )
	{
		size_t end = body.find( "[/img", beg );
		if( end != ddstring::npos )
			return  "/articleimage/" + src["id"] + "/" + body.substr( beg+1, end-beg-1 );
	}

	return "";
}

/**
	Rendert den threadlink: <a href='/t/2310432'>Problem mit der Datei von Openoffice und word, wer kann helfen ?</a>
*/

void ddwebrenderer::render_threadlink( dditem& item, dddatastaterow& src, const ddstring& value )
{
	// eigentlich besser: 	<a href="<!--#xx_threadbase-->" class="black"><b><!--#sn_msgsubject--></a>					
	item.set_link( "<b>" + value.as_split_text( DDTEASERSUBJECTSIZE ) + "</b>", get_thread_link( src ), "black" );
}


/**
	Rendert die threadbasis:/t/2310437
*/

void ddwebrenderer::render_threadbase( dditem& item, dddatastaterow& src, const ddstring& value )
{
	item.from_string( get_thread_link( src ) );
}
	
	
/**
	Markiert ein Membereintrag in der liste, wenn das member gerade online ist.
*/

void ddwebrenderer::render_member_is_online( dditem& item, dddatastaterow& src, const ddstring& value )
{
	bool is_online = _pagelet->get_session_manager().is_member_online( src["sn_nickname"] );
	if( item.get_name() == "xx_online" )
		item.from_string( is_online ? "<b class='green-txt'>Online</b>" : "<b>Offline</b>"  );
	else if( item.get_name() == "xx_online_icon" )
		item.from_string( is_online ? "<li class='online'><img src='/pix/online.gif' alt='online'/></li>" : ""  );
	else if( item.get_name() == "xx_online_img" )
		item.from_string( is_online ? "<img class='online' src='/pix/online.gif' alt='online'/></li>" : ""  );
}
	

/**
	ab hier: alt und ungeprüft
*/

/**
	
*/

void ddwebrenderer::render_group_link( dditem& item, dddatastaterow& src, const ddstring& value )
{
	const ddstring& id = src["id_group"];
	ddstring txt = "&nbsp;" + _pagelet->get_groups()[ id ].get_name();
	ddstring base = "groups/";
	if( src["id_type"] != "posting" && src["id_type"] != "reply")
		base = src["id_type"] + base;
	base += id;
	// FIX! MÖRDERPFUSCH
	if( id == "1" || id == "70" )
		base = "news";
	else if( id == "71" )
		base = "sale";
	item.set_link( txt, "/" + base );

}


/**
	Rendert das kleine Icon in der Übersichtsdarstellung: Superhack mit
	doppelter Fallunterscheidung: das icon kommt entweder aus der db oder aus dem teaser
	bei mischübersichten wird ein anderer style verwendet als bei reinen faqs/news
*/

void ddwebrenderer::render_icon_link( dditem& item, dddatastaterow& src, const ddstring& value )
{
	
	ddstring threadlink = get_thread_link( src );
	ddstring imgclass = item.get_name() == "xx_icon_link" ? "'forum-ic'" : "'forum-ic2'" ;  
		
	// wir sind faqs
	if( src.contains_key( "id_type" ) && ( src[ "id_type" ] == "faqs" || src[ "id_type" ] == "news" ) )
	{
		
		// wenn der teaser ein icon enthält, wird dieses
		// anstelle des standard-icons angezeigt.
		ddstring iconname = get_image_url( src, "sn_block_teaser" );
		
		// wenn kein icon gefunden wird,
		// dann nehmen wird eins aus dem body
		if( iconname.empty() )
			iconname = get_image_url( src, "sn_msgbody" );
		// immernoch nix? dann kommt der dummy
		
		if( iconname.empty() )
			iconname = "/pix/mp_ic13.gif";
		
		// faqs floaten nicht in gemischten übersichten
		ddstring link = "<a href='" + threadlink + "'><img class=" + imgclass + " src='" + iconname + "' border='0' alt=''></a>";
		item.from_string( link );	
	
		return;
		
	}
	
	
	if( src.contains_key( "id_group" ) )
	{
		const ddstring& id = src["id_group"];
		ddstring link = "<a href='" + threadlink + "'><img class=" + imgclass + " src='/pix/";
		link += _pagelet->get_groups()[ id ].get_icon_small() + "' border='0' alt=''></a>";		
		//ddstring link = "<img class='forum-ic' src='/pix/";
		//link += _pagelet->get_groups()[ id ].get_icon_small() + "' border='0'>";
		item.from_string( link );
	}
	//<img class="forum-ic2" src="<!--#xx_group_icon_small-->">
	
}

//


void ddwebrenderer::render_icon_link_news( dditem& item, dddatastaterow& src, const ddstring& value )
{
	
	//item.from_string( "<a href='/newsthread/<!--#id1-->'><img src='/images/news-img1.gif' alt='Die R&uuml;ckkehr der Wikinger auf Nintendo DS und iPhone/iPad' /></a>" );
	
	ddstring threadlink = get_thread_link( src );
		
	// wir sind faqs
	if( src.contains_key( "id_type" ) && ( src[ "id_type" ] == "faqs" || src[ "id_type" ] == "news" ) )
	{
		
		// wenn der teaser ein icon enthält, wird dieses
		// anstelle des standard-icons angezeigt.
		ddstring iconname = get_image_url( src, "sn_block_teaser" );
		
		// wenn kein icon gefunden wird,
		// dann nehmen wird eins aus dem body
		if( iconname.empty() )
			iconname = get_image_url( src, "sn_msgbody" );
		// immernoch nix? dann kommt der dummy
		
		if( iconname.empty() )
			iconname = "/pix/mp_ic13.gif";
		
		// faqs floaten nicht in gemischten übersichten
		ddstring link = "<a href='" + threadlink + "'><img  src='" + iconname + "' border='0' alt='" + src["sn_msgsubject"]  + "'></a>";
		item.from_string( link );	
	
		return;
		
	}

}


/**
	Den gruppenlink rendern, aber eben nicht mehr als link :-)
*/

void ddwebrenderer::render_group_link_nl( dditem& item, dddatastaterow& src, const ddstring& value )
{

	const ddstring& id = src["id_group"];
	ddstring txt = "&nbsp;" + _pagelet->get_groups()[ id ].get_name();
	item.from_string( txt );
	
}


/**
	Bastelt den Nickname-Link
*/

void ddwebrenderer::render_member_link( dditem& item, dddatastaterow& src, const ddstring& value )
{
	const ddstring& id = src["id_user"];
	const ddstring& nn = src["sn_nickname"];
	// <a class="grayU" href="#">
	if( id == "1" || id.empty() )
		item.set_link( nn, "" );
	else
		item.set_link( nn, DDMEMBERLINKURL + id, "grayU" );	

}


/**
	Bastelt den Nickname-Link, aber nicht als link :-)
*/

void ddwebrenderer::render_member_link_nl( dditem& item, dddatastaterow& src, const ddstring& value )
{
	const ddstring& id = src["id_user"];
	const ddstring& nn = src["sn_nickname"];
	
	if( id == "1" || id.empty() )
		item.from_string( nn );
	else
		item.from_string( "<font style='text-decoration:underline'>" + nn + "</font>" );

}


/**
	Bastelt Kurzversion der visi
*/

void ddwebrenderer::render_homepage_short( dditem& item, dddatastaterow& src, const ddstring& value )
{
	ddstring res = ddstring("<a href='") + DDMEMBERLINKURL + src["id_user"] + "' title='" + value +"'>";
	res += value.dup().trim_text( 10 ) + "</a>";
	item.from_string( res );
}



/**
	Etwas skuril: rendert die Rechtseinrückungen in der neuen Pager-Postbox, 
	wenn die Nachricht von Mir stammt.	
*/

void ddwebrenderer::render_pager_indent( dditem& item, dddatastaterow& src, const ddstring& value )
{

	bool sender = src["xx_self_sender"] == "1";
	
	if( item.get_name() == "xx_pager_indent5" )
		item.from_string( sender ? "left" :  "right"  );	
	else
		item.from_string( sender ? "2" : "1" );	

}


/**
	Rendert den Sprunglink zur letzten Seite eines Pager-Threads (Neu: das sind alle Nachrichten
	von einem oder für einen Bekannten)
	Also: 
	- Thread passt auf eine Seite: 				/pager/2312
	- Thread passt nicht auf eine Seite:	/pager/2312/12 <- letzte Seite
*/

void ddwebrenderer::render_pager_jump_last( dditem& item, dddatastaterow& src, const ddstring& value )
{
	int nument 	= src["sn_numentries"].as_int();
	int itemspp	= _pagelet->get_user_id().get_items_per_page();
	int pages 	= ( nument / itemspp );
	
	// letzter eintrag?
	item.from_string( pages ? "/" + ddstrutil::from_int( pages ) : "" );
}



/**
	Den Jump-Navi( Nächster Eintrag / Letzter Eintrag) anzeigen.
*/

void ddwebrenderer::render_navi_jmp( dditem& item, dddatastaterow& src, const ddstring& value )
{
	
	// daten holen
	const ddstring& date = src["sn_datemodified"];
	
	ddstring idnxt, idlst;
	ddwebview& table = _pagelet->get_view( _pagelet->get_name() );
	table.execute( "get_previous_entry", date, DDADMFILTERRENDERER, _pagelet->context.pagetype ).read( idlst );
	table.execute( "get_next_entry", 		 date, DDADMFILTERRENDERER, _pagelet->context.pagetype ).read( idnxt );
	
	item["sn_nxt"]["id"].from_string( idnxt );
	item["sn_lst"]["id"].from_string( idlst );		
	item["sn_nxt"].set_visible( !idnxt.empty() );
	item["sn_lst"].set_visible( !idlst.empty() );

}


/**
	Den comment-Navi( x Kommentare lesen, kommentar schreiben ) anzeigen.
*/

void ddwebrenderer::render_navi_com( dditem& item, dddatastaterow& src, const ddstring& value )
{
	bool showcomments = (_pagelet->get_user_id().is_member()) 
		|| ( _pagelet->get_split_path()[0].find( "com" ) != ddstring::npos );
	
	item.set_visible( !showcomments );
	if( showcomments ) 
		return;
		

	int count = src["xx_answercount"].as_int();
	// isses net scheeee?
	ddstring hint = count ? count > 1 ? src["xx_answercount"]+ " Kommentare" : "1 Kommentar" : "";	
	
	
	
	item["sn_com1"].set_visible( !hint.empty() );
	if( !hint.empty() )
		item["sn_com1"]["sn_comments"].from_string( hint );
		
	// bei geschlossenen threads kann man nicht schreiben
	item["sn_com2"].set_visible(	!src.is_closed() );
}



/**
	Childcount etc.: die '0' wegmachen
*/


void ddwebrenderer::render_number( dditem& item, dddatastaterow& src, const ddstring& value )
{
	item.from_string( value == "0" ? " " : value );
}


/**
	Rendert "Antworten", "Antwort" bzw. einen Leerstring je nach Anzahl der Antworten. 
*/

void ddwebrenderer::render_answercount( dditem& item, dddatastaterow& src, const ddstring& value )
{
	int count = value.as_int();
	// isses net scheeee?
	ddstring text = "<b><span class='dark-blue-txt'>" + value + "</span> Antwort" + (count>1 ? "en" : "") + "</b>";
	item.from_string( count ? text : "" );	
}


/**
	Rendert "23 Hits -" bzw. einen Leerstring je nach Anzahl der Hits. 
*/

void ddwebrenderer::render_hitcount( dditem& item, dddatastaterow& src, const ddstring& value )
{
	
	int count = value.as_int();
	// isses net scheeee?
	item.from_string( count ? count > 1 ? " - " + value + " Hits - " : " - 1 Hit - " : "" );	
}


/**
	Zeigt das visi-icon des Members an bzw. das Dummy icon
*/

void ddwebrenderer::render_member_icon( dditem& item, dddatastaterow& src, const ddstring& value )
{
	// FIX! Bild hochladen link !?
	// orig: <p class="m-icon"><a href="#"><img src="images/m-icon.gif" alt="" /></a></p>
	const ddstring& img = src["sn_homepageimage"];
	ddstring link;
	if( img.empty() )
		link = "<img src='/pix/m-icon.gif' title='Hier k&ouml;nnte Ihr bild stehen' alt='Hier k&ouml;nnte Ihr bild stehen'/>";
	else
		link = "<img src='/memberimage/" + img +"' width='48' height='48'/>";
	item.from_string( link );	
}


/**
	Zeigt einen Teaserblock _ohne_ Bilder an
*/

void ddwebrenderer::render_teaser_flat( dditem& item, dddatastaterow& src, const ddstring& value )
{
	// hack: erst den block normal rendern,
	_tagblocks.get_entry( "sn_block_teaser" ).render( *_pagelet, item, src, value );
	// dann bild rausschneiden
	ddstring block = item.get_value();
	// anfang finden
	size_t pos = block.find( "<a href='/articleimage/" );
	if( pos != ddstring::npos )
	{
		size_t end = block.find( "</a>" );
		if( end != ddstring::npos )
			block.erase( pos, end-pos+4 );
	}
	int size = item.get_name().find( "short") != ddstring::npos ? 100 : 150 ;
	item.from_string( block.as_split_text( DDTEASERLINELEN ).trim_text( size ) );	
	
	/*
		ddstring text = value.as_codeless_text().as_split_text( DDTEASERLINELEN );
	item.from_string( text.trim_text( DDTEASERMAXSIZE ) );
	*/
}


/**
	Zeigt einen nur das Bild eines Teasers an.
*/

void ddwebrenderer::render_teaser_image( dditem& item, dddatastaterow& src, const ddstring& value )
{
	//item.from_string( " " );		
	// hack: erst den block normal rendern,
	_tagblocks.get_entry( "sn_block_teaser" ).render( *_pagelet, item, src, src["sn_block_teaser_flat"] );
	// dann bild rausschneiden
	ddstring block = item.get_value();
	// anfang finden
	size_t pos = block.find( "<a href='/articleimage/" );
	if( pos != ddstring::npos )
	{
		size_t end = block.find( "</a>" );
		if( end != ddstring::npos )
			item.from_string( block.substr( pos, end-pos+4 ) );	
	}	
	
}
	
	



/**
	Rendert die Statusbits eines Eintrags, gelöscht, gemeldet etc.
*/

void ddwebrenderer::render_state_bits( dditem& item, dddatastaterow& src, const ddstring& value )
{
	// pfusch
	if( item.get_name() == "xx_state_good" )
		item.from_string( src.is_solved() ? "good" : "" );
	else if( item.get_name() == "xx_state_solved" )
		item.from_string( src.is_solved() ? "<span class='h3Right'>L&ouml;sung</span>" : "" );
	else if( item.get_name() == "xx_state_killed" )
		item.from_string( src.is_removed() ? "class='red-txt'" : "" );
	else if( item.get_name() == "xx_state_alertet" )
		item.from_string( src.is_alerted() ? "<img src='/pix/spam-ic.gif' alt='Amdins Benachrichtigt' class='ok-ic2'/>" : "" );
	else if( item.get_name() == "xx_state_solved_icon" )
		item.from_string( src.is_solved() ? "<span class='ok-ic'></span>" : "" );	
	else if( item.get_name() == "xx_state_solved_hook" )
		item.from_string( src.is_solved() ? "<img src='/pix/ok-ic.gif' alt='L&ouml;sung' class='ok-ic2'/>" : "" );	
}


/**
	rendert den teaser, gesäubert und verkürzt: max 200 Zeichen, umbrechen nach 80
*/

void ddwebrenderer::render_msgteaser( dditem& item, dddatastaterow& src, const ddstring& value )
{
	ddstring text = value.as_codeless_text().as_split_text( DDTEASERLINELEN );
	item.from_string( text.trim_text( DDTEASERMAXSIZE ) );	
}




/**
	steuert die anzeige des user-editor buttons
*/

void ddwebrenderer::render_user_is_owner( dditem& item, dddatastaterow& src, const ddstring& value )
{
	// nur dem  "Besitzer" anzeigen
	item.set_visible( _pagelet->get_user_id().get_id() == src["id_user"]  );
}




/**
	Die Zutatenliste eines Rezepts rendern.
	FIX! Das gehört hier eigentlich nicht hin!
	(Unterklasse des Renderers bilden, set_renderer einbauen )
*/

void ddwebrenderer::render_recipe_ing( dditem& item, dddatastaterow& src, const ddstring& value )
{
	// die personenzahl ermitteln...
	double oc = src["sn_unitcount"].as_double();
	double nc = _pagelet->get_cgi()["sn_count"].as_double();
	if( !nc )
		nc = oc;
	if( nc>100 )
		nc = 100;
	
	// ...im form speichern..
	(*_target)["sn_count"].from_double( nc );
	
	// und die Zutatenliste umrechnen und anzeigen.
	ddstringrow content;
	content.create_lines( value );
	ddstring result;
	for( size_t i=0; i<content.size(); ++i )
	{
		const ddstring& line = content[i];
		if( line.contains( '|' ) )
		{
			ddstring count, entry;
			line.split( "|", count, entry );
			count.replace_all( ',','.' );
			double ic = count.trim().as_double() / oc * nc;
			result += ddstrutil::from_double( ic ) + " " + entry;
		}
		else
		{
			result += line;
		}
		result += "<br>";
	}
	
	result.replace_all( "0.25","&frac14;" );
	result.replace_all( "0.5", "&frac12;" );
	result.replace_all( "0.75","&frac34;" );
	result.replace_all( ".25", "&frac14;" );
	result.replace_all( ".5",  "&frac12;" );
	result.replace_all( ".75", "&frac34;" );
	
	result.replace_all( '.',',' );
	
	item.from_string( result );
	
}



	
/**
	Die Nummer der aktuellen Antwort rendern.
*/

void ddwebrenderer::render_count( dditem& item, dddatastaterow& src, const ddstring& value )
{
	// Die user kriegen die sichtbare id, die admin die echte
	if(  _is_admin && src.is_removed() )
		item.from_int( _realidx );
	else
		item.from_int( _visibleidx );
}


/**
	Rendert den #jumplast Anker, damit man zur letzten antwort springen kann.
*/

void ddwebrenderer::render_jump_last( dditem& item, dddatastaterow& src, const ddstring& value )
{
	// letzter eintrag?
	item.from_string( _realidx == _source->size() - 1 ? "<a name=\"jumplast\"></a>" : "" );
}


/**
	Rendert die Admin-Console.
*/

void ddwebrenderer::render_block_admin( dditem& item, dddatastaterow& src, const ddstring& value )
{
	
	//item.set_field_value( "id0", src["id"] );
	item.set_field_value( "id1", src["id"] );
	item.set_field_value( "id2", src["id"] );
	item.set_field_value( "id3", src["id"] );
	
	item.set_field_value( "sn_senderip1", src["sn_senderip"] );
	item.set_field_value( "sn_senderip2", src["sn_senderip"] );
	item.set_field_value( "sn_senderip3", src["sn_senderip"] );
	item.set_field_value( "sn_senderip4", src["sn_senderip"] );
	
	ddstring threadlink = get_thread_link( src );
	item.set_field_value( "xx_threadbase", threadlink );		
	item.set_field_value( "xx_threadbase2", threadlink );		
	item.set_field_value( "xx_threadbase3", threadlink );		
	
	if( src.contains_key( "id_parent") )
	{
		bool is_parent = ( src["id_parent"] == src["id"]) ;
		if( item.contains_key( "xx_editlink" )  )
			item["xx_editlink"].from_string(  is_parent ? "on_edit" : "on_reply_edit&id=" +  src["id"] );
		if( item.contains_key( "xx_removelink" )  )
			item["xx_removelink"].from_string(  is_parent ? "on_remove" : "on_reply_remove&id=" +  src["id"] );
		if( item.contains_key( "xx_removeblocklink" )  )			
			item["xx_removeblocklink"].from_string(  is_parent ? "on_remove_block" : "on_reply_remove_block&id=" +  src["id"] );
	}
	
	
		
	if( item.contains_key( "sn_voter" ) )
	{
		item.set_field_value( "sn_voterip1", src["sn_voterip"] );
		item.set_field_value( "sn_voterip2", src["sn_voterip"] );
		//item.set_field_value( "sn_voter", 	 src["sn_voter"] );
		_pagelet->get_site().create_user_link( item["sn_voter"], src["id_voter"] );	
		item.set_field_value( "sn_datevote", src["sn_datevote"] );
		//<b>Voter: <!--#sn_voter-->, <!--#sn_datevote--> (ip: <a href="/admin_search_thread?sn_senderip=<!--#sn_voterip1-->" target='_parent'><!--#sn_voterip2--></a>)</b><br>
	}
	
}



/**
	Rendert die Bewertungslinks
*/

void ddwebrenderer::render_block_vote( dditem& item, dddatastaterow& src, const ddstring& value )
{
	
	//item["sn_navigator"].set_visible( false );

	// Den votezähler anzeigen
	const ddstring& vc = src["sn_votecount"];
	ddstring hint = "Antwort noch nicht bewertet";
	if( vc == "1" )
		hint = "<font color='green'>'Gute Antwort' meinte 1 Mitglied</font>";
	else if( vc != "0" )
		hint  = "<font color='green'>'Gute Antwort' meinten " + vc + " Mitglieder</font>";
	item["sn_votehint"].from_string( hint );

	const ddstring& answeruid = src["id_user"];
  const ddstring& entryid   = src["id"];
  const ddstring& uid				= _pagelet->get_user_id().get_id();
  // ist die Id des Artikels in den Userdaten gespeichert?
 // bool	mookoo 							= _pagelet->get_user_id().get_property( (*_source)[0]["id"] ).empty();
  
  // Daten für die Vote-links einbauen.  	
	dditem& x1 = item["sn_x1"];
	dditem& x2 = item["sn_x2"];
	dditem& x3 = item["sn_x3"];

	x1["id_user"].from_string( answeruid );
	x1["id"].from_string( entryid );
	// 'gute antwort' link	bei eigenen Antworten ausblenden
	x1.set_visible( answeruid != uid );
	
	// den 'das ist die lösung' link erstmal ausblenden
	x2.set_visible( false );
	
	
	// Wenn das Problem schon gelöst ist, hint zeigen und weg
	x3.set_visible( src.is_solved() );	
	if( (*_source)[0].is_solved() )
		return;
	
	// ab hier: problem ist nicht gelöst
	// den 'das ist die lösung link nur beim fragesteller anzeigen
	// aber nicht, wenn die antwort selbst gegeben wurde
	if( (*_source)[0]["id_user"] == uid && answeruid != uid )
	{
		x2["id_user"].from_string( answeruid );
		x2["id"].from_string( entryid );
  	x2.set_visible( true );	
  }

}


/**
	Rendert das Kontext--Menu eines Eintrags
*/

void ddwebrenderer::render_block_options( dditem& item, dddatastaterow& src, const ddstring& value )
{
	
	item.set_field_value( "sn_cnt1", _realidx );
	item.set_field_value( "sn_cnt2", _realidx );
	item.set_field_value( "sn_cnt3", _realidx );
	
	item.set_field_value( "id1", src["id"] );
	item.set_field_value( "id2", src["id"] );
	item.set_field_value( "id3", src["id"] );
	
	if( src.contains_key( "id_user" ) )
	{
		item.set_field_value( "id_user1", src["id_user"] );
		item.set_field_value( "id_user2", src["id_user"] );
	}
	
	if( src.contains_key( "sn_nickname" ) )	
		item.set_field_value( "sn_nickname", src["sn_nickname"] );
	
	
	if( item.contains_key( "xx_block_admin") )
		render_block_admin( item[ "xx_block_admin" ], src, value );
	
	if( item.contains_key( "xx_user_is_owner") )
	{
		item[ "xx_user_is_owner" ].set_visible( _pagelet->get_user_id().get_id() == src["id_user"]  );
		item[ "xx_user_is_owner" ].set_field_value( "id1", src["id"] );
	}

}


/**
	Nachfolger von dem itemblockrenderer
*/

void ddwebrenderer::render_bbcode_block( dditem& item, dddatastaterow& src, const ddstring& value )
{	
	//_tagblocks.get_entry( item.get_name() ).dump();
	_tagblocks.get_entry( item.get_name() ).render( *_pagelet, item, src, value );
}	


/**
	Rendert die Werbeeinblendungen innerhalb eines Threads
*/

void ddwebrenderer::render_ad_include( dditem& item, dddatastaterow& src, const ddstring& value )
{
	
	item.set_visible( false );
	if( _adincludecount < _maxadincludes && _visibleidx % _adincluderatio == 0 )
	{
		item.set_visible( true );
		_adincludecount++;
	}
	
}
