MediaWiki r13335 - Code Review

Jump to: navigation, search
Repository:MediaWiki
Revision:r13334‎ | r13335 (on ViewVC)‎ | r13336 >
Date:19:03, 26 March 2006
Author:jeluf
Status:old
Tags:
Comment:
Ajax based suggest feature for the search box
Modified paths:

Diff [purge]

Index: trunk/phase3/skins/common/ajax.js
===================================================================
--- trunk/phase3/skins/common/ajax.js	(revision 0)
+++ trunk/phase3/skins/common/ajax.js	(revision 13335)
@@ -0,0 +1,177 @@
+// remote scripting library
+// (c) copyright 2005 modernmethod, inc
+var sajax_debug_mode = false;
+var sajax_request_type = "GET";
+
+var started;
+var typing;
+var memory=null;
+var body=null;
+var oldbody=null;
+
+function sajax_debug(text) {
+	if (sajax_debug_mode)
+		alert("RSD: " + text)
+}
+
+
+function sajax_init_object() {
+	sajax_debug("sajax_init_object() called..")
+	var A;
+	try {
+		A=new ActiveXObject("Msxml2.XMLHTTP");
+	} catch (e) {
+		try {
+			A=new ActiveXObject("Microsoft.XMLHTTP");
+		} catch (oc) {
+			A=null;
+		}
+	}
+	if(!A && typeof XMLHttpRequest != "undefined")
+		A = new XMLHttpRequest();
+	if (!A)
+		sajax_debug("Could not create connection object.");
+	return A;
+}
+
+
+function sajax_do_call(func_name, args) {
+	var i, x, n;
+	var uri;
+	var post_data;
+	uri = wgServer + "/" + wgScriptPath + "/ajax.php";
+	if (sajax_request_type == "GET") {
+		if (uri.indexOf("?") == -1)
+			uri = uri + "?rs=" + escape(func_name);
+		else
+			uri = uri + "&rs=" + escape(func_name);
+		for (i = 0; i < args.length-1; i++)
+			uri = uri + "&rsargs[]=" + escape(args[i]);
+		uri = uri + "&rsrnd=" + new Date().getTime();
+		post_data = null;
+	} else {
+		post_data = "rs=" + escape(func_name);
+		for (i = 0; i < args.length-1; i++)
+			post_data = post_data + "&rsargs[]=" + escape(args[i]);
+	}
+	x = sajax_init_object();
+	x.open(sajax_request_type, uri, true);
+	if (sajax_request_type == "POST") {
+		x.setRequestHeader("Method", "POST " + uri + " HTTP/1.1");
+		x.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+	}
+	x.setRequestHeader("Pragma", "cache=yes");
+	x.setRequestHeader("Cache-Control", "no-transform");
+	x.onreadystatechange = function() {
+		if (x.readyState != 4)
+			return;
+		sajax_debug("received " + x.responseText);
+		var status;
+		var data;
+		status = x.responseText.charAt(0);
+		data = x.responseText.substring(2);
+		if (status == "-")
+			alert("Error: " + data);
+		else
+			args[args.length-1](data);
+	}
+	x.send(post_data);
+	sajax_debug(func_name + " uri = " + uri + "/post = " + post_data);
+	sajax_debug(func_name + " waiting..");
+	delete x;
+}
+
+// Remove the typing barrier to allow call() to complete
+function Search_doneTyping()
+{
+	typing=false;
+}
+
+// Wait 500ms to run call()
+function Searching_Go()
+{
+        setTimeout("Searching_Call()", 500);
+}
+
+// If the user is typing wait until they are done.
+function Search_Typing() {
+	started=true;
+	typing=true;
+	window.status = "Waiting until you're done typing...";
+	setTimeout("Search_doneTyping()", 500);
+
+	// I believe these are needed by IE for when the users press return?
+	if (window.event)
+	{
+		if (event.keyCode == 13)
+		{
+			event.cancelBubble = true;
+			event.returnValue = false;
+		}
+	}
+}
+
+// Set the body div to the results
+function Searching_SetResult(result)
+{
+        //body.innerHTML = result;
+	t = document.getElementById("searchTarget");
+	if ( t == null ) {
+		oldbody=body.innerHTML;
+		body.innerHTML= '<div id="searchTargetContainer"><div id="searchTarget" ></div></div>' ;
+		t = document.getElementById("searchTarget");
+	}
+	t.innerHTML = result;
+	t.style.display='block';
+}
+
+function Searching_Hide_Results()
+{
+	t = document.getElementById("searchTarget");
+	t.style.display='none';
+	body.innerHTML = oldbody;
+}
+
+
+// This will call the php function that will eventually
+// return a results table
+function Searching_Call()
+{
+	var x;
+	Searching_Go();
+
+	//Don't proceed if user is typing
+	if (typing)
+		return;
+
+	x = document.getElementById("searchInput").value;
+
+	// Don't search again if the query is the same
+	if (x==memory)
+		return;
+
+	memory=x;
+	if (started) {
+		// Don't search for blank or < 3 chars.
+		if ((x=="") || (x.length < 3))
+		{
+			return;
+		}
+		x_wfSajaxSearch(x, Searching_SetResult);
+	}
+}
+
+function x_wfSajaxSearch() {
+	sajax_do_call( "wfSajaxSearch", x_wfSajaxSearch.arguments );
+}
+
+	
+//Initialize
+function sajax_onload() {
+	x = document.getElementById( 'searchInput' );
+	x.onkeypress= function() { Search_Typing(); };
+	Searching_Go();
+	body = document.getElementById("content");
+}
+
+hookEvent("load", sajax_onload);

Property changes on: trunk/phase3/skins/common/ajax.js
___________________________________________________________________
Name: svn:eol-style
   + native
Name: svn:keywords
   + Author Date Id Revision

Index: trunk/phase3/skins/MonoBook.php
===================================================================
--- trunk/phase3/skins/MonoBook.php	(revision 13334)
+++ trunk/phase3/skins/MonoBook.php	(revision 13335)
@@ -55,7 +55,6 @@
 	<head>
 		<meta http-equiv="Content-Type" content="<?php $this->text('mimetype') ?>; charset=<?php $this->text('charset') ?>" />
 		<?php $this->html('headlinks') ?>
-		<?php $this->html('headscripts') ?>
 		<title><?php $this->text('pagetitle') ?></title>
 		<style type="text/css" media="screen,projection">/*<![CDATA[*/ @import "<?php $this->text('stylepath') ?>/<?php $this->text('stylename') ?>/main.css?5"; /*]]>*/</style>
 		<link rel="stylesheet" type="text/css" <?php if(empty($this->data['printable']) ) { ?>media="print"<?php } ?> href="<?php $this->text('stylepath') ?>/common/commonPrint.css" />
@@ -82,6 +81,8 @@
 		<script type="<?php $this->text('jsmimetype') ?>"><?php $this->html('userjsprev') ?></script>
 <?php	}
 		if($this->data['trackbackhtml']) print $this->data['trackbackhtml']; ?>
+		<!-- Head Scripts -->
+		<?php $this->html('headscripts') ?>
 	</head>
 <body <?php if($this->data['body_ondblclick']) { ?>ondblclick="<?php $this->text('body_ondblclick') ?>"<?php } ?>
 <?php if($this->data['body_onload'    ]) { ?>onload="<?php     $this->text('body_onload')     ?>"<?php } ?>
Index: trunk/phase3/includes/Article.php
===================================================================
--- trunk/phase3/includes/Article.php	(revision 13334)
+++ trunk/phase3/includes/Article.php	(revision 13335)
@@ -490,7 +490,7 @@
 			}
 			$revision = Revision::newFromId( $this->mLatest );
 			if( is_null( $revision ) ) {
-				wfDebug( "$fname failed to retrieve current page, rev_id $data->page_latest\n" );
+				wfDebug( "$fname failed to retrieve current page, rev_id {$data->page_latest}\n" );
 				return false;
 			}
 		}
Index: trunk/phase3/includes/OutputPage.php
===================================================================
--- trunk/phase3/includes/OutputPage.php	(revision 13334)
+++ trunk/phase3/includes/OutputPage.php	(revision 13335)
@@ -460,6 +460,7 @@
 	function output() {
 		global $wgUser, $wgOutputEncoding;
 		global $wgContLanguageCode, $wgDebugRedirects, $wgMimeType, $wgProfiler;
+		global $wgJsMimeType, $wgStylePath, $wgUseAjax, $wgScriptPath, $wgServer;
 
 		if( $this->mDoNothing ){
 			return;
@@ -468,6 +469,14 @@
 		wfProfileIn( $fname );
 		$sk = $wgUser->getSkin();
 
+		if ( $wgUseAjax ) {
+			$this->addScript( "<script type=\"{$wgJsMimeType}\">
+				var wgScriptPath=\"{$wgScriptPath}\";
+				var wgServer=\"{$wgServer}\";
+			</script>" );
+			$this->addScript( "<script type=\"{$wgJsMimeType}\" src=\"{$wgStylePath}/common/ajax.js\"></script>\n" );
+		}
+
 		if ( '' != $this->mRedirect ) {
 			if( substr( $this->mRedirect, 0, 4 ) != 'http' ) {
 				# Standards require redirect URLs to be absolute
Index: trunk/phase3/includes/AjaxFunctions.php
===================================================================
--- trunk/phase3/includes/AjaxFunctions.php	(revision 0)
+++ trunk/phase3/includes/AjaxFunctions.php	(revision 13335)
@@ -0,0 +1,153 @@
+<?php
+
+if( !defined( 'MEDIAWIKI' ) )
+        die( -1 );
+
+require_once('WebRequest.php');
+
+/**
+ * Function converts an Javascript escaped string back into a string with specified charset (default is UTF-8).
+ * Modified function from http://pure-essence.net/stuff/code/utf8RawUrlDecode.phps
+ *
+ * @param string $source escaped with Javascript's escape() function
+ * @param string $iconv_to destination character set will be used as second paramether in the iconv function. Default is UTF-8.
+ * @return string
+ */
+function js_unescape($source, $iconv_to = 'UTF-8') {
+   $decodedStr = '';
+   $pos = 0;
+   $len = strlen ($source);
+   while ($pos < $len) {
+       $charAt = substr ($source, $pos, 1);
+       if ($charAt == '%') {
+           $pos++;
+           $charAt = substr ($source, $pos, 1);
+           if ($charAt == 'u') {
+               // we got a unicode character
+               $pos++;
+               $unicodeHexVal = substr ($source, $pos, 4);
+               $unicode = hexdec ($unicodeHexVal);
+               $decodedStr .= code2utf($unicode);
+               $pos += 4;
+           }
+           else {
+               // we have an escaped ascii character
+               $hexVal = substr ($source, $pos, 2);
+               $decodedStr .= chr (hexdec ($hexVal));
+               $pos += 2;
+           }
+       }
+       else {
+           $decodedStr .= $charAt;
+           $pos++;
+       }
+   }
+
+   if ($iconv_to != "UTF-8") {
+       $decodedStr = iconv("UTF-8", $iconv_to, $decodedStr);
+   }
+  
+   return $decodedStr;
+}
+
+/**
+ * Function coverts number of utf char into that character.
+ * Function taken from: http://sk2.php.net/manual/en/function.utf8-encode.php#49336
+ *
+ * @param int $num
+ * @return utf8char
+ */
+function code2utf($num){
+   if ( $num<128 )
+   	return chr($num);
+   if ( $num<2048 )
+   	return chr(($num>>6)+192).chr(($num&63)+128);
+   if ( $num<65536 )
+   	return chr(($num>>12)+224).chr((($num>>6)&63)+128).chr(($num&63)+128);
+   if ( $num<2097152 )
+   	return chr(($num>>18)+240).chr((($num>>12)&63)+128).chr((($num>>6)&63)+128) .chr(($num&63)+128);
+   return '';
+}
+
+class AjaxCachePolicy {
+	var $policy;
+
+	function AjaxCachePolicy( $policy = null ) {
+		$this->policy = $policy;
+	}
+
+	function setPolicy( $policy ) {
+		$this->policy = $policy;
+	}
+
+	function writeHeader() {
+		header ("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
+		if ( is_null( $this->policy ) ) {
+			// Bust cache in the head
+			header ("Expires: Mon, 26 Jul 1997 05:00:00 GMT");    // Date in the past
+			// always modified
+			header ("Cache-Control: no-cache, must-revalidate");  // HTTP/1.1
+			header ("Pragma: no-cache");                          // HTTP/1.0
+		} else {
+			header ("Expires: " . gmdate( "D, d M Y H:i:s", time() + $this->policy ) . " GMT");
+			header ("Cache-Control: public,max-age={$this->policy}");
+		}
+	}
+}
+			
+
+function wfSajaxSearch( $term ) {
+	global $wgContLang, $wgUser, $wgRequest, $wgAjaxCachePolicy;
+	$limit = 16;
+	
+	$l = new Linker;
+
+	$term = str_replace( ' ', '_', $wgContLang->ucfirst( 
+			$wgContLang->checkTitleEncoding( $wgContLang->recodeInput( js_unescape( $term ) ) )
+		) );
+
+	if ( strlen( str_replace( '_', '', $term ) )<3 )
+		return;
+
+	$wgAjaxCachePolicy->setPolicy( 30*60 );
+
+	$db =& wfGetDB( DB_SLAVE );
+	$res = $db->select( 'page', 'page_title',
+			array(  'page_namespace' => 0,
+				"page_title LIKE '". $db->strencode( $term) ."%'" ),
+				"wfSajaxSearch",
+				array( 'LIMIT' => $limit+1 )
+			);
+
+	$r = "";
+
+	$i=0;
+	while ( ( $row = $db->fetchObject( $res ) ) && ( ++$i <= $limit ) ) {
+		$nt = Title::newFromDBkey( $row->page_title );
+		$r .= '<li>' . $l->makeKnownLinkObj( $nt ) . "</li>\n";
+	}
+	if ( $i > $limit ) {
+		$more = '<i>' .  $l->makeKnownLink( $wgContLang->specialPage( "Allpages" ),
+		                                wfMsg('moredotdotdot'),
+		                                "namespace=0&from=" . wfUrlEncode ( $term ) ) .
+			'</i>';
+	} else {
+		$more = '';
+	}
+
+	$term = htmlspecialchars( $term );
+	return '<div style="float:right; border:solid 1px black;background:gainsboro;padding:2px;"><a onclick="Searching_Hide_Results();">' 
+		. wfMsg( 'hideresults' ) . '</a></div>'
+		. '<h1 class="firstHeading">'.wfMsg('search')
+		. '</h1><div id="contentSub">'.wfMsg('searchquery', $term) . '</div><ul><li>'
+		. $l->makeKnownLink( $wgContLang->specialPage( 'Search' ),
+					wfMsg( 'searchcontaining', $term ),
+					"search=$term&fulltext=Search" )
+		. '</li><li>' . $l->makeKnownLink( $wgContLang->specialPage( 'Search' ),
+					wfMsg( 'searchnamed', $term ) ,
+					"search=$term&go=Go" )
+		. "</li></ul><h2>" . wfMsg( 'articletitles', $term ) . "</h2>"
+		. '<ul>' .$r .'</ul>'.$more;
+}
+
+?>

Property changes on: trunk/phase3/includes/AjaxFunctions.php
___________________________________________________________________
Name: svn:eol-style
   + native
Name: svn:keywords
   + Author Date Id Revision

Index: trunk/phase3/includes/DefaultSettings.php
===================================================================
--- trunk/phase3/includes/DefaultSettings.php	(revision 13334)
+++ trunk/phase3/includes/DefaultSettings.php	(revision 13335)
@@ -1897,5 +1897,10 @@
  */
 $wgJobLogFile = false;
 
+/**
+ * Enable use of AJAX features, currently auto suggestion for the search bar
+ */
+$wgUseAjax = false;
 
+
 ?>
Index: trunk/phase3/ajax.php
===================================================================
--- trunk/phase3/ajax.php	(revision 0)
+++ trunk/phase3/ajax.php	(revision 13335)
@@ -0,0 +1,71 @@
+<?php
+
+$wgRequestTime = microtime();
+
+unset( $IP );
+@ini_set( 'allow_url_fopen', 0 ); # For security...
+
+# Valid web server entry point, enable includes.
+# Please don't move this line to includes/Defines.php. This line essentially defines
+# a valid entry point. If you put it in includes/Defines.php, then any script that includes
+# it becomes an entry point, thereby defeating its purpose.
+define( 'MEDIAWIKI', true );
+require_once( './includes/Defines.php' );
+require_once( './LocalSettings.php' );
+require_once( 'includes/Setup.php' );
+require_once( 'includes/AjaxFunctions.php' );
+
+if ( ! $wgUseAjax ) {
+	die ( -1 );
+}
+
+wfProfileIn( 'main-misc-setup' );
+
+header( 'Content-Type: text/html; charset=utf-8', true );
+
+// List of exported PHP functions
+$sajax_export_list = array( 'wfSajaxSearch' );
+
+$mode = "";
+
+$wgAjaxCachePolicy = new AjaxCachePolicy();
+
+if (! empty($_GET["rs"])) {
+	$mode = "get";
+}
+
+if (!empty($_POST["rs"])) {
+	$mode = "post";
+}
+
+if (empty($mode)) {
+	return;
+}
+
+if ($mode == "get") {
+	$func_name = $_GET["rs"];
+	if (! empty($_GET["rsargs"])) {
+		$args = $_GET["rsargs"];
+	} else {
+		$args = array();
+	}
+} else {
+	$func_name = $_POST["rs"];
+	if (! empty($_POST["rsargs"])) {
+		$args = $_POST["rsargs"];
+	} else {
+		$args = array();
+	}
+}
+
+if (! in_array($func_name, $sajax_export_list)) {
+	echo "-:$func_name not callable";
+} else {
+	echo "+:";
+	$result = call_user_func_array($func_name, $args);
+	$wgAjaxCachePolicy->writeHeader();
+	echo $result;
+}
+exit;
+
+?>

Property changes on: trunk/phase3/ajax.php
___________________________________________________________________
Name: svn:eol-style
   + native
Name: svn:keywords
   + Author Date Id Revision

Index: trunk/phase3/README
===================================================================
--- trunk/phase3/README	(revision 13334)
+++ trunk/phase3/README	(revision 13335)
@@ -49,6 +49,16 @@
 also released into the public domain, which does not impair the obligations of
 users under the GPL for use of the whole code or other sections thereof.
 
+[2] MediaWiki makes use of the Sajax Toolkit by modernmethod,
+	http://www.modernmethod.com/sajax/
+    which has the following license:
+
+       'This work is licensed under the Creative Commons Attribution
+        License. To view a copy of this license, visit
+        http://creativecommons.org/licenses/by/2.0/ or send a letter
+        to Creative Commons, 559 Nathan Abbott Way,
+        Stanford, California 94305, USA.'
+
 Many thanks to the Wikipedia regulars for testing and suggestions.
 
 The official website for mediawiki is located at:
Index: trunk/phase3/RELEASE-NOTES
===================================================================
--- trunk/phase3/RELEASE-NOTES	(revision 13334)
+++ trunk/phase3/RELEASE-NOTES	(revision 13335)
@@ -717,6 +717,8 @@
 * (bug 4729) Add user preference that marks a user's edits as patrolled if user is able to
 * (bug 4497,4704,5010) Added some new language codes.
 * (bug 4630) Add user preference to prompt users when entering blank edit summaries
+* Added optional suggest feature for the search box. Set wgUseAjax to true to
+  enable it.
 
 === Caveats ===
 
Index: trunk/phase3/languages/Messages.php
===================================================================
--- trunk/phase3/languages/Messages.php	(revision 13334)
+++ trunk/phase3/languages/Messages.php	(revision 13335)
@@ -1986,6 +1986,10 @@
 
 'youhavenewmessagesmulti' => "You have new messages on $1",
 'newtalkseperator' => ',_',
+'searchcontaining' => "Search for articles containing ''$1''.",
+'searchnamed' => "Search for articles named ''$1''.",
+'articletitles' => "Articles starting with ''$1''",
+'hideresults' => 'Hide results',
 
 );
 
Index: trunk/phase3/languages/MessagesDe.php
===================================================================
--- trunk/phase3/languages/MessagesDe.php	(revision 13334)
+++ trunk/phase3/languages/MessagesDe.php	(revision 13335)
@@ -1127,9 +1127,14 @@
 Wenn Sie *nicht* $2 sind, folgen Sie dem Link bitte nicht.
 
 Der Bestätigungskode läuft am $4 ab.
-"
+",
+'searchcontaining' => "Suche nach Artikeln, in denen ''$1'' vorkommt.",
+'searchnamed' => "Suche nach Artikeln, deren Name ''$1'' enthält.",
+'articletitles' => "Artikel, die mit ''$1'' beginnen",
+'hideresults' => 'Verbergen',
 
+
 );
 
 
-?>
\ No newline at end of file
+?>

Status & tagging log

  • 01:58, 13 October 2010 ^demon (Talk | contribs) changed the status of r13335 [removed: new added: old]
Personal tools
Namespaces
Variants
Views
Actions
Site
Support
Download
Development
Communication
Toolbox