<?php
/*
Plugin Name: WordPress AutoLink
Plugin URI: http://rudd-o.com/projects/wp-autolink/
Description: Lets you break free from long, absolute URLs in your blog — <a href="http://rudd-o.com/projects/wp-autolink/wp-autolink-usage/">Usage instructions</a>
Version: 0.2.0
Author: Manuel Amador (Rudd-O)
Author URI: http://rudd-o.com/
*/

/*  Copyright 2005  Manuel Amador

Derivative work from bsuite, written by Casey Bisson

    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.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/* TODO
maybe blog: para paginas a partir del blog, y home: para paginas a partir del home
maybe enhance autolink to support {post:}-type links
make post: work on embedded javascript events
*/

require_once(dirname(__FILE__)."/tagmogrify/tagmogrify.php");

/* start defines */

$autolink_recognized_html_attributes = array("href","src","cite");
$autolink_recognized_js_attributes = array("onclick","onfocus","onblur","onmouseover","onmouseout","onchange","ondblclick");

/* end defines */


#this may be useful in the short-term future
#$path = plugin_basename(__FILE__); <- for including the mogrify php file


/* the code for displaying warnings to the user in the post editor */
/* this code is shared with autolink but namespaced to prevent clashes */

$autolink_warnings = "";
$autolink_warnings_displayed = false;

function autolink_warning_footer() { global $autolink_warnings; echo $autolink_warnings; }
function autolink_display_warning($warning_id,$title,$contents) {
	global $autolink_warnings;
	$autolink_warnings .= "
	<div id='$warning_id' class='updated fade-ff0000'><p><strong>".$title.".</strong> ".$contents."</p></div>
	";
	$autolink_warnings .= '<script>';
	$autolink_warnings .= "var m = document.getElementById('$warning_id');";
	$autolink_warnings .= "var ps = document.getElementById('poststuff');";
	$autolink_warnings .= "ps.insertBefore(m,ps.firstChild);";
	$autolink_warnings .= '</script>';

	global $autolink_warnings_displayed;
	if (!$autolink_warnings_displayed) {
		add_action('admin_footer', 'autolink_warning_footer');
		$autolink_warnings_displayed = true;
	}
}

/* end admin communication code */

/* start utility functions */

/* I UNDERSTAND what's the purpose of this function.  I just don't understand why you guys (WP devels) NEEDED to place the content_edit_pre filter *after* this piece of shit code.  content_edit_pre is useless for this very reason.  */

function autolink_unhtmlspecialchars ($content) {
	$trans = get_html_translation_table(HTML_SPECIALCHARS);
	$trans = array_flip($trans);
	return strtr($content, $trans);
}

function autolink_undo_format_to_edit_shitload($content) {
	$richedit = ( 'true' == get_user_option('rich_editing') ) ? true : false;
	if (! $richedit ) $content = autolink_unhtmlspecialchars($content);
	return $content;
} function autolink_uftes($content) { return autolink_undo_format_to_edit_shitload($content); }

function autolink_redo_format_to_edit_shitload($content) {
	$richedit = ( 'true' == get_user_option('rich_editing') ) ? true : false;
	if (! $richedit ) $content = htmlspecialchars($content);
	return $content;
} function autolink_rftes($content) { return autolink_redo_format_to_edit_shitload($content); }

function autolink_get_post_permalink($handle) {

	global $wpdb;

	if (intval($handle)."" === $handle) { /* we have a numeric post link */
		$sql = "SELECT ID FROM $wpdb->posts WHERE ID=$handle limit 1;";
	} else { /* we have a text post link */
		$handle = $wpdb->escape($handle);
		$sql = "SELECT ID FROM $wpdb->posts WHERE post_name='$handle' limit 1;";
	}
	/* to verify its existence in the database */
	$post_id = $wpdb->get_var( $sql );
	if ($post_id) {
		return array($post_id,get_permalink($post_id));
	}

}

function autolink_get_the_title_and_excerpt($post_id) {
	global $wpdb;
	$title = apply_filters('the_title', get_the_title($post_id));

/*	$password = $wpdb->get_var( "SELECT post_password FROM $wpdb->posts WHERE ID=$post_id limit 1;" );
	if ( !empty($password) && $_COOKIE['wp-postpass_'.COOKIEHASH] != $password ) {
		$excerpt = "";
	} else {
		$excerpt = $wpdb->get_var( "SELECT post_excerpt FROM $wpdb->posts WHERE ID=$post_id limit 1;" );
		if (!$excerpt) {
			$sql = "SELECT post_content FROM $wpdb->posts WHERE ID=$post_id limit 1;";
			$excerpt = $wpdb->get_var( $sql );
		}
		if ($excerpt) {
			$excerpt = apply_filters('the_excerpt',$excerpt);
		}
	}
*/
	$output = $title;
// 	if ($excerpt) $output .= ": $excerpt"; // disabled (and above as well) because it brings disallowed html entities into attribute values
// die("micifuz".htmlspecialchars($output));
	return $output;
}


function autolink_get_content_url($handle,$minusslash=false) {

	if (!$handle && $minusslash) { return get_settings("fileupload_url"); }
	return get_settings("fileupload_url") . "/" . $handle;

}

function autolink_get_weblog_url($handle,$minusslash=false) {

	if (!$handle && $minusslash) { return get_settings("siteurl"); }
	return get_settings("siteurl") . "/" . $handle;

}

function autolink_refers_to_this_weblog($url) {
	$weblog_url = autolink_get_weblog_url("");
	if (substr($url,0,strlen($weblog_url)) == $weblog_url) return true;

	$splits = @parse_url($url);
	$splits2 = @parse_url($weblog_url);
	foreach (array("scheme","host","port","user","pass") as $component) {
		// equalize in case the supplied url by the user was a partial url
		if (!$splits[$component]) $splits[$component] = $splits2[$component];
		if ($splits[$component] != $splits2[$component]) return false;
	}
	$weblog_path = $splits2["path"];
	$url_path = $splits["path"];
	if (substr($url_path,0,strlen($weblog_path)) == $weblog_path) return true;
	return false;
}

function autolink_get_querystring_from_permalink($permalink) {
	$permalink = substr($permalink,strlen(autolink_get_weblog_url("")),strlen($permalink));
	global $wp_rewrite;
	$rewrite = $wp_rewrite->wp_rewrite_rules();
	foreach($rewrite as $match=>$query) {
// 		print($match."---".$query."<br>");
		if (preg_match("!^$match!", $permalink, $matches) || preg_match("!^$match!", urldecode($permalink), $matches)) {
			$matched_rule = $match;
			$query = preg_replace("!^.+\?!", '', $query);
			$query = preg_replace("!^.+\?!", '', $query);
			eval("\$query = \"$query\";");
			return $query;
		}
	}
}

function autolink_get_querystring_from_url($presumed_permalink) {
	$splits = @parse_url($presumed_permalink); $query = $splits["query"];
	if ($query) { //if there is a querystring, which might be from this WP installation
		// we test them to see if the supplied URL refers to a resource in this weblog
		$splits2 = @parse_url(autolink_get_weblog_url(""));
		$url_path = $splits["path"]; $weblog_path = $splits2["path"];
		$possible_paths = array($weblog_path,$weblog_path."index.php",$weblog_path."index.php/");
		// if the path in the url corresponds to the index.php in some way, then grab the querystring
		if (in_array($url_path,$possible_paths)) return $query;
	}
}

function autolink_get_post_shortcut_from_querystring($querystring) {
	$post_shortcut = null;
	parse_str($querystring,$parameters);
	foreach($parameters as $key=>$val) {
		if ($parameters[$key] == "") unset($parameters[$key]);
	}

	if (count($parameters) > 0) { // if there is at least one parameter present
		if (array_key_exists("pagename",$parameters) && $parameters["pagename"]) { // if there is a page name
			$parameters["pagename"] = preg_replace("|/+$|","",$parameters["pagename"]);
			$splits = explode("/",$parameters["pagename"]);
			if (count($splits)) { // and that page name can be parsed into separate entities,
				$slug = $splits[count($splits) - 1]; // we take the last to be the slug
				$slug = strtolower(urlencode($slug));
				list($post_id,$throwawayvalue) = autolink_get_post_permalink($slug);
				if ($post_id) { //if there actually is a post which corresponds to that slug
					$post_shortcut = "post:$slug"; // we finally determine the post slug
				}
			}
		}
		if (array_key_exists("page",$parameters) && $parameters["page"]) { // if there is a page name
			$parameters["page"] = preg_replace("|/+$|","",$parameters["page"]);
			$slug = $parameters["page"]; // we take the name to be the slug
			$slug = strtolower(urlencode($slug));
			list($post_id,$throwawayvalue) = autolink_get_post_permalink($slug);
			if ($post_id) { //if there actually is a post which corresponds to that slug
				$post_shortcut = "post:$slug"; // we finally determine the post slug
			}
		}
		if (array_key_exists("p",$parameters) && $parameters["p"]) { // if there is a page name
			$parameters["p"] = preg_replace("|/+$|","",$parameters["p"]);
			$id = $parameters["p"]; // we take the name to be the slug
			list($post_id,$throwawayvalue) = autolink_get_post_permalink($id);
			if ($post_id) { //if there actually is a post which corresponds to that slug
				$post_shortcut = "post:$id"; // we finally determine the post slug
			}
		}
		if (array_key_exists("page_id",$parameters) && $parameters["page_id"]) { // if there is a page name
			$parameters["page_id"] = preg_replace("|/+$|","",$parameters["page_id"]);
			$id = $parameters["page_id"]; // we take the name to be the slug
			list($post_id,$throwawayvalue) = autolink_get_post_permalink($id);
			if ($post_id) { //if there actually is a post which corresponds to that slug
				$post_shortcut = "post:$id"; // we finally determine the post slug
			}
		}
	}
	return $post_shortcut;
}

function autolink_get_entity_html() {

	$trans = get_html_translation_table(HTML_ENTITIES);
	$entityxml = '<!DOCTYPE chapter SYSTEM "/just/a/test.dtd" [' . "\n";
	$counter = 1;
	foreach($trans as $char=>$entityname) {
		$entityname = str_replace("&","",$entityname);
		$entityname = str_replace(";","",$entityname);
		$char = "$counter";
		if (in_array($entityname,array("amp","quot","lt","gt"))) continue;
		$entityxml .= "<!ENTITY $entityname \"$char\">\n";
		$counter = $counter + 1;
	}
	
	$entityxml .= "]>\n";
	return $entityxml;

}

function autolink_simple_mogrifier_factory($function_name,$mogrificator_type) {
	$mogrificator_type = "add_{$mogrificator_type}_mogrificator";
	$m = new TagMogrifier(get_settings('blog_charset'),$fatal_errors=false,autolink_get_entity_html());
	$m->$mogrificator_type($function_name);
	return $m;
}

/* end utility functions */

/* start rendering routines post:, content: -> http://... */

function autolink_render_html_post_links(&$attribute,$titles=true) {

	$attrvalue = $attribute->nodeValue;

	if (substr($attrvalue,0,strlen('post:')) == "post:") {
		/* we have a post link */
		$handle = substr($attrvalue,strlen('post:'),strlen($attrvalue));
		
		$splits = explode("#",$handle,2);
		$handle = $splits[0];
		if (count($splits) == 2) { /* there WAS an anchor */ $anchor = $splits[1];}
		else { $anchor = null; }

		list($post_id,$permalink) = autolink_get_post_permalink($handle);

		if ($permalink) {
			$attrvalue = apply_filters('the_permalink', $permalink );
			if ($anchor) {
				$attrvalue .= "#" . $anchor;
			}
			$tag = $attribute->parentNode;
			if (strtolower($attribute->nodeName) == "href" && !$tag->getAttribute("title") && $titles == true) { /* we only autofill the title if it's an a href link and it already does not have a title and if titles were explicitly requested */
				$tag->setAttribute("title",autolink_get_the_title_and_excerpt($post_id));
			}
		}
	}

	$attribute->nodeValue = $attrvalue;

}

function autolink_render_html_post_links_notitles(&$attribute) { return autolink_render_html_post_links($attribute,$titles=false); }

function autolink_render_html_content_links(&$attribute) {

	$attrvalue = $attribute->nodeValue;

	if (substr($attrvalue,0,strlen('content:')) == "content:") {
		/* we have a content link */
		$handle = substr($attrvalue,strlen('content:'),strlen($attrvalue));
		$attrvalue = autolink_get_content_url($handle);
	}

	$attribute->nodeValue = $attrvalue;

}

function autolink_render_js_content_links(&$attribute) {

	$attrvalue = $attribute->nodeValue;

	$attrvalue = str_replace("'content:", "'" . autolink_get_content_url("") , $attrvalue);
	$attrvalue = str_replace("\"content:", "\"" . autolink_get_content_url("") , $attrvalue);
	
	$attribute->nodeValue = $attrvalue;

}

function autolink_render($content,$for_editing) {

	global $autolink_recognized_html_attributes,$autolink_recognized_js_attributes;

	// html post links
	if ($for_editing) $m1 = autolink_simple_mogrifier_factory("autolink_render_html_post_links_notitles","attribute");
	else $m1 = autolink_simple_mogrifier_factory("autolink_render_html_post_links","attribute");
	foreach ($autolink_recognized_html_attributes as $attribute) {
		$m1->add_attribute_match(null,"istring:$attribute","regexp:/^post:/");
	}

	// html content links
	$m2 = autolink_simple_mogrifier_factory("autolink_render_html_content_links","attribute");
	foreach ($autolink_recognized_html_attributes as $attribute) {
		$m2->add_attribute_match(null,"istring:$attribute","regexp:/^content:/");
	}

	// js content links
	$m3 = autolink_simple_mogrifier_factory("autolink_render_js_content_links","attribute");
	foreach ($autolink_recognized_js_attributes as $attribute) {
		$m3->add_attribute_match(null,"istring:$attribute","regexp:/'content:/");
		$m3->add_attribute_match(null,"istring:$attribute","regexp:/\"content:/");
	}

	$newcontent = @$m3->run(@$m2->run(@$m1->run($content)));
	$warning = "<!-- AutoLink could not process this post because it has invalid HTML.  Please fix it. -->";
	if ($newcontent === false) {
		if ($for_editing) {
			// show a warning, dood
			autolink_display_warning("autolink-warning",__('AutoLink cannot process this post'),sprintf(__('This post contains invalid HTML.  AutoLink cannot process posts with invalid HTML.  Please fix it, and then hit %1$s.'), __("Save and Continue Editing")));
		}
		return $content;
	}
	return $newcontent;

}

function autolink_render_for_display($content) { return autolink_render($content,$for_editing=false); }
function autolink_render_for_editing($content) { return autolink_render($content,$for_editing=true); }

/* end rendering routines post:, content: -> http://... */

/* start simplification routines http://... -> content:, post: */

function autolink_simplify_html_post_links ($attribute) {

	$attrvalue = $attribute->nodeValue;
	
	if (autolink_refers_to_this_weblog($attrvalue)) {
		/* we have a link to the weblog, let's see if it's to a post */

		/* we save the anchor */
		$splits = explode("#",$attrvalue,2);
		if (count($splits) == 1) {
			$presumed_permalink = $attrvalue;
			$anchor = null;
		} else
			list($presumed_permalink,$anchor) = $splits;

		/* we attempt to match to querystring rules */
		$querystring = autolink_get_querystring_from_permalink($presumed_permalink);
			/* if matching against permalink structure failed, we make one last attempt to get a querystring */
		if (!$querystring) $querystring = autolink_get_querystring_from_url($presumed_permalink);

		if ($querystring) {
			$shortcut = autolink_get_post_shortcut_from_querystring($querystring);
			if ($shortcut) {
				$attrvalue = $shortcut;
				/* if we had an anchor, we append it to the post shortcut link */
				if ($anchor) $attrvalue .= "#" . $anchor;

				/* andwe finally store the node value */
				$attribute->nodeValue = $attrvalue;
			}
		}
	}

}

function autolink_simplify_html_content_links ($attribute) {

	$attrvalue = $attribute->nodeValue;

	$content_url = autolink_get_content_url("");

	if (substr($attrvalue,0,strlen($content_url)) == $content_url) {
		/* we have a content link */
		$handle = substr($attrvalue,strlen($content_url),strlen($attrvalue)); /* which should give me the rest */
		$attrvalue = "content:" . $handle;
	} elseif ($attrvalue == $content_url) {
		$attrvalue = "content:";
	}

	$content_url = autolink_get_content_url("",$minusslash=true);

	// in case they forget the trailing slash
	if ($attrvalue == $content_url) {
		$attrvalue = "content:";
	}

	$attribute->nodeValue = $attrvalue;

}

function autolink_simplify_js_content_links(&$attribute) {

	$attrvalue = $attribute->nodeValue;

	$attrvalue = str_replace("'" . autolink_get_content_url("") , "'content:" , $attrvalue);
	$attrvalue = str_replace("\"" . autolink_get_content_url("") , "\"content:" , $attrvalue);
	// in case they forget the trailing slash
	$attrvalue = str_replace("'" . autolink_get_content_url("",$minusslash=true) . "'" , "'content:'" , $attrvalue);
	$attrvalue = str_replace("\"" . autolink_get_content_url("",$minusslash=true) . "\"" , "\"content:\"" , $attrvalue);
	
	$attribute->nodeValue = $attrvalue;

}

function autolink_simplify($content) {

	global $autolink_recognized_html_attributes,$autolink_recognized_js_attributes;

	// html post links
	$m1 = autolink_simple_mogrifier_factory("autolink_simplify_html_post_links","attribute");
	foreach ($autolink_recognized_html_attributes as $attribute) {
		$m1->add_attribute_match(null,"istring:$attribute",null);
	}

	// html content links
	$m2 = autolink_simple_mogrifier_factory("autolink_simplify_html_content_links","attribute");
	foreach ($autolink_recognized_html_attributes as $attribute) {
		$m2->add_attribute_match(null,"istring:$attribute",null);
	}

	// js content links
	$m3 = autolink_simple_mogrifier_factory("autolink_simplify_js_content_links","attribute");
	foreach ($autolink_recognized_js_attributes as $attribute) {
		$m3->add_attribute_match(null,"istring:$attribute",null);
	}

	$newcontent = @$m3->run(@$m2->run(@$m1->run($content)));
	if ($newcontent === false) return $content;
	return $newcontent;

}

/* end simplification routines http://... -> content:, post: */

/* start setup */

function autolink_filter_content($content) { return autolink_render_for_display($content); }
function autolink_filter_excerpt($content) { return autolink_render_for_display($content); }
function autolink_filter_excerpt_rss($content) { return autolink_render_for_display($content); }
function autolink_filter_content_save_pre($content) { return addslashes(autolink_simplify(stripslashes($content))); }
function autolink_filter_excerpt_save_pre($content) { return addslashes(autolink_simplify(stripslashes($content))); }
function autolink_filter_format_to_edit($content) {
// 	die(htmlspecialchars($content));
	$content = autolink_uftes($content);
	$content = autolink_render_for_editing($content);
	$content = autolink_rftes($content);
	/* first undo the format_to_edit, then pass through the renderer, then redo the format_to_edit munging */
	/* FIXME there's gotta be a better way to do this! And I don't mean that in a Perlish way! */
	return $content;
}

add_filter('the_content', 'autolink_filter_content');
add_filter('the_excerpt', 'autolink_filter_excerpt');
add_filter('the_excerpt_rss', 'autolink_filter_excerpt_rss');
add_filter('content_save_pre', 'autolink_filter_content_save_pre',0);
add_filter('excerpt_save_pre', 'autolink_filter_excerpt_save_pre',0);
add_filter('content_edit_pre', 'autolink_filter_format_to_edit',0);

/* end setup */

?>