Index: inc/core/class.dc.blog.php
===================================================================
--- inc/core/class.dc.blog.php	(revision 1570)
+++ inc/core/class.dc.blog.php	(revision 1620)
@@ -81,5 +81,5 @@
 			$this->desc = $b->blog_desc;
 			$this->url = $b->blog_url;
-			$this->host = preg_replace('|^([a-z]{3,}://)(.*?)/.*$|','$1$2',$this->url);
+			$this->host = http::getHostFromURL($this->url);
 			$this->creadt = strtotime($b->blog_creadt);
 			$this->upddt = strtotime($b->blog_upddt);
@@ -91,13 +91,13 @@
 			$this->public_path = path::fullFromRoot($this->settings->system->public_path,DC_ROOT);
 			
-			$this->post_status['-2'] = __('pending');
-			$this->post_status['-1'] = __('scheduled');
-			$this->post_status['0'] = __('unpublished');
-			$this->post_status['1'] = __('published');
-			
-			$this->comment_status['-2'] = __('junk');
-			$this->comment_status['-1'] = __('pending');
-			$this->comment_status['0'] = __('unpublished');
-			$this->comment_status['1'] = __('published');
+			$this->post_status['-2'] = __('Pending');
+			$this->post_status['-1'] = __('Scheduled');
+			$this->post_status['0'] = __('Unpublished');
+			$this->post_status['1'] = __('Published');
+			
+			$this->comment_status['-2'] = __('Junk');
+			$this->comment_status['-1'] = __('Pending');
+			$this->comment_status['0'] = __('Unpublished');
+			$this->comment_status['1'] = __('Published');
 			
 			# --BEHAVIOR-- coreBlogConstruct
@@ -300,5 +300,9 @@
 		$counter = $this->getCategoriesCounter($c_params);
 		
-		$without_empty = $this->core->auth->userID() == false; # For public display
+		if (isset($params['without_empty']) && ($params['without_empty'] == false)) {
+			$without_empty = false;
+		} else {
+			$without_empty = $this->core->auth->userID() == false; # Get all categories if in admin display
+		}
 		
 		$start = isset($params['start']) ? (integer) $params['start'] : 0;
@@ -550,4 +554,17 @@
 		$this->triggerBlog();
 	}
+
+        /**
+        Set category position
+
+        @param  id              <b>integer</b>          Category ID
+        @param  left            <b>integer</b>          Category ID before
+        @param  right           <b>integer</b>          Category ID after
+        */
+        public function updCategoryPosition($id,$left,$right)
+        {
+                $this->categories()->updatePosition($id,$left,$right);
+                $this->triggerBlog();
+        }
 	
 	/**
Index: inc/core/class.dc.categories.php
===================================================================
--- inc/core/class.dc.categories.php	(revision 1179)
+++ inc/core/class.dc.categories.php	(revision 1532)
@@ -188,4 +188,22 @@
 	}
 	
+        public function updatePosition($id,$left,$right)
+        {
+                $sql = 'UPDATE '.$this->table.' SET '
+                        .$this->f_left.' = '.$left.', '
+                        .$this->f_right.' = '.$right
+                        .' WHERE '.$this->f_id .' = '.(integer) $id
+                        .$this->getCondition();
+
+                $this->con->begin();
+                try {
+                        $this->con->execute($sql);
+                        $this->con->commit();
+                } catch (Exception $e) {
+                        $this->con->rollback();
+                        throw $e;
+                }
+        }
+
 	public function deleteNode($node,$keep_children=true)
 	{
Index: inc/core/class.dc.media.php
===================================================================
--- inc/core/class.dc.media.php	(revision 1677)
+++ inc/core/class.dc.media.php	(revision 1468)
@@ -299,7 +299,5 @@
 			$f->media_thumb = array();
 			$p = path::info($f->relname);
-
 			$alpha = ($p['extension'] == 'png') || ($p['extension'] == 'PNG');
-
 			$thumb = sprintf(($alpha ? $this->thumb_tp_alpha : $this->thumb_tp),$this->root.'/'.$p['dirname'],$p['base'],'%s');
 			$thumb_url = sprintf(($alpha ? $this->thumb_tp_alpha : $this->thumb_tp),$this->root_url.$p['dirname'],$p['base'],'%s');
@@ -308,18 +306,8 @@
 			$thumb_url = preg_replace('#\./#','/',$thumb_url);
 			$thumb_url = preg_replace('#(?<!:)/+#','/',$thumb_url);
-
-			if ($alpha) {
-				$thumb_alt = sprintf($this->thumb_tp,$this->root.'/'.$p['dirname'],$p['base'],'%s');
-				$thumb_url_alt = sprintf($this->thumb_tp,$this->root_url.$p['dirname'],$p['base'],'%s');
-				# Cleaner URLs
-				$thumb_url_alt = preg_replace('#\./#','/',$thumb_url_alt);
-				$thumb_url_alt = preg_replace('#(?<!:)/+#','/',$thumb_url_alt);
-			}
 			
 			foreach ($this->thumb_sizes as $suffix => $s) {
 				if (file_exists(sprintf($thumb,$suffix))) {
 					$f->media_thumb[$suffix] = sprintf($thumb_url,$suffix);
-				} elseif ($alpha && file_exists(sprintf($thumb_alt,$suffix))) {
-					$f->media_thumb[$suffix] = sprintf($thumb_url_alt,$suffix);
 				}
 			}
@@ -511,8 +499,7 @@
 	@param	post_id	<b>integer</b>		Post ID
 	@param	media_id	<b>integer</b>		Optionnal media ID
-	@param	return_rs	<b>boolean</b>		Whether to return a resultset (true) or an array (false, default value).
-	@return	<b>array</b> Array or ResultSet of fileItems
-	*/
-	public function getPostMedia($post_id,$media_id=null,$return_rs=false)
+	@return	<b>array</b> Array of fileItems
+	*/
+	public function getPostMedia($post_id,$media_id=null)
 	{
 		$params = array(
@@ -530,9 +517,9 @@
 			$f = $this->fileRecord($rs);
 			if ($f !== null) {
-				$res[] = $return_rs ? new ArrayObject($f) : $f;
-			}
-		}
-		
-		return $return_rs ? staticRecord::newFromArray($res) : $res;
+				$res[] = $f;
+			}
+		}
+		
+		return $res;
 	}
 	
Index: inc/core/class.dc.postmedia.php
===================================================================
--- inc/core/class.dc.postmedia.php	(revision 1179)
+++ inc/core/class.dc.postmedia.php	(revision 1505)
Index: inc/core/class.dc.trackback.php
===================================================================
--- inc/core/class.dc.trackback.php	(revision 1179)
+++ inc/core/class.dc.trackback.php	(revision 1684)
@@ -14,7 +14,7 @@
 /**
 @ingroup DC_CORE
-@brief Trackbacks sender and server
-
-Sends and receives trackbacks. Also handles trackbacks auto discovery.
+@brief Trackbacks/Pingbacks sender and server
+
+Sends and receives trackbacks/pingbacks. Also handles trackbacks/pingbacks auto discovery.
 */
 class dcTrackback
@@ -80,36 +80,56 @@
 		}
 		
-		$data = array(
-			'title' => $post_title,
-			'excerpt' => $post_excerpt,
-			'url' => $post_url,
-			'blog_name' => trim(html::escapeHTML(html::clean($this->core->blog->name)))
-			//,'__debug' => false
-		);
-		
-		# Ping
-		try
-		{
-			$http = self::initHttp($url,$path);
-			$http->post($path,$data,'UTF-8');
-			$res = $http->getContent();
-		}
-		catch (Exception $e)
-		{
-			throw new Exception(__('Unable to ping URL'));
-		}
-		
-		$pattern =
-		'|<response>.*<error>(.*)</error>(.*)'.
-		'(<message>(.*)</message>(.*))?'.
-		'</response>|msU';
-		
-		if (!preg_match($pattern,$res,$match))
-		{
-			throw new Exception(sprintf(__('%s is not a ping URL'),$url));
-		}
-		
-		$ping_error = trim($match[1]);
-		$ping_msg = (!empty($match[4])) ? $match[4] : '';
+		$ping_parts = explode('|',$url);
+		
+		# Let's walk by the trackback way
+		if (count($ping_parts) < 2) {
+			$data = array(
+				'title' => $post_title,
+				'excerpt' => $post_excerpt,
+				'url' => $post_url,
+				'blog_name' => trim(html::escapeHTML(html::clean($this->core->blog->name)))
+				//,'__debug' => false
+			);
+			
+			# Ping
+			try
+			{
+				$http = self::initHttp($url,$path);
+				$http->post($path,$data,'UTF-8');
+				$res = $http->getContent();
+			}
+			catch (Exception $e)
+			{
+				throw new Exception(__('Unable to ping URL'));
+			}
+			
+			$pattern =
+			'|<response>.*<error>(.*)</error>(.*)'.
+			'(<message>(.*)</message>(.*))?'.
+			'</response>|msU';
+			
+			if (!preg_match($pattern,$res,$match))
+			{
+				throw new Exception(sprintf(__('%s is not a ping URL'),$url));
+			}
+			
+			$ping_error = trim($match[1]);
+			$ping_msg = (!empty($match[4])) ? $match[4] : '';
+		}
+		# Damnit ! Let's play pingback
+		else {
+			try {
+				$xmlrpc = new xmlrpcClient($ping_parts[0]);
+				$res = $xmlrpc->query('pingback.ping', $post_url, $ping_parts[1]);
+				$ping_error = '0';
+			}
+			catch (xmlrpcException $e) {
+				$ping_error = $e->getCode();
+				$ping_msg = $e->getMessage();	
+			}
+			catch (Exception $e) {
+				throw new Exception(__('Unable to ping URL'));
+			}
+		}
 		
 		if ($ping_error != '0') {
@@ -126,4 +146,34 @@
 	}
 	//@}
+	
+	private function addBacklink($post_id, $url, $blog_name, $title, $excerpt, &$comment)
+	{
+		if (empty($blog_name)) {
+			$blog_name = 'Anonymous blog';
+		}
+		
+		$comment =
+		"<!-- TB -->\n".
+		'<p><strong>'.($title ? $title : $blog_name)."</strong></p>\n".
+		'<p>'.$excerpt.'</p>';
+		
+		$cur = $this->core->con->openCursor($this->core->prefix.'comment');
+		$cur->comment_author = (string) $blog_name;
+		$cur->comment_site = (string) $url;
+		$cur->comment_content = (string) $comment;
+		$cur->post_id = $post_id;
+		$cur->comment_trackback = 1;
+		$cur->comment_status = $this->core->blog->settings->system->trackbacks_pub ? 1 : -1;
+		$cur->comment_ip = http::realIP();
+		
+		# --BEHAVIOR-- publicBeforeTrackbackCreate
+		$this->core->callBehavior('publicBeforeTrackbackCreate',$cur);
+		if ($cur->post_id) {
+			$comment_id = $this->core->blog->addComment($cur);
+			
+			# --BEHAVIOR-- publicAfterTrackbackCreate
+			$this->core->callBehavior('publicAfterTrackbackCreate',$cur,$comment_id);
+		}
+	}
 	
 	/// @name Receive trackbacks
@@ -196,8 +246,5 @@
 			
 			if (!$charset) {
-				$charset = mb_detect_encoding($title.' '.$excerpt.' '.$blog_name,
-				'UTF-8,ISO-8859-1,ISO-8859-2,ISO-8859-3,'.
-				'ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,'.
-				'ISO-8859-9,ISO-8859-10,ISO-8859-13,ISO-8859-14,ISO-8859-15');
+				$charset = self::detectCharset($title.' '.$excerpt.' '.$blog_name);
 			}
 			
@@ -226,32 +273,7 @@
 			$url = trim(html::clean($url));
 			
-			if (!$blog_name) {
-				$blog_name = 'Anonymous blog';
-			}
-			
-			$comment =
-			"<!-- TB -->\n".
-			'<p><strong>'.($title ? $title : $blog_name)."</strong></p>\n".
-			'<p>'.$excerpt.'</p>';
-			
-			$cur = $this->core->con->openCursor($this->core->prefix.'comment');
-			$cur->comment_author = (string) $blog_name;
-			$cur->comment_site = (string) $url;
-			$cur->comment_content = (string) $comment;
-			$cur->post_id = $post_id;
-			$cur->comment_trackback = 1;
-			$cur->comment_status = $this->core->blog->settings->system->trackbacks_pub ? 1 : -1;
-			$cur->comment_ip = http::realIP();
-			
 			try
 			{
-				# --BEHAVIOR-- publicBeforeTrackbackCreate
-				$this->core->callBehavior('publicBeforeTrackbackCreate',$cur);
-				if ($cur->post_id) {
-					$comment_id = $this->core->blog->addComment($cur);
-					
-					# --BEHAVIOR-- publicAfterTrackbackCreate
-					$this->core->callBehavior('publicAfterTrackbackCreate',$cur,$comment_id);
-				}
+				$this->addBacklink($post_id, $url, $blog_name, $title, $excerpt, $comment);
 			}
 			catch (Exception $e)
@@ -261,15 +283,4 @@
 			}
 		}
-		
-		
-		$debug_trace =
-		"  <debug>\n".
-		'    <title>'.$title."</title>\n".
-		'    <excerpt>'.$excerpt."</excerpt>\n".
-		'    <url>'.$url."</url>\n".
-		'    <blog_name>'.$blog_name."</blog_name>\n".
-		'    <charset>'.$charset."</charset>\n".
-		'    <comment>'.$comment."</comment>\n".
-		"  </debug>\n";
 		
 		$resp =
@@ -283,8 +294,139 @@
 		
 		if (!empty($_POST['__debug'])) {
-			$resp .= $debug_trace;
+			$resp .= 
+			"  <debug>\n".
+			'    <title>'.$title."</title>\n".
+			'    <excerpt>'.$excerpt."</excerpt>\n".
+			'    <url>'.$url."</url>\n".
+			'    <blog_name>'.$blog_name."</blog_name>\n".
+			'    <charset>'.$charset."</charset>\n".
+			'    <comment>'.$comment."</comment>\n".
+			"  </debug>\n";
 		}
 		
 		echo	$resp."</response>";
+	}
+	//@}
+
+	/// @name Receive pingbacks
+	//@{
+	/**
+	Receives a pingback and insert it as a comment of given post.
+	
+	@param	from_url		<b>string</b>		Source URL
+	@param	to_url			<b>string</b>		Target URL
+	*/
+	public function receive_pb($from_url, $to_url)
+	{
+		$reg = '!^'.preg_quote($this->core->blog->url).'(.*)!';
+		$type = $args = $next = '';
+		
+		# Are you dumb?
+		if (!preg_match($reg, $to_url, $m)) {
+			throw new Exception(__('Any chance you ping one of my contents? No? Really?'), 0);
+		}
+		
+		# Does the targeted URL look like a registered post type? 
+		$url_part = $m[1];
+		$p_type = '';
+		$post_types = $this->core->getPostTypes();
+		foreach ($post_types as $k => $v) {
+			$reg = '!^'.preg_quote(str_replace('%s', '', $v['public_url'])).'(.*)!';
+			if (preg_match($reg, $url_part, $n)) {
+				$p_type = $k;
+				$post_url = $n[1];
+				break;
+			}
+		}
+		
+		if (empty($p_type)) {
+			throw new Exception(__('Sorry but you can not ping this type of content.'), 33);
+		}
+
+		# Time to see if we've got a winner...
+		$params = array(
+			'post_type' => $p_type,
+			'post_url' => $post_url,
+		);
+		$posts = $this->core->blog->getPosts($params);
+		
+		# Missed! 
+		if ($posts->isEmpty()) {
+			throw new Exception(__('Oops. Kinda "not found" stuff. Please check the target URL twice.'), 33);
+		}
+		
+		# Nice try. But, sorry, no.
+		if (!$posts->trackbacksActive()) {
+			throw new Exception(__('Sorry, dude. This entry does not accept pingback at the moment.'), 33);
+		}
+
+		# OK. We've found our champion. Time to check the remote part.
+		try {
+			$http = self::initHttp($from_url, $from_path);
+			
+			# First round : just to be sure the ping comes from an acceptable resource type.
+			$http->setHeadersOnly(true);
+			$http->get($from_path);
+			$c_type = explode(';', $http->getHeader('content-type'));
+
+			# Bad luck. Bye, bye...
+			if (!in_array($c_type[0],array('text/html', 'application/xhtml+xml'))) {
+				throw new Exception(__('Your source URL does not look like a supported content type. Sorry. Bye, bye!'), 0);
+			}
+			
+			# Second round : let's go fetch and parse the remote content
+			$http->setHeadersOnly(false);
+			$http->get($from_path);
+			$remote_content = $http->getContent();
+
+			$charset = self::getCharsetFromRequest($http->getHeader('content-type'));
+
+			if (!$charset) {
+				$charset = self::detectCharset($remote_content);
+			}
+			
+			if (strtolower($charset) != 'utf-8') {
+				$remote_content = iconv($charset,'UTF-8',$remote_content);
+			}
+			
+			# We want a title...
+			if (!preg_match('!<title>([^<].*?)</title>!mis', $remote_content, $m)) {
+				throw new Exception(__('Where\'s your title?'), 0);
+			}
+			$title = trim(html::clean($m[1]));
+			$title = html::decodeEntities($title);
+			$title = html::escapeHTML($title);
+			$title = text::cutString($title,60);
+			
+			preg_match('!<body[^>]*?>(.*)?</body>!msi', $remote_content, $m);
+			$source = $m[1];
+			$source = preg_replace('![\r\n\s]+!ms',' ',$source);
+			$source = preg_replace( "/<\/*(h\d|p|th|td|li|dt|dd|pre|caption|input|textarea|button)[^>]*>/", "\n\n", $source );
+			$source = strip_tags($source, '<a>');
+			$source = explode("\n\n",$source);
+			
+			$excerpt = '';
+			foreach ($source as $line) {
+				if (strpos($line, $to_url) !== false) {
+					if (preg_match("!<a[^>]+?".$to_url."[^>]*>([^>]+?)</a>!", $line, $m)) {
+						$excerpt = strip_tags($line);
+						break;
+					}
+				}
+			}
+			if ($excerpt) {
+				$excerpt = '(&#8230;) '.text::cutString(html::escapeHTML($excerpt),200).' (&#8230;)';
+			}
+			else {
+				$excerpt = '(&#8230;)';
+			}
+
+			$this->addBacklink($posts->post_id, $from_url, '', $title, $excerpt, $comment);
+		}
+		catch (Exception $e) {
+			throw new Exception(__('Sorry, an internal problem has occured.'), 0);
+		}
+		
+		return __('Thanks, mate. It was a pleasure.');
 	}
 	//@}
@@ -301,9 +443,12 @@
 	}
 	
-	private static function getCharsetFromRequest()
-	{
-		if (isset($_SERVER['CONTENT_TYPE']))
-		{
-			if (preg_match('|charset=([a-zA-Z0-9-]+)|',$_SERVER['CONTENT_TYPE'],$m)) {
+	private static function getCharsetFromRequest($header = '')
+	{
+		if (!$header && isset($_SERVER['CONTENT_TYPE'])) {
+			$header = $_SERVER['CONTENT_TYPE'];
+		}
+		
+		if ($header) {
+			if (preg_match('|charset=([a-zA-Z0-9-]+)|',$header,$m)) {
 				return $m[1];
 			}
@@ -312,5 +457,13 @@
 		return null;
 	}
-	
+
+	private static function detectCharset($string)
+	{
+		return mb_detect_encoding($remote_content,
+				'UTF-8,ISO-8859-1,ISO-8859-2,ISO-8859-3,'.
+				'ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,'.
+				'ISO-8859-9,ISO-8859-10,ISO-8859-13,ISO-8859-14,ISO-8859-15');
+	}
+		
 	/// @name Trackbacks auto discovery
 	//@{
@@ -346,5 +499,5 @@
 			for ($i = 0; $i<count($match); $i++)
 			{
-				if (preg_match('/href="(http:\/\/[^"]+)"/ms', $match[$i][1], $matches)) {
+				if (preg_match('/href="((https?:\/)?\/[^"]+)"/ms', $match[$i][1], $matches)) {
 					$res[$matches[1]] = 1;
 				}
@@ -358,5 +511,5 @@
 			for ($i = 0; $i<count($match); $i++)
 			{
-				if (preg_match('/cite="(http:\/\/[^"]+)"/ms', $match[$i][2], $matches)) {
+				if (preg_match('/cite="((https?:\/)?\/[^"]+)"/ms', $match[$i][2], $matches)) {
 					$res[$matches[1]] = 1;
 				}
@@ -369,4 +522,8 @@
 	private function getPingURL($url)
 	{
+		if (strpos($url,'/') === 0) {
+			$url = http::getHost().$url;
+		}
+		
 		try
 		{
@@ -374,4 +531,5 @@
 			$http->get($path);
 			$page_content = $http->getContent();
+			$pb_url = $http->getHeader('x-pingback');
 		}
 		catch (Exception $e)
@@ -380,4 +538,20 @@
 		}
 		
+		# If we've got a X-Pingback header and it's a valid URL, it will be enough
+		if ($pb_url && filter_var($pb_url,FILTER_VALIDATE_URL) && preg_match('!^https?:!',$pb_url)) {
+			return $pb_url.'|'.$url;
+		}
+		
+		# No X-Pingback header. A link rel=pingback, maybe ?
+		$pattern_pingback = '!<link rel="pingback" href="(.*?)"( /)?>!msi';
+		
+		if (preg_match($pattern_pingback,$page_content,$m)) {
+			$pb_url = $m[1];
+			if (filter_var($pb_url,FILTER_VALIDATE_URL) && preg_match('!^https?:!',$pb_url)) {
+				return $pb_url.'|'.$url;
+			}
+		}
+
+		# No pingback ? OK, let's check for a trackback data chunk...
 		$pattern_rdf =
 		'/<rdf:RDF.*?>.*?'.
@@ -388,9 +562,12 @@
 		preg_match_all($pattern_rdf,$page_content,$rdf_all,PREG_SET_ORDER);
 		
+		$url_path = parse_url($url, PHP_URL_PATH);
+		$sanitized_url = str_replace($url_path, html::sanitizeURL($url_path), $url);
+		
 		for ($i=0; $i<count($rdf_all); $i++)
 		{
 			$rdf = $rdf_all[$i][1];
-			
-			if (preg_match('/dc:identifier="'.preg_quote($url,'/').'"/msi',$rdf)) {
+			if (preg_match('/dc:identifier="'.preg_quote($url,'/').'"/msi',$rdf) ||
+				preg_match('/dc:identifier="'.preg_quote($sanitized_url,'/').'"/msi',$rdf)) {
 				if (preg_match('/trackback:ping="(.*?)"/msi',$rdf,$tb_link)) {
 					return $tb_link[1];
Index: inc/core/class.dc.update.php
===================================================================
--- inc/core/class.dc.update.php	(revision 1231)
+++ inc/core/class.dc.update.php	(revision 1280)
@@ -32,5 +32,4 @@
 	
 	protected $cache_ttl = '-6 hours';
-	protected $nocache_ttl = '-2 mins';
 	protected $forced_files = array();
 	
@@ -56,10 +55,9 @@
 	 * 
 	 * @param version		string	Current version to compare
-	 * @param nocache		boolean	Force checking
 	 * @return string				Latest version if available
 	 */
-	public function check($version, $nocache=false)
-	{
-		$this->getVersionInfo($nocache);
+	public function check($version)
+	{
+		$this->getVersionInfo();
 		$v = $this->getVersion();
 		if ($v && version_compare($version,$v,'<')) {
@@ -70,13 +68,8 @@
 	}
 	
-	public function getVersionInfo($nocache=false)
-	{
-		# Check minimum time without cache (prevents from server flood)
-		if ($nocache && is_readable($this->cache_file) && filemtime($this->cache_file) > strtotime($this->nocache_ttl)) {
-			$nocache = false;
-		}
-		
+	public function getVersionInfo()
+	{
 		# Check cached file
-		if (is_readable($this->cache_file) && filemtime($this->cache_file) > strtotime($this->cache_ttl) && !$nocache)
+		if (is_readable($this->cache_file) && filemtime($this->cache_file) > strtotime($this->cache_ttl))
 		{
 			$c = @file_get_contents($this->cache_file);
Index: inc/core/class.dc.xmlrpc.php
===================================================================
--- inc/core/class.dc.xmlrpc.php	(revision 1179)
+++ inc/core/class.dc.xmlrpc.php	(revision 1674)
@@ -214,4 +214,9 @@
 			array('array','integer','string','string'),
 			'Retrieve all of the comment statuses.');
+			
+		# Pingback support
+		$this->addCallback('pingback.ping',array($this,'pingback_ping'),
+			array('string', 'string', 'string'),
+			'Notify a link to a post.');
 	}
 	
@@ -270,5 +275,5 @@
 	}
 	
-	private function setBlog()
+	private function setBlog($bypass = false)
 	{
 		if (!$this->blog_id) {
@@ -288,6 +293,7 @@
 		}
 		
-		if (!$this->core->blog->settings->system->enable_xmlrpc ||
-		!$this->core->auth->check('usage,contentadmin',$this->core->blog->id)) {
+		if (!$bypass &&
+			(!$this->core->blog->settings->system->enable_xmlrpc ||
+			!$this->core->auth->check('usage,contentadmin',$this->core->blog->id))) {
 			$this->core->blog = null;
 			throw new Exception('Not enough permissions on this blog.');
@@ -1629,4 +1635,18 @@
 		);
 	}
+
+	/* Pingback support
+	--------------------------------------------------- */
+	public function pingback_ping($from_url, $to_url)
+	{
+		# Come on, buddy! Don't make me waste time with this kind of silliness...
+		if (!(filter_var($from_url, FILTER_VALIDATE_URL) && preg_match('!^https?://!',$from_url))) {
+			throw new Exception(__('No valid source URL provided? Try again!'), 0);
+		}
+		
+		$this->setBlog(true);
+		$tb = new dcTrackback($this->core);
+		return $tb->receive_pb($from_url, $to_url);
+	}
 }
 ?>
