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

    supportware
    Copyright (C) 2001-2008 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 <sxpage.h>
#include <ddwebfile.h>
#include <stdlib.h>
#include <time.h>
#include <stdio.h>


/**
	Constructor
*/

sxpage::sxpage( const ddstring& name, ddwebsite* site, const ddstring& type, const ddstring& title )
: ddwebpage( name, site ), _hintbox( 0 ), _use_overlay_image( false )
{

	context.pagetype  = type;
	if( !title.empty() )
		context.pagetitlestatic = context.pagetitle = "Supportnet - " + title;
	
	// bei den Übersichten ist der linktext der pagetype (news,faqs,recipes etc)
	context.navilinkbase = "/" + type + "/";

	// jede seite hat jetzt ihre eigene connection
	ddconfig& config =  get_site().get_config();
	get_data_connection().connect( config["main"]["connectstring"] );

	// alle meine tables connecten
	ddwebviewmap& viewmap = get_site().get_view_map();
	for( int i = 0; i < viewmap.size(); ++i )
	{
		ddwebview& view = viewmap.get_entry( i );
		if( view.get_name().contains( get_name() ) )
			view.set_data_connection( &get_data_connection() );
	}	
	
	// hintbox holen
	dditem* item = get_site().lookup_item( "xx_box_hint" );
	if( !item )
		throw runtime_error( "sxpage: hintbox nicht gefunden!" );
	_hintbox = item->clone();
		
	// Für den Header und title: supportnet Tipps etc.
	_typemapper["faqs"] 			= "Tipp";
	_typemapper["forum"] 			= "Forum";
	_typemapper["news"] 			= "News";
	
	// global
	add_request_handler( "on_close_sysmessage", 	"xx_dummy", 		(ddmemptr) &sxpage::on_close_sysmessage );  
	add_request_handler( "on_listen",						 	"xx_dummy", 		(ddmemptr) &sxpage::on_listen );  	
	add_request_handler( "on_image_vote_good",		"xx_dummy", 		(ddmemptr) &sxpage::on_image_vote );  	
	add_request_handler( "on_image_vote_bad",			"xx_dummy", 		(ddmemptr) &sxpage::on_image_vote );  	
	add_request_handler( "on_image_preview", 			"xx_dummy", 		(ddmemptr) &sxpage::on_image_preview, 3 );  	
	add_request_handler( "on_image_upload",	 			"xx_dummy",			(ddmemptr) &sxpage::on_image_upload );  	
	add_request_handler( "on_image_list",					"xx_ajax_list",	(ddmemptr) &sxpage::on_image_list );  		
	
}


/**
	Destructor
*/

sxpage::~sxpage()
{
}


/**
	Initialisiert alle gängigen callbacks.
	ACHTUNG! die entsprechenden Items müssen vorhanden sein
*/

void sxpage::init_requests()
{
 		
 	const ddstring& type = context.pagetype;
	ddstring pre = "xx_" + type + "_";
	
	add_request_handler( type,						   	pre + "overview", 			(ddmemptr) &sxpage::on_overview );
	add_request_handler( type + "thread", 	 	pre + "show",						(ddmemptr) &sxpage::on_show, 2 );
	add_request_handler( type + "comthread",	pre + "show",						(ddmemptr) &sxpage::on_show_replies, 	2 );
		
	add_request_handler( "on_create",		 			pre + "update", 				(ddmemptr) &sxpage::on_create, 				1, dduserid::usr_can_edit_news );  
	add_request_handler( "on_edit",		 				pre + "update", 				(ddmemptr) &sxpage::on_edit, 					1, dduserid::usr_can_edit_news );  
	add_request_handler( "on_update", 				pre + "update", 				(ddmemptr) &sxpage::on_update, 				1, dduserid::usr_can_edit_news );  
	add_request_handler( "on_remove", 				pre + "update", 				(ddmemptr) &sxpage::on_remove, 				1, dduserid::usr_can_edit_news );  
	add_request_handler( "on_close", 					pre + "update", 				(ddmemptr) &sxpage::on_set_state, 		1, dduserid::usr_can_edit_news );  
	add_request_handler( "on_open", 					pre + "update", 				(ddmemptr) &sxpage::on_set_state, 		1, dduserid::usr_can_edit_news );  
	add_request_handler( "on_publish",				pre + "update", 				(ddmemptr) &sxpage::on_publish,		 		1, dduserid::usr_can_edit_news );  
	add_request_handler( "on_preview", 				pre + "preview",  			(ddmemptr) &sxpage::on_preview );  
	
	add_request_handler( "on_user_create",		"xx_dummy", 						(ddmemptr) &sxpage::on_user_create );  	
	add_request_handler( "on_user_edit",	 		pre + "user_update",		(ddmemptr) &sxpage::on_edit );  	
	add_request_handler( "on_user_update",	 	pre + "user_update",	 	(ddmemptr) &sxpage::on_user_update );  		
	add_request_handler( "on_user_publish",	 	pre + "user_update",	 	(ddmemptr) &sxpage::on_user_publish );  		
	
	add_request_handler( "on_alert", 					"xx_admin_alert", 			(ddmemptr) &sxpage::on_alert ); 
	add_request_handler( "on_alert_send",			"xx_admin_alert", 			(ddmemptr) &sxpage::on_alert_send ); 
	
	add_request_handler( "on_reply_edit", 		"xx_forum_update_reply",	(ddmemptr) &sxpage::on_reply_edit, 		2, dduserid::usr_can_edit_news );  
	add_request_handler( "on_reply_update", 	"xx_forum_update_reply",	(ddmemptr) &sxpage::on_reply_update, 	2, dduserid::usr_can_edit_news );  
	
	add_request_handler( "on_reply_remove", 	pre + "show",						(ddmemptr) &sxpage::on_reply_remove,  2, dduserid::usr_can_edit_news );  
	add_request_handler( "on_reply_create",		pre + "show",						(ddmemptr) &sxpage::on_reply_create );
	// ?? auskommentiert für faqs?
	//add_request_handler( "on_reply_vote", 		"xx_forum_vote",			(ddmemptr) &sxpage::on_reply_vote );
	
	add_request_handler( "on_bookmark",				"xx_dummy",							(ddmemptr) &sxpage::on_bookmark );
	add_request_handler( "on_bookmark_drop",	"xx_dummy",							(ddmemptr) &sxpage::on_bookmark );
	add_request_handler( "on_bookmark_update","xx_dummy",							(ddmemptr) &sxpage::on_bookmark );

	
}



/**
	Eine non-fatal Fehlermeldung in ein Kästchen malen
	und ggf. abhauen.
	Je nach Flag den Sichtbarkeitzustand des Items entsprechend steuern.
	z.b bei create_hint_box( block, DDHINTCLOSED, table[0].is_closed() );
*/


dd_result sxpage::create_hint_box( dditem& item, const ddstring& hint, bool showhint )
{
	if( showhint )
	{
		(*_hintbox)["sn_subbody"].from_string( hint );
		item.from_string( _hintbox->to_string() );
	}
	else
	{
		item.clear_value();
	}
	
	return rs_ok;
}


/**
	Analog zur Hint-box: Ein popup Zeigen und redirecten.
*/

dd_result sxpage::create_system_message( const ddstring& sysmessage1, const ddstring& sysmessage2, const ddstring& syslink, const ddstring& path  )
{
	get_user_id().set_system_message( sysmessage1, sysmessage2, syslink );
	return create_redirect( path.empty() ? get_full_path() : path );
}


/**
	Benachrichtigungs-Popup anzeigen & nach '/member_register' redirecten
*/

dd_result sxpage::create_login_message( const ddstring& msg1, const ddstring& msg2, const ddstring& lnk )
{
	get_user_id().set_system_message( msg1, msg2, lnk );
	return create_redirect( DDREGISTERLINK );
}


/**
	FIX! hässlich!
	Malt die Title- und Keyword tags
*/

void sxpage::create_header_entries( ddwebview& table, dditem& item )
{
	
	// tabelle leer? dann den eintrag laden um die headerdaten 
	// doch noch zu erhalten.
	
	if( table.size() < 1 && table.contains_query( "on_fetch_header") )
		table.execute( "on_fetch_header", context.id ).read();
	if( table.size() < 1 )	
		return;
		
	// hack: subject & grp id extrahieren...
	context.subject		= table[0]["sn_msgsubject"];
	context.pagetitle	= context.subject + ": Supportnet " + _typemapper[context.pagetype] ;
	
	context.idgrp			= table.contains_field( "id_group" ) ? table[0]["id_group"] : "1";

	if( table.contains_field( "id_type" ) && table[0]["id_type"] == "posting" )
	{
		// hack: hier den title wieder ändern
		context.pagetitle	= context.subject + " (Gruppe: " + get_groups()[context.idgrp].get_name()+ ")";
		if( table[0].is_solved()  )			
			context.pagetitle = "L&ouml;sung:  " + context.pagetitle;
	}
	
		
	
	// meta keys frisieren
	// description
	if( table.contains_field( "sn_block_posting" ) )
	{
		ddstring body = table[0]["sn_block_posting"].as_codeless_text();
		body.resize( 145 );	
		item["sn_meta"]["sn_meta_header"].from_string( body );
		// keyword: subject
		item["sn_meta"]["sn_meta_keyword"].from_string( context.subject );
	}
}


void sxpage::create_categories()
{	

	// Kategorien auch speichern
	if( context.pagetype == "faqs" )
	{
		ddwebcgi& cgi = get_cgi();
		
		// kategories in folgende form bringen:
		//replace into tr_msgcatmap( id_faq, id_cat ) values (993, 300), ( 993, 400), (993, 500) ;
		const ddstring& cats 	= cgi["sn_msgcategories"];
		const ddstring& id 		= cgi["id"];
	
      if( !cats.empty() )
		{
			ddstringrow entries( cats, "," );
			ddstring result;
			for( size_t i=0; i<entries.size(); ++i )			
			{	
				result += "( " + id + "," + entries[i] + " )";
				if( i < entries.size() - 1)
					result += ", ";
			}
			get_view().execute( "on_update_category", id, result );
		}
		
	}
}
	
	
/**
	redirect prototype
*/

dd_result sxpage::on_redirect( dditem& item )
{
	return create_redirect( "/", ret_301_moved_permanently );
}


/**
	Einen Admin-Alert schicken.	
*/

dd_result sxpage::on_alert( dditem& item )
{
	
	// auch für öffentlich sichtbare links, dazu daten laden
	//create_header_entries( get_view(), item );
	
	
	// die admin-alert message ...
	dditem& block   = get_body()["sn_form"]["sn_subbody"];
	// ... form säubern ...
	block["sn_alert"].clear_value();
	get_body()["sn_form"].clear_value();
	// ...anzeigen ...
	get_body().clear_value();
	// ... und preparieren ...
	block["id"].from_string( get_cgi()["id"].empty() ? context.id : get_cgi()["id"] );	
	block["sn_action"] .from_string( get_full_path() );
	block["sn_count"] .from_string( get_cgi()["sn_count"] );
	
	// ...und ab
	return rs_ok;
	(void) item;
}


/**
	Den Adminalert abschicken
*/

dd_result sxpage::on_alert_send( dditem& item )
{
	
	// ... form preparieren ...
	ddwebcgi& cgi = get_cgi();
		
	// alertbox holen
	dditem& message = *( get_site().lookup_item( "xx_admin_alert_message" ) );
	message.clear_children();
	
	dditem& block   = get_body()["sn_form"]["sn_subbody"];
	block["id"].from_string( cgi["id"] );	
	block["sn_action"] .from_string( get_full_path() );
	
	// nix eingegeben?
	const ddstring& alert = cgi["sn_alert"];
	if( alert.empty() )
		return rs_ok;
	
	size_t pos = alert.find( "http://" );
	// spam? link suchen, falls der nach aussen geht -> weg
	if( pos != ddstring::npos )
		if( alert.find( "supportnet.de", pos, 40 ) == ddstring::npos )
			//return rs_ok;
			return create_system_message(	"Ihre Nachricht wurde an die Admins verschickt" );
	
	
	// links ermitteln
	// kind oder parent?
	ddstring link = "?sx_action=on_edit";
	ddstring id = cgi["id"];
	if( id != context.id )
		link = "?sx_action=on_reply_edit&id=" + id;	
	else
		id = context.id;
	
	// Spam-Test: Das Subject darf _nur_ Ziffern enthalten
	if( !id.is_number() )
		return rs_ok;
	
	time_t now = ::time(0);	
	if( now - get_user_id().get_last_alert_time() < DDALERTDELAY )
		return create_system_message( DDHINTFASTPOST );
	
	// nachrichtenfelder setzen
	// hack: faqscomthread haut nicht hin für admins, da ist dann
	// das body feld leer, wg. der trennung zwischen comments und body
	// daher muss faqsthread aufgerufen werden.
	
	ddstring path = "http://" + get_connection().get_request().get_host_port();
	path += get_full_path();
	path.replace_all( "com", "" );
	
	message["sn_threadlink"].from_string( path );	
	message["sn_adminlink"].from_string( path + link );
	message["id1"].from_string( context.id );
	message["id2"].from_string( id );
	
	message["sn_replycount"].set_visible( false );
	if( !cgi["sn_count"].empty() )
	{
		message["sn_replycount"].set_visible( true );
		message["sn_replycount"]["sn_count"].from_string( cgi["sn_count"] );
	}
	
	// die hints ins mail-formular laden
	message["sn_alert"].from_string( cgi["sn_alert"] );
	
	// senderip laden
	//get_view( get_name()+".comments" ).execute( "on_alert", id ).read( message );
	message["sn_senderip1"].from_string( get_connection().get_host_ip() );
	message["sn_senderip2"].from_string( get_connection().get_host_ip() );
	
	// das mail-formular in die pager-message laden
	message.flush_children( *this, cgi["sn_msgbody"] );
	cgi["sn_msgsubject"] = id;	
	
	// send it to all admins ....
	get_user_manager().send_admin_pager_message( get_user_id(), cgi );
	
	// update der alertzeit
	get_user_id().update_alert_time();
		
	ddwebview& table = get_view();
	ddstring op = "id_state|" + ddstrutil::from_size_t( dddatastaterow::data_is_alerted );
	table.execute( "on_set_state", op, id );	
	
	// ... und ab
	return create_system_message(	"Ihre Nachricht wurde an die Admins verschickt" );

}
	

/**
	Ein Bild bewerten, 
*/

dd_result sxpage::on_image_vote( dditem& item )
{	
	

	ddwebcgi& cgi = get_cgi();
	dduserid& uid = get_user_id();
	const ddstring& img = cgi[ "sn_key" ];
	bool good_vote = ( cgi["sx_action"] == "on_image_vote_good" );
	
	ddwebview& table = get_view();
	
	// es darf jedes bild nur einmal bewertet werden
	int votecount;
	table.execute( "vote_count", uid.get_id(), img ).read( votecount );
	
	if( !uid.is_admin() && votecount )
	//if( votecount )		
		return create_system_message(	"Ein Bild kann nur eimal bewertet werden." );
	
	// bewertung speichern
	table.execute( "vote_image", uid.get_id(), good_vote ? "1" : "0", img, get_connection().get_host_ip(), good_vote ? "sn_good_votes" : "sn_bad_votes" );
	
	// admin aktionen gehen vor
	/*
	 	data_is_public = 1,
		data_is_removed = 2,
		data_is_alerted = 4,
	*/
	if( uid.is_admin() )
		table.execute( "vote_admin", good_vote ? "1" : "6", img );
	
		// webfilemanager setup
	get_site().get_file_manager().init_data( get_site().get_config() );

	// ... und ab
	return create_system_message(	"Vielen Dank für Ihre Bewertung." );
}	
	

/**
	Ein Bild als Preview Anzeigen, ggf. die ID berechnen.
*/


dd_result sxpage::on_image_preview( dditem& item )
{	
	
	// das ist ein ziemlicher hack: weil der Editor die ID nicht kennt,
	// sendet er die '0', also ggf. Id holen
	
	ddstring id = get_split_path()[1];
	if( id == "0" )
		id = fetch_draft_id();
	
	// aufs 'echte' bild redirecten
	return create_redirect( "/articleimage/" + id + "/" + get_split_path()[2] );
	
}	


dd_result sxpage::on_listen( dditem& item )
{	
	
	// ...
	
	return create_redirect( "/index.html" );
	
}	

// auch für öffentlich sichtbare links, daszu daten laden

/**
	Bookmark setzen
*/

dd_result sxpage::on_bookmark( dditem& item )
{
	
	// auch für öffentlich sichtbare links, daszu daten laden
	//create_header_entries( get_view(), item );
	
	// Jeder User darf nur einmal!
	if( get_user_id().is_guest() )
		return create_login_message( "Favoriten k&ouml;nnen nur von Mitgliedern gespeichert werden!" );	
			
	get_body().clear_value();
	const ddstring& link = get_cgi()[ DDACTIONKEY ];
	// fehler bei mehrfachem einstellen ignorieren
	get_view().execute( link, get_user_id().get_id(), context.id );
	
	
	
	//??
	if( link == "on_bookmark_update" )
		//return create_redirect( "/" threads/"+context.id );
		return create_redirect( get_full_path() );
	
	// create a redirect
  return create_redirect( "/threadwatch" );
  
	(void) item;

}


/**
	Hier wird der 'Als gute Antwort bewerten' link behandelt.
	- 1. Es dürfen nur Member ein bewerten
	- 2. Eigene Postings dürfen nicht bewertet werden
	- 3. Jedes Posting darf nur einmal bewertet werden.
	- 4. Es dürfen nur 5 Bewertungen am Tag abgegeben werden.
	- 5. Der Vorgang der Stimmabgabe wird gespeichert.
*/

dd_result sxpage::on_reply_vote( dditem& item )
{
	
	ddwebview& table = get_view();
	ddstring& id = get_cgi()["id"];
	ddstring& iduser = get_cgi()["id_user"];
	
	// 1. Nur Members dürfen voten.
	dduserid& uid = get_user_id();
	if( uid.is_guest() )
		return create_login_message();
	
	// 2. Eigenlob stinkt
	if( uid.get_id() == iduser )
		return create_system_message( "Eigene Artikel k&ouml;nnen nicht bewertet werden!" );
	
	// 3. Jeder User darf nur einmal...
	// und IPs müssen auch überwacht werden.
	ddstring votes;
	table.execute( "posting_vote0", uid.get_id(), id ).read( votes );
	ddstringmapcache& vc = get_site().get_vote_cache();
	if( votes != "0" || !vc[uid.get_host_ip()][id].empty() )
		return create_system_message( "Jeder Artikel kann nur einmal bewertet werden!" );
	
	// 4. Es dürfen maximal nur 5 Bewertungen pro Tag abgegeben werden.
	table.execute( "posting_vote1", uid.get_id() ).read( votes );
	if( votes.as_int() >= 5  )
		return create_system_message( "Pro Tag d&uuml;rfen nur 5 Bewertungen abgegeben werden." );
	
	// 5: Den Votevorgang speichern ...
	//if( !get_user_id().is_admin() ) 
	//{
		// ... und zwar in der voting table ...
		table.execute( "posting_vote2", uid.get_id(), id, uid.get_host_ip() );		
		// ... im cache
		// was macht dieser Code eingentlich?
		vc[uid.get_host_ip()][id] = ddbasedate::now().to_string();	
		// und in den member-daten auch noch mitzählen
		if( get_cgi()["id_user"] != "1" )
			get_user_manager().usr_update_single( uid.get_id(), "sn_numvotes", "sn_numvotes+1" );		
	//}
		
	// die antwort als 'gute antwort' markieren
	table.execute( "posting_vote3", id );
	if( get_cgi()["id_user"] != "1" )
		table.execute( "posting_vote4", get_cgi()["id_user"] );

	// popup hochreissen und gut
	return create_system_message( "Vielen Dank, dass Sie diese Antwort als 'gut' bewertet haben." );	

}


/**
 	Eine Antort als Lösung markieren, Punkte verteilen

	Bei richtigen Beantwortung einer Frage die Punkte eines Members erhöhen:
	Dazu bekommt der Beantworter die Punkte, die dem Rang des Fragestellers entsprechen.
	Die Antwort muss dann gesperrt werden.
	
	- 1. Es darf nur ein Member bewerten
	- 2. Eigene Postings dürfen nicht bewertet werden
	- 3. Jedes Posting darf nur einmal bewertet werden.
	- 4. Es dürfen nur 5 Bewertungen am Tag abgegeben werden.
	- 5. Der Vorgang der Stimmabgabe wird gespeichert.
*/

dd_result sxpage::on_set_solved( dditem& item )
{
	
	ddwebcgi& cgi = get_cgi();
	ddwebview& table = get_view();
	ddstring& id = cgi["id"];
	ddstring& iduser = cgi["id_user"];
	
	// 1. Nur Members dürfen voten.
	dduserid& uid = get_user_id();
	if( uid.is_guest() )
		return create_login_message();
		
	// 2. Eigenlob stinkt
	if( uid.get_id() == iduser )
		return create_system_message( "Eigene Artikel k&ouml;nnen nicht bewertet werden!" );
	
	// 2B. Der Sender muss der Fragesteller sein.
	ddstring sender;
	table.execute(	"solvedcheck", context.id ).read( sender );
	if( uid.get_id() != sender )
		return create_system_message( "Nur der Fragesteller kann eine Antwort als L&ouml;sung markieren!" );
	
	// 3. Jeder User darf nur einmal...
	// und IPs müssen auch überwacht werden.
	ddstring votes;
	table.execute( "posting_vote0", uid.get_id(), id ).read( votes );
	ddstringmapcache& vc = get_site().get_vote_cache();
	if( votes != "0" || !vc[uid.get_host_ip()][id].empty() )
		return create_system_message( "Jeder Artikel kann nur einmal bewertet werden!" );
	
	// 4. Es dürfen maximal nur 5 Bewertungen pro Tag abgegeben werden.
	table.execute( "posting_vote1", uid.get_id() ).read( votes );
	if( votes.as_int() >= 5  )
		return create_system_message( "Pro Tag d&uuml;rfen nur 5 Bewertungen abgegeben werden." );
	
	
	// 5: Den Votevorgang speichern ...
	//if( !get_user_id().is_admin() ) 
	//{
		// ... und zwar in der voting table ...
		table.execute( "posting_vote2", uid.get_id(), id, uid.get_host_ip() );		
		// ... im cache
		// was macht dieser Code eingentlich?
		vc[uid.get_host_ip()][id] = ddbasedate::now().to_string();	
		// und in den member-daten auch noch mitzählen
		if( get_cgi()["id_user"] != "1" )
			get_user_manager().usr_update_single( uid.get_id(), "sn_numvotes", "sn_numvotes+1" );		
	//}
	
	// punkte vergeben: der sender ...
	size_t points = get_user_id().get_rank_score();
	// an den Empänger
	get_user_manager().usr_inc_score( iduser, points );
	
	// 'Gelöst'-Flag der Antwort ...
	ddstring op = "id_state|" + ddstrutil::from_size_t( dddatastaterow::data_is_solved);
	// kind
	table.execute( "on_set_state", op, id );	
	// parent
	table.execute( "on_set_state", op, context.id );	

  
  return create_system_message( "Vielen Dank, dass Sie dem Mitglied mit der richtigen Antwort die Bonuspunkte f&uuml;r die Beantwortung Ihrer Frage vergeben haben." ); 
	
}
	

/**
	Die Mailbenachrichtigung abschalten.
*/

dd_result sxpage::on_drop_subscription( dditem& item )
{
	get_view().execute( "on_drop_alert", get_cgi()["sn_nickmail"], context.id );
	return create_redirect( get_full_path() );	
	(void) item;
}


/**
	Übersichtsdarstellung basteln, standard prototyp
	für guestbook und was noch?
*/

dd_result sxpage::on_overview( dditem& item )
{
	// daten holen, sofern nötig...
	ddwebview& table = get_view( get_name()+".overview" );
	
	// ...daten holen, der zweite parameter steuert den filter
	context.numrows = table.fill( "on_overview", context.pagetype, DDADMFILTER );			
	
	// ...testen...
	if( context.numrows <= 0 )
		return create_system_message( DDHINTNOGROUPENTRY );	
	
	// ...alles fein...
	get_body().set_visible( true );
	// könnte vorher abgeschaltet worden sein
	get_body()["sn_content"].set_visible( true );
	
	// ...anzeigen...
	dditem& entry = get_body()["sn_content"]["sn_msgentry"];
	table.on_render( *this, entry, context.navifrom, context.navito );	
	
	// ...und gut
	return rs_ok;
	
}
	

/**
	Einen news/faqs Thread anlegen.
	Neue Version, nach dem schema wie on user_create: erst anlegen, dann editieren.
*/

dd_result sxpage::on_create( dditem& item )
{
	
	dduserid& uid = get_user_id();	
	dditem& block = get_body()["sn_form"];//["sn_subbody"];	
	ddwebview& table = get_view( get_name() );

	// erst neu anlegen ...
	table.execute( "on_user_create", uid.get_id(), uid.get_nick_name(),  uid.get_nick_mail(), get_connection().get_host_ip() );
	
	// ... dann zum bearbeiten ins form packen laden...
	block.clear_children();
	// id sichern
	//context.id = table.get_last_insert_id();
	
	
		return create_redirect( "/" + context.pagetype + "thread/" + table.get_last_insert_id() + "?sx_action=on_edit" );
	
	
	
	
	get_view().execute( "on_edit", context.id ).read( *this, block );
	
	// ... beim anlegen ist der state = 64, also draft, zum bearbeiten schalten
	// wir gleich auf public.
	block["id_state"].from_int( 1 );
	
	
	return rs_ok;
		
	(void) item;


}	
		

/**
	Edit funktion: Es wird ein vorhandener Eintrag ins Form geladen
	und die ID in der Session gespeichert, um Fakes zu verhindern.
	Wird sowohl von den Admin-Editoren als auch von den user-editoren
	verwendet, nur die zugriffsrechte & forms sind unterschiedlich.
*/

dd_result sxpage::on_edit( dditem& item )
{
	
	dditem& block = get_body()["sn_form"];
	block.clear_children();
	get_view().execute( "on_edit", context.id ).read( *this, block );
	
	// hack für faqs:
	// cat menschenlesbar anzeigen
	if( block.contains_key( "sn_msgcategories" ) )
		get_view().execute( "on_edit_categories", context.id ).read( *this, block );
	
	get_user_id().set_property( DDENTRYID, context.id );
	
	return rs_ok;
	
}


/**
	Usercontent speichern. Es wird einfach ein Dummy-Eintrag geschrieben und
	dann 'on_user_edit' aufgerufen. Desweiteren wird Id des neuen Eintrag in der
	Session gespeichert, um Fakes zu verhindern.
*/

dd_result sxpage::on_user_create( dditem& item )
{
	
	dduserid& uid = get_user_id();
	if( uid.is_guest() )
		return create_login_message();
			
	const ddstring& id 		= uid.get_id();
	const ddstring& nick	= uid.get_nick_name();
	const ddstring& mail	= uid.get_nick_mail();
	const ddstring& ip		= get_connection().get_host_ip();

	// FIX!!  TIMEOUT BEACHTEN!!!
	
	ddwebview& table = get_view( get_name() );
	table.execute( "on_user_create", id, nick, mail, ip );
		// anzeige updaten	
	get_site().get_user_content_count() = "";
	
	return create_redirect( "/" + context.pagetype + "/" + table.get_last_insert_id() + "?sx_action=on_user_edit" );
	

}


/**
	User Edit funktion
*/


dd_result sxpage::on_user_update( dditem& item )
{
	dduserid& uid = get_user_id();
	// Fakes verhindern
	//if( context.id != uid.get_property( DDCONTENTID ) 
	//	|| uid.get_nick_name() != uid.get_property( DDNICKNAME ) )
	//	return create_error( "ID Fehler!" );
	
	ddwebcgi& cgi = get_cgi();
	ddwebview& table = get_view();
	dditem& block = get_body()["sn_form"];
	block.clear_children();
	ddformmanager& mgr = get_site().get_form_manager();
	
	// ab hier: es wird versucht, einen thread upzudaten.
	
	// daten erst testen...
	if( !mgr.check_form( "check." + get_name(), block, uid, cgi ) )
		return rs_ok;
	
	// nur speichern, wenn ok
	if(	!mgr.contains_blocked_words() )
	{		
		// ... overlay die links einbauen...
		if( _use_overlay_links )	
			get_site().get_overlay_links().replace_keys( cgi["sn_msgbody"] );
		// ... overlay die bilder einbauen...
		if( _use_overlay_image )	
			get_site().get_overlay_images().replace_keys( cgi["sn_msgbody"], cgi["sn_msgsubject"] );
		
		// .. ip nachreichen ...
		cgi["sn_senderip"]	= get_connection().get_host_ip();
		// id state je nach status
		// nein, doch nicht hier
		//cgi["id_state"].from_int( uid.is_admin() ? 1 : 64 );
		// ...dann speichern...
		table.execute( cgi );
		
		// draftID löschen
		get_user_id().clear_property( DDENTRYID );
		
		create_categories();
	}
		
	// anzeige updaten	
	get_site().get_user_content_count() = "";
	
	// hübsch danke sagen
	uid.set_system_message( "Der Artikel wurde erfolgreich abgeschickt.", "Herzlichen Dank f&uuml;r Ihren neuen Artikel." );
	// ...und zur Übersicht bzw. als admin zum neuen thread
	return create_redirect( "/beitraege/" + uid.get_id() );

}


/**
	Ein User schickt einen fertig geschriebenen Artikel an 
	die Redaktion: state von 160 (==private und closed) 
	auf 64 (==draft)
*/

dd_result sxpage::on_user_publish( dditem& item )
{
	// ab hier: es wird versucht, einen thread anzulegen.
	
	ddwebcgi& cgi = get_cgi();
	dditem& block = get_body()["sn_form"];
	ddformmanager& mgr = get_site().get_form_manager();
	dduserid& uid = get_user_id();

	// daten erst testen...
	if( !mgr.check_form( "check."+get_name(), block, get_user_id(), cgi ) )
		return rs_ok;
	
	
	// 1. Freischalten: state auf draft stellen ...
	cgi["id_state"] 	= "64";	
	// ...dann speichern...
	get_view().execute( cgi );
	// anzeige updaten	
	get_site().get_user_content_count() = "";
	
	create_categories();
	
	// hübsch danke sagen
	uid.set_system_message( "Der Artikel wurde an die Redaktion geschickt.", "Herzlichen Dank f&uuml;r Ihren neuen Artikel." );
	// ...und zur Übersicht bzw. als admin zum neuen thread
	return create_redirect( "/beitraege/" + uid.get_id() );
	
	

}


/**
	Ein Bild zu einem Artikel hochladen. Das Problem: Es gibt hier noch keine Artikel-ID, es soll auch nicht, wie bei den FAQs,
	jedesmal 'auf Verdacht' eine ID angelegt werden, wenn das Eingabeformular angezeigt wird.
	Lösung: Erst wenn der User ein Bild hochgeladen hat, also hier, wird ein Draft-Artikel angelegt, und zwar nur einer pro
	User. 
	
	Es muss also
	
	- geprüft werden, ob es schon ein draft für den user gibt
	- ggf. einen draft anlegen
	- dessen id holen
	- unter dieser id die bilder speichern
	- beim speichern des artikels von 'neu anlegen' auf 'draft updaten' umschalten
	
*/

dd_result sxpage::on_image_upload( dditem& item )
{
	
	// bereitmachen
	get_body().clear_value();
	ddwebcgi& cgi = get_cgi();
	
	if( cgi.empty() || cgi["sn_filename"].empty() )		
		return rs_ok;
		
	if( get_split_path()[0] == "member" )
		get_site().get_file_manager().create_user_files( *this, cgi );
	else
	// Bei upload wirds dann ernst: fetch_draft_id( true ) erzeugt ggf. neuen eintrag
	get_site().get_file_manager().create_image_files( *this, cgi, fetch_draft_id( true ) );
	
	
	return create_redirect( get_full_path() + "?sx_action=on_image_list" );

}



/**
	Bilder für einen Artikel als JSON-Tree anzeigen.
	
	Ein Objekt mit den EIgenschaften video und Pic.
	Video ist eine liste mit objekten. Jedes objekt enthählt die Infos zu ein 
	Video.
	
	Pic ist ein Objekt was die Informationen über das original bild enthält, sowie 
	eine eigenschaft subpic. Das ist eine liste mit allen Bildern die vom Orig. 
	erstellt wurden in den verschiedenen größen.
	
	{
	'url':'/pfad oder url zu den bildern/videos',
	'video':[
					{'name':'video1.avi','width':'100','height':'100'},
					{'name':'video2.avi','width':'100','height':'100'},
					...
					],
	
	'pic':{
					'name_upload_pic1':{
									'name':'name_upload_pic1.jpg',
									'width':'100',
									'heigh't:'100',
									'size':'0',
									'subpic':[
													{'name':'pic1_80.jpg','width':'10','height':'10','size':'80'},
													{'name':'pic1_20.jpg','width':'100','height':'10','size':'20'},
													...
													]
									},
					'name_upload_pic2':{
									'name':'name_upload_pic2.jpg',
									'width':'100',
									'height':'100',
									'size':'0',
									'subpic':[
													{'name':'pic2_80.jpg','width':'10','height':'10','size':'80'},
													{'name':'pic2_20.jpg','width':'100','height':'10','size':'20'},
													...
													]
									},
					.....
					}
	}
	
*/
/**
	den editor kann man auch frisieren
	<!-- TinyMCE -->
					<script type="text/javascript" src="/tiny_mce/tiny_mce.js"></script>
					<script type="text/javascript">
						
						<!--#extends:sx_editor_plugin_and_theme_load.html-->
						
						tinyMCE.init({
							skin : "supportnet",
							//skin : "o2k7",
							//skin : "cycsown",
							<!--#extends:sx_editor_plugin_and_theme.html-->
				
							<!--#extends:sx_editor_settings.html-->,
							// wild
							snstuff_img_url_list:"/admin_list_pictures_member_json/{artikelid}",
							snstuff_img_url_image:"/memberimage/{artikelid}/{imgname}"
						});
					</script>
					<!-- /TinyMCE -->

*/


dd_result sxpage::on_image_list( dditem& item )
{
	//context.contenttype = "application/json;charset=iso-8859-1";
	//context.contenttype = "text/xml; charset=iso-8859-1";//application/atom+xml";
	context.contenttype = "text/html; charset=iso-8859-1";//application/atom+xml";
	
	item.from_string( "[]" );
	ddioinfo dirinfo;


	// fallunterscheidung: member oder article, bzw. alter oder neuer eintrag ?
	bool is_member_image = ( get_split_path()[0] == "member" );
	ddstring pathid = is_member_image ? get_user_id().get_id() : fetch_draft_id();
	
	ddstring realdir = get_image_path( true );
	if( !is_member_image )
		realdir += "/" + pathid;
		
	try
	{
		dirinfo.init_entries( realdir ); 		
	}
	catch( exception& error )
	{
		::system( "pwd" );
		return rs_ok;
	}
  
 	size_t max = dirinfo.size();
	if( !max )
		return rs_ok;
	
	
	// kommasteuerung: ist es das erste video / bild ?
	bool firstvideo = true;
	bool firstimage = true;
	bool firstsubpic = true;

	ddstring resultvideo, resultimage;
	
	// säubern
	item.clear_value();
	dditem& blockvideo = item["sn_block_video"];	
	dditem& blockimage = item["sn_block_image"];	
	dditem& contentvideo = blockvideo["sn_content"];
	dditem& contentimage = blockimage["sn_content"];
	dditem& contentsubpic = contentimage["sn_subpic"];  		 	
	
	// alles säubern
	blockvideo.clear_value();
	blockimage.clear_value();
	contentvideo.clear_value();
	contentimage.clear_value();

	ddstring fullurl =  "http://" + get_connection().get_request().get_host_port() + "/";
	fullurl += ( is_member_image ? DDUSERPICPATH : DDPICPATH ) + pathid + "/";
	// die basis-url für das Verzeichnis übermitteln
	//item["sn_base_url"].from_string( "http://panik.zonc.net:8088/memberimage/19748/" );	
	item["sn_base_url"].from_string( fullurl );	
	
	//	Ein Verzeichnis kann videos, originalbilder und deren
	//	skalierungen in beliebiger reihenfolge und menge enthalten,		
	
   for( size_t i=0; i<max; ++i )
   {
  
      // den tatsächlichen dateinamen ermitteln ...
      ddstring realname = realdir + "/" + dirinfo[i].get_full_name();		
  	
      // ... und informationen über die datei ermitteln (siehe ddwebfile
      ddwebfile webfile( realname );
		
		// fallunterscheidung: video oder bild?
		if( webfile.is_video_file() )
		{
			// hatten wir schon ein video? dann brauchen
			// ein trennendes komma.
			
			if( !firstvideo )
				resultvideo += ",\n";
			firstvideo = false;
			
			contentvideo["sn_filename"].from_string( dirinfo[i].get_full_name() );
         contentvideo["sn_width"].from_string( webfile.get_media_width() );
         contentvideo["sn_height"].from_string( webfile.get_media_height() );
      
         resultvideo += contentvideo.to_string();
     
			
		}
		else // es ist ein bild
		{
						
			// weitere Fallunterscheidung: ist es das originalbild
			// oder eine skalierung davon? Skalierungen werden ignoriert,
			// aus dem original wird eine eigene struktur, die dann eine
			// list mit den skalierten versionen erhält.
			
			if( webfile.is_rescaled() )
				continue;
				
			// ab hier: wir bauen die struktur für ein originalbild mit
			// seinen skalierungen auf
			
			// hatten wir schon ein bild? dann brauchen
			// ein trennendes komma.
			
			if( !firstimage )
				resultimage += ",\n";
			firstimage = false;
			
			// das original
			contentimage["sn_basename"].from_string( dirinfo[i].get_file_name() ); 
         contentimage["sn_filename"].from_string( dirinfo[i].get_full_name() );
         contentimage["sn_width"].from_string( webfile.get_media_width() );
         contentimage["sn_height"].from_string( webfile.get_media_height() );
      
         // saubermachen
         ddstring resultsubpic;
         contentsubpic.clear_value();  
			
			// jetzt muss die bildliste nochmal nach den skalierungen dieses
         // originals durchsucht werden.      
			const ddstring& origbase = webfile.get_media_base();
         for( size_t x=0; x<max; ++x )
         {
  		 	
  		 	// das original nicht nochmal verwenden
  		 	if( x==i )
  		 		continue;
  		 	
  		 	// der basisfilename (=anfang) muss übereinstimmen 
  		 	if( dirinfo[x].get_file_name().contains( origbase ) )
  		 	{
  		 	
  		 		// hatten wir schon eine skalierung? dann brauchen
					// ein trennendes komma.
								
  		 		if( !firstsubpic )
						resultsubpic += ",\n";
					firstsubpic = false;
      		
      		// dirty: noch ein webfile anlegen
      		ddwebfile subpic( realdir + "/" + dirinfo[x].get_full_name() );		
      		
      		contentsubpic["sn_filename"].from_string( dirinfo[x].get_full_name() );
      		contentsubpic["sn_width"].from_string( subpic.get_media_width() );
      		contentsubpic["sn_height"].from_string( subpic.get_media_height() );
     			contentsubpic["sn_size"].from_string( subpic.get_media_size() );
  		 	
  		 		// sichern
  		 		resultsubpic += contentsubpic.to_string();
  		 		
  		 	}
      
      
      } // subpics
     
      // wenn keine skalierungen vorhanden sind, wird ein leeres array übergeben,
      // sonst den subpic-block sichern
      
      if( firstsubpic )
      	contentsubpic.set_visible( false );
      else
      	contentsubpic.from_string( resultsubpic );
      
      resultimage += contentimage.to_string();
		
			// komma zurücksetzen
			firstsubpic = true;
		} // zweig 'es ist ein bild'
		
  	
	} // loop über alle verzeichniseinträge
	
	// hatten wir videos?
	if( firstvideo )
		blockvideo.set_visible( false );
	else		
		contentvideo.from_string( resultvideo );
	
	// hatten wir bilder?
	if( firstimage )
		blockimage.set_visible( false );
	else
		contentimage.from_string( resultimage );

	cout << item.to_string() << endl;
	
	// und ab
	return rs_ok;

}


/**
	Änderungen speichern.
*/

dd_result sxpage::on_update( dditem& item )
{
	
	
	// ab hier: es wird versucht, einen thread anzulegen.
		
	ddwebcgi& cgi = get_cgi();
	dditem& block = get_body()["sn_form"];
	ddformmanager& mgr = get_site().get_form_manager();
	
	cgi.dump();
	
	// die ID auch in den context schreiben, für die javascript routinen...
	context.id	= cgi["id"];
	
	// daten erst testen...
	if( !mgr.check_form( "check."+get_name(), block, get_user_id(), cgi ) )
		return rs_ok;
	
	// ... overlay die links einbauen...
	if( _use_overlay_links )	
		get_site().get_overlay_links().replace_keys( cgi["sn_msgbody"] );
	
	// ... eventuell die bilder einbauen...
	if( _use_overlay_image )
		get_site().get_overlay_images().replace_keys( cgi["sn_msgbody"], cgi["sn_msgsubject"] );
	
	cgi.dump();
	
	// ...dann speichern...
	get_view().execute( cgi );
	
	// draftID löschen
	get_user_id().clear_property( DDENTRYID );
	
	create_categories();
	
	// PFUSCH
	if( context.pagetype == "forum" )
		return create_redirect( "/t/" + context.id );
	
	// ...und zum thread zurück.
	return create_redirect( "/" + context.pagetype + "thread/" + context.id );
	//return create_redirect( get_full_path() );
	
	
}


/**
	Einen Eintrag freischalten.
*/

dd_result sxpage::on_publish( dditem& item )
{
	// ab hier: es wird versucht, einen thread anzulegen.
	
	ddwebcgi& cgi = get_cgi();
	dditem& block = get_body()["sn_form"];
	ddformmanager& mgr = get_site().get_form_manager();
		
	// daten erst testen...
	if( !mgr.check_form( "check."+get_name(), block, get_user_id(), cgi ) )
		return rs_ok;
	
	
	// 1. Freischalten: state auf public stellen ...
	cgi["id_state"] 	= "1";	
	// ... und das ganze erstmalig in die cache table einfügen
	get_view().execute( cgi );
	
	// 2. Wir verwenden die 'on_update'-methode, um den rest 
	// der Daten zu übertragen
	// das datum wird vorher auf 'jetzt' gestellt.
	cgi["sn_datemodified"] 	= ddbasedate::now().to_string_mysql();
	cgi["sn_datecreated"] 	= ddbasedate::now().to_string_mysql();
	cgi[DDACTIONKEY] 	= "on_update";	
	// ...dann speichern...
	get_view().execute( cgi );
	// anzeige updaten	
	get_site().get_user_content_count() = "";
	
	create_categories();
	
	// ...und zum thread zurück.
	//return create_redirect( get_full_path() );
	return create_redirect( "/" + context.pagetype + "thread/" + context.id );
	
	
	
}


/**
 	Vorschau anzeigen.
*/

dd_result sxpage::on_preview( dditem& item )
{
	
	
	dditem& block = get_body();
	block.clear_children();
	block.clear_value();
	ddwebcgi& cgi = get_cgi();
	
	// FIX! MEGAPFUSCH
	// renderer einstellen
	cgi["sn_nickname"] = get_user_id().get_nick_name();
	cgi["id_user"] = get_user_id().get_id();
	cgi["sn_block_posting_nnf"] = cgi["sn_msgbody"];
	cgi["sn_block_article_nnf"] = cgi["sn_msgbody"];
	cgi["sn_block_teaser"] 			= cgi["sn_msgteaser"];
	
	// ?? bei preview fehlt die id_parent, wenn bilder dabei sind
	if( cgi["id_parent"].empty() )
		cgi["id_parent"] 			= context.id;
	
	// rezepte
	if( !cgi["sn_ingredients"].empty() )
		cgi["sn_recipe_ing"] = cgi["sn_ingredients"];
	
	get_view().on_render_cgi( *this, block );
	
	return rs_ok;
	
}


/**
 	Allgemeiner Einstieg: Das Antwortformular säubern und dann den Thread anzeigen,
	mittels @see on_show_entry, das wird dann überladen in 
	- @see sxpageforumbase
	- @see sxpagefaqs	
*/

dd_result sxpage::on_show( dditem& item )
{
	// draftID löschen
	get_user_id().clear_property( DDENTRYID );
	get_body()["sn_comments"]["sn_form"]["sn_subbody"].clear_children();
	return on_show_entry( item );
}


/**
 	Einen Thread anzeigen (Ohne das Antwortformular zu säubern).
*/

dd_result sxpage::on_show_entry( dditem& item )
{
	
	// erstmal haupeintrag holen & rendern...
	ddwebview& table = get_view( get_name()+".show" );
	get_body().set_visible( true );
	get_body()["sn_content"].set_visible( true );
	
	context.numrows = table.fill( "on_show", context.id, DDADMFILTER );
	
	// exceptions vermeiden
	if( context.numrows <= 0 )
		return create_redirect( "/linknotfound?sn_key=" + get_full_path().dup().escape(), ret_301_moved_permanently );
	
	if( !context.pagetype.empty() && context.pagetype != table[0]["id_type"] )
		return create_error( "Falscher Artikeltyp: " + table[0]["id_type"], ddwebresponse::ret_404_not_found );

	// eventuell vg-wort tag sichern
	if( table.contains_field( "sn_msgvgwort" ) )
		_vgworttag = table[ 0 ]["sn_msgvgwort"];
		
	// thread geschlossen?
	dditem& comform = get_body()["sn_comments"]["sn_form"];	
	create_hint_box( comform, DDHINTCLOSED, table[0].is_closed() );
	
	// ...Artikel anzeigen...
	table.on_render( *this, get_body()["sn_content"], 0, 1 );	
	

	create_header_entries( table, item );
	
	// ab hier: neu
	
	dduserid& uid = get_user_id();
	dditem& comments  = get_body()["sn_comments"];

	// formular säubern
	comments.set_visible( uid.is_member() );
	
	if( !uid.is_member() )
		return rs_ok;
	
	dditem& comblock = comments["sn_content"];
	
	if( context.numrows <= 1 )
	{
		// block ausblenden
		comblock.set_visible( false );
		return rs_ok;
	}
	
	// block wieder einblenden
	comblock.set_visible( true );
	
	// ...anzeigen...
	table.on_render( *this, comblock["sn_msgentry"], context.navifrom+1, context.navito+1 );	
	
	return rs_ok;
	
	
}



/**
	(Nur) Antworten anzeigen.
*/

dd_result sxpage::on_show_replies( dditem& item )
{
	
	// daten holen, sofern nötig...
	ddwebview& table = get_view( get_name()+".show" );	
	
	context.numrows = table.fill( "on_show", context.id, DDADMFILTER );
	
	create_header_entries( table, item );
	
	// title, description und keywords enthalten die ersten 7 worte des ersten comments 	
	if( table.size() > 1 )
	{
		ddstring msgbody = table[1]["sn_block_article"].as_codeless_text();
		size_t i=0;
		size_t words=0;
		size_t bs = msgbody.size();
		while( i < bs && words < 7 )
		{
			// skip blanks
			while( i < bs && isblank( msgbody[i]) )
				++i;
			// skip word
			while( i < bs && !isblank( msgbody[i]) )
				++i;
			if( i < bs )
				words++;
				
		}
		msgbody = msgbody.substr( 0,i ) + "...";
		//context.pagetitle	= "Supportnet " + _typemapper[context.pagetype] + ": " + msgbody;
		context.pagetitle	= msgbody;
		// meta keys frisieren
		// description
		item["sn_meta"]["sn_meta_header"].from_string( msgbody );
		// keyword: subject
		item["sn_meta"]["sn_meta_keyword"].from_string( msgbody );		
	}
	// no comment:
	else
	{
		context.pagetitle	 = "Kommentar schreiben";
		item["sn_meta"]["sn_meta_header"].from_string( "Kommentar schreiben" );
		item["sn_meta"]["sn_meta_keyword"].from_string( "Kommentar schreiben" );	
	}

	
	dditem& comments  = get_body()["sn_comments"];
	comments.set_visible( true );
	
	get_body()["sn_content"].set_visible( false );
	

	dditem& comblock = comments["sn_content"];
		// ...navi basteln ...
	//ddstring base =  "/" + get_split_path()[0] + "/" + context.id + "/";
	//create_navigator( comments["sn_navigator"], base, context.navifrom, context.navito, context.numrows-1 );	

	if( context.numrows <= 1 )
	{
		// block ausblenden
		comblock.set_visible( false );
		return rs_ok;
	}
	
	// block wieder einblenden
	comblock.set_visible( true );
	// ...anzeigen...
	table.on_render( *this, comblock["sn_msgentry"], context.navifrom+1, context.navito+1 );	
	
	return rs_ok;
	
	
}


/**
	Einen Thread schliessen oder wieder öffnen.
*/

dd_result sxpage::on_set_state( dditem& item )
{
	
	// cache eintrag holen, so vorhanden
	bool has_entry = false;
	size_t flags = 0;
	ddwebview& overview = get_view( get_name()+".overview" );
	int idx = overview.index_of( context.id );
	if( idx > -1 && overview[ idx ].contains_key("id_state") )
	{
		has_entry = true;
		flags = overview[ idx ].get_state_flags();
	}
	
	// FIX?
	// das gehört in die table !?!
	
	// query bestimmen...
	ddstring op;
	const ddstring& request = get_cgi()[ DDACTIONKEY ];
	
	if( request == "on_close" )
	{
		op = "id_state |" + ddstrutil::from_size_t( dddatastaterow::data_is_closed );
		flags |= dddatastaterow::data_is_closed;
	
	}
	else if( request == "on_open" )
	{
		op = "id_state & ~" + ddstrutil::from_size_t( dddatastaterow::data_is_closed );
		flags &= ~dddatastaterow::data_is_closed;
	}	
	else if( request == "on_remove" )
	{
		op = "id_state |" + ddstrutil::from_size_t( dddatastaterow::data_is_removed );
		flags = flags & !dddatastaterow::data_is_removed;
	}
	
	// ...ausführen....
	ddwebview& table = get_view();
	table.execute( "on_set_state", op, context.id );	

	// ...und zur Übersicht zurück.
	return create_redirect(  get_full_path() );
	
	(void) item;

}


/**
	Einen Thread als gelöscht markieren.
*/

dd_result sxpage::on_remove( dditem& item )
{
	
	/* 
	 Löschen: public flag löschen und removed flag setzen:
	 state XOR ( removed | public )
		on_remove = "update tr_msgmain set id_state = id_state & ~1 where id_parent = '{id}'
		update tr_msgmain set id_state = id_state|2 where id_parent = '{id}';
		update tr_msgcache set id_state =  id_state^3 where id_parent = '{id}' ";

		QUARK!
		 = 2 BASTA
	*/
	
	get_view().execute( "on_remove", context.id );	
	get_site().get_user_content_count() = "";
	
		
	ddwebcgi& cgi = get_cgi();
	// fallunterscheidung: soll die ip gleich mit geblockt werden?	
	if(cgi[ DDACTIONKEY ] == "on_remove_block" )
	{
		ddstringrowset& list = get_site().get_server().get_blocked_ips();
		ddstring& ip = cgi["sn_x_ip"];
		if( !ip.empty() )
		{
			list.add_entry( ip, "(schnellblocker)" );
			get_site().store_data_entry( DDKEYBLOCKEDIPS, list.to_string() );
		}	
		return create_redirect( "/postings" );
	}
	
	// ...und zur Übersicht zurück.
	return create_redirect(  get_full_path() );
	
	(void) item;
	
}
	
		
/**
	Eine Antwort erzeugen, hier ist die Kommentarfunktion zu Faqs/News etc. gemeint.
*/

dd_result sxpage::on_reply_create( dditem& item )
{
	
	// um einen eintrag zu erzeugen, müssen die automatikfelder
	// geladen sein, dazu muss der entsprechende thread anzeigt gewesen
	// sein.
	
	ddwebcgi& cgi = get_cgi();
	dduserid& uid = get_user_id();
	dditem& block = get_body()["sn_comments"]["sn_form"]["sn_subbody"];
	ddformmanager& mgr = get_site().get_form_manager();
	
	// testen, ob der thread überhaupt offen und gültig ist.
	ddwebview& table = get_view( get_name()+".comments" );
	int flag;
	table.execute( "on_close_check", context.id ).read( flag );
	if( flag )
		create_system_message( "Thread geschlossen oder ung&uuml;ltig!" );
	
	get_body()["sn_content"].clear_value();
	
	// im fehlerfall:
	if( !mgr.check_form( "check.comment", block, get_user_id(), cgi ) || mgr.contains_blocked_words() )
		// hier muss auch die Fallunterscheidung rein: gäste kriegen _nur_ comments, member alles
		return uid.is_guest() ? on_show_replies( item ) : on_show_entry( item );
		
	// alles ok: die fehlenden Daten nachreichen...	
	cgi["id"]						= context.id;
	cgi["sn_senderip"]	= get_connection().get_host_ip();
	cgi["id_user"]			= uid.get_id();
	cgi["sn_nickmail"]	= uid.get_nick_mail();

	cgi.dump();
	
	// hack: bei gästen die anmeldeprozedur starten, dazu die query umbiegen
	// und den ganzen sotter in der tabelle tr_msgtmp speichern.
	
	/*
	
	Daten ab hier:
	
	key: <id> value: <2333390>
	key: <id_group> value: <56>
	key: <id_user> value: <1>
	key: <sn_mailalert> value: <9>
	key: <sn_msgbody> value: <moooooooooo>
	key: <sn_msgsubject> value: <RE: cgi.dump();>
	key: <sn_nickmail> value: <>
	key: <sn_senderip> value: <127.0.0.2>
	key: <sx_action> value: <on_reply_create>

	*/

	if( uid.is_guest() )
	{
		cgi[ "sn_key" ] = uid.get_session_key();
		cgi[ "sn_lastpath" ] = uid.get_property( DDLASTPATH );
		cgi[ DDACTIONKEY ] = "on_tmp_reply_create";
		table.execute( cgi );
		// gäste zum anmelden schicken
		return create_login_message( "Nach dem Anmelden wird ihre Nachricht freigeschaltet." );
	}
	
	
		// und noch eine Fallunterscheidung: Müssen wir einen temporären
	// Beitrag umbiegen oder legen wir einen neuen an?
		
	// Gibt es einen Draft-Eintrag?
	ddstring draftid;
	get_view( "sxpageforumstart.draft").execute( "fetch_draft", uid.get_id() ).read( draftid );
	cgi[ "id_type" ] = "comments";	
	// es gibt keinen 'Entwurf', also stino inserter anwerfen
	if( draftid.empty() )
	{
		table.execute( cgi );
	}
	else
	{
		cgi[ "id" ] = draftid;
		// hier sind wir kind
		cgi[ "id_parent" ] = context.id;
		get_view( "sxpageforumstart.draft").execute( "on_draft_update", cgi );
		
	}
	// draftID löschen
	get_user_id().clear_property( DDENTRYID );	


	/*
		BUG SNSPII-156:
		Aufgrund der langen uptime von 10 tagen wurde die cache-table nicht neu geladen und die betreffenden threads sind da rausgegplumst.
		Im Detail: post von 30.8, letzte antwort 31.8, Serverstart am 22.9 -> thread ist zu alt, kommt nicht in die cache-table
		neue antwort am 29.09 -> thread müsste in die Übersicht, ist aber nicht in der cache table ...
	
		Lösung: Die query
		"update tr_msgcache set sn_childcount = sn_childcount + 1, sn_datemodified = now() where id_parent = '2259573'" schlägt im diesem Fall fehl,
		also die query vereinzeln, das ergebnis testen (get_affected_rows ) und ggf. dann ein "insert into tr_msgcache select ... " hinterherschicken.
	
	*/
	cgi["id"]	= context.id;
	int affrows = table.execute( "on_cache_update", cgi ).read();
	// fehlgeschlagen: dann den datensatz in die cache-table laden
	if( !affrows )
		table.execute( "on_cache_insert", cgi );

	// mailalerts verschicken
	ddmailmanager& mm = get_site().get_mail_manager();
	mm.create_alert_mails( context.id, uid.get_nick_mail(), uid.get_nick_name(), cgi["sn_msgsubject"], get_site().get_name() );	
	
	// den antwort-counter hochzählen	
	if( uid.get_id() != "1" )
		get_user_manager().usr_update_single( uid.get_id(), "sn_numanswers", "sn_numanswers+1" );

	//  alles ok, wir machen einen redirect auf uns selbst
	// _OHNE_ CGI-Daten, die werden nämlich beim reload nochmal gepostet
	
	return create_redirect( get_full_path().trim_pattern( "/docreate" ) + "#jumplast" );
	
}



/**
	Eine Antwort editieren.
*/

dd_result sxpage::on_reply_edit( dditem& item )
{	
	// erstmal holen & rendern...
	ddwebview& table = get_view( get_name()+".comments" );
	dditem& block = get_body()["sn_form"];
	block.clear_children();
	table.execute( "on_reply_edit", get_cgi()["id"] ).read( *this, block );	
	
	get_user_id().set_property( DDENTRYID,  get_cgi()["id"] );

	return rs_ok;
	

}


/**
	Geänderte Antwort speichern
*/

dd_result sxpage::on_reply_update( dditem& item )    
{

	
	ddwebcgi& cgi = get_cgi();
	dditem& block = get_body()["sn_form"];
	//block["sn_action"].from_string( get_full_path() );
	ddformmanager& mgr = get_site().get_form_manager();
	
	// daten erst testen...
	if( !mgr.check_form( "check.comment", block, get_user_id(), cgi ) )
		return rs_ok;
	
	// ...dann speichern...
	ddwebview& table = get_view( get_name()+".comments" );
	table.execute( cgi );
	
	// childcount des elterneintrags updaten
	ddstring count;
	table.execute( "on_childcount", context.id ).read( count );	
	//  speichern	
	table.execute( "on_update_childcount", count, context.id );	
	
	get_user_id().clear_property( DDENTRYID );
	
	// ...und zum thread zurück.
	return create_redirect( get_full_path() );
	
	(void) item;
}


/**
	Eine Antwort löschen.
*/

dd_result sxpage::on_reply_remove( dditem& item ) 
{
	
	ddwebcgi& cgi = get_cgi();
	
	// ...ausführen....
	ddwebview& table = get_view( get_name()+".comments" );
	
	// erst die gültige anzahl kinder berechen
	ddstring& id = cgi["id"];
	ddstring date, count;
	table.execute( "on_fetch_date", context.id, id ).read( count, date );	
	// count ist mit elterneintrag, also eins abziehen und speichern
	table.execute( "on_reply_remove", id, count--, date, context.id );	
	
	// fallunterscheidung: soll die ip gleich mit geblockt werden?	
	if(cgi[ DDACTIONKEY ] == "on_reply_remove_block" )
	{
		ddstringrowset& list = get_site().get_server().get_blocked_ips();
		ddstring& ip = cgi["sn_x_ip"];
		if( !ip.empty() )
		{
			list.add_entry( ip, "(schnellblocker)" );
			get_site().store_data_entry( DDKEYBLOCKEDIPS, list.to_string() );
		}	
		return create_redirect( "/postings" );
	}
	
	// ...und zum thread  zurück	
	return create_redirect( get_full_path() );
	
	(void) item;
}
	

/**
	Benachrichtigungs-Popup wieder löschen
*/

dd_result sxpage::on_close_sysmessage( dditem& item )
{
	get_user_id().clear_system_message();
	return create_redirect(  get_full_path(), ret_302_moved_temporarily );
}






