From 513fe7eeb094738b6cd5de72e1124bb3281d628c Mon Sep 17 00:00:00 2001
From: diosmosis <benaka.moorthi@gmail.com>
Date: Mon, 25 Mar 2013 10:01:39 +0000
Subject: [PATCH] Fixes #3734, added 'external backlinks' and 'referrer
 domains' stats to SEO widget via Majestic SEO API.

---
 lang/en.php                        |   2 +
 plugins/SEO/API.php                |  18 +++++-
 plugins/SEO/MajesticClient.php     |  95 +++++++++++++++++++++++++++++
 plugins/SEO/RankChecker.php        |  34 +++++++++++
 plugins/SEO/images/majesticseo.png | Bin 0 -> 674 bytes
 plugins/SEO/templates/index.tpl    |   2 +-
 6 files changed, 148 insertions(+), 3 deletions(-)
 create mode 100644 plugins/SEO/MajesticClient.php
 create mode 100644 plugins/SEO/images/majesticseo.png

diff --git a/lang/en.php b/lang/en.php
index 36f52c3c7a..72f52b122f 100644
--- a/lang/en.php
+++ b/lang/en.php
@@ -1285,6 +1285,8 @@ And thank you for using Piwik!',
 	'SEO_Bing_IndexedPages' => 'Bing indexed pages',
 	'SEO_Dmoz' => 'DMOZ entries',
 	'SEO_SEORankingsFor' => 'SEO Rankings for %s',
+	'SEO_ExternalBacklinks' => 'External Backlinks',
+	'SEO_ReferrerDomains' => 'Referrer Domains',
 	'SitesManager_PluginDescription' => 'Websites Management in Piwik: Add a new Website, Edit an existing one, Show the JavaScript code to include on your pages. All the actions are also available through the API.',
 	'SitesManager_Sites' => 'Websites',
 	'SitesManager_TrackingTags' => 'Tracking code for %s',
diff --git a/plugins/SEO/API.php b/plugins/SEO/API.php
index 0cec1ad523..0df9c3b1da 100644
--- a/plugins/SEO/API.php
+++ b/plugins/SEO/API.php
@@ -36,9 +36,9 @@ class Piwik_SEO_API
 	}
 	
 	/**
-	 * Get rank
+	 * Returns SEO statistics for a URL.
 	 *
-	 * @param string $url URL to request Ranks for
+	 * @param string $url URL to request SEO stats for
 	 * @return Piwik_DataTable
 	 */
 	public function getRank( $url )
@@ -46,6 +46,8 @@ class Piwik_SEO_API
 		Piwik::checkUserHasSomeViewAccess();
 		$rank = new Piwik_SEO_RankChecker($url);
 		
+		$linkToMajestic = Piwik_SEO_MajesticClient::getLinkForUrl($url);
+		
 		$data = array(
 			'Google PageRank' 	=> array(
 				'rank' => $rank->getPageRank(),
@@ -72,6 +74,18 @@ class Piwik_SEO_API
 				'logo' => 'plugins/SEO/images/whois.png',
 				'id'   => 'domain-age',
 			),
+			Piwik_Translate('SEO_ExternalBacklinks') => array(
+				'rank' => $rank->getExternalBacklinkCount(),
+				'logo' => 'plugins/SEO/images/majesticseo.png',
+				'logo_link' => $linkToMajestic,
+				'id'   => 'external-backlinks',
+			),
+			Piwik_Translate('SEO_ReferrerDomains') => array(
+				'rank' => $rank->getReferrerDomainCount(),
+				'logo' => 'plugins/SEO/images/majesticseo.png',
+				'logo_link' => $linkToMajestic,
+				'id'   => 'referrer-domains',
+			),
 		);
 
 		// Add DMOZ only if > 0 entries found
diff --git a/plugins/SEO/MajesticClient.php b/plugins/SEO/MajesticClient.php
new file mode 100644
index 0000000000..61ab4d2d29
--- /dev/null
+++ b/plugins/SEO/MajesticClient.php
@@ -0,0 +1,95 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik_Plugins
+ * @package Piwik_SEO
+ */
+
+/**
+ * Client for Majestic SEO's HTTP API.
+ * 
+ * Hides the HTTP request sending logic.
+ */
+class Piwik_SEO_MajesticClient
+{
+	const API_BASE = 'http://simpleapi.majesticseo.com/sapi/';
+	const API_KEY = 'ETHPYY'; // please only use this key within Piwik
+	
+	/**
+	 * Returns a URL that can be used to view all SEO data for a particular website.
+	 * 
+	 * @param string $targetSiteUrl The URL of the website for whom SEO stats should be
+	 *                              accessible for.
+	 * @return string
+	 */
+	public static function getLinkForUrl( $targetSiteUrl )
+	{
+		$domain = @parse_url($targetSiteUrl, PHP_URL_HOST);
+		return "http://www.majesticseo.com/reports/site-explorer/summary/$domain?IndexDataSource=F";
+	}
+	
+	/**
+	 * Returns backlink statistics including the count of backlinks and count of
+	 * referrer domains (domains with backlinks).
+	 * 
+	 * This method issues an HTTP request and waits for it to return.
+	 * 
+	 * @param string $siteDomain The domain of the website to get stats for.
+	 * @param int $timeout The number of seconds to wait before aborting
+	 *                     the HTTP request.
+	 * @return array An array containing the backlink count and referrer
+	 *               domain count:
+	 *               array(
+	 *                   'backlink_count' => X,
+	 *                   'referrer_domains_count' => Y
+	 *               )
+	 *               If either stat is false, either the API returned an
+	 *               error, or the IP was blocked for this request.
+	 */
+	public function getBacklinkStats( $siteDomain, $timeout = 300 )
+	{
+		$apiUrl = $this->getApiUrl($method = 'GetBacklinkStats', $args = array(
+			'items' => '1',
+			'item0' => $siteDomain
+		));
+		$apiResponse = Piwik_Http::sendHttpRequest($apiUrl, $timeout);
+		
+		$result = array(
+			'backlink_count' => false,
+			'referrer_domains_count' => false
+		);
+		
+		$apiResponse = Piwik_Common::json_decode($apiResponse, $assoc = true);
+		if (!empty($apiResponse)
+			&& !empty($apiResponse['Data']))
+		{
+			$siteSeoStats = reset($apiResponse['Data']);
+			
+			if (isset($siteSeoStats['ExtBackLinks'])
+				&& $siteSeoStats['ExtBackLinks'] !== -1)
+			{
+				$result['backlink_count'] = $siteSeoStats['ExtBackLinks'];
+			}
+			
+			if (isset($siteSeoStats['RefDomains'])
+				&& $siteSeoStats['RefDomains'] !== -1)
+			{
+				$result['referrer_domains_count'] = $siteSeoStats['RefDomains'];
+			}
+		}
+		
+		return $result;
+	}
+	
+	private function getApiUrl( $method, $args = array() )
+	{
+		$args['sak'] = self::API_KEY;
+		
+		$queryString = http_build_query($args);
+		return self::API_BASE.$method.'?'.$queryString;
+	}
+}
diff --git a/plugins/SEO/RankChecker.php b/plugins/SEO/RankChecker.php
index c5a43b2261..da9fa0cf60 100644
--- a/plugins/SEO/RankChecker.php
+++ b/plugins/SEO/RankChecker.php
@@ -21,6 +21,7 @@
 class Piwik_SEO_RankChecker
 {
 	private $url;
+	private $majesticInfo = null;
 
 	public function __construct($url)
 	{
@@ -170,6 +171,28 @@ class Piwik_SEO_RankChecker
         }
         return false;
     }
+    
+    /**
+     * Returns the number backlinks that link to the current site.
+     * 
+     * @return int
+     */
+    public function getExternalBacklinkCount()
+    {
+    	$majesticInfo = $this->getMajesticInfo();
+    	return $majesticInfo['backlink_count'];
+    }
+    
+    /**
+     * Returns the number of referrer domains that link to the current site.
+     * 
+     * @return int
+     */
+    public function getReferrerDomainCount()
+    {
+    	$majesticInfo = $this->getMajesticInfo();
+    	return $majesticInfo['referrer_domains_count'];
+    }
 
     /**
      * Returns the domain age archive.org lists for the current url
@@ -332,4 +355,15 @@ class Piwik_SEO_RankChecker
 
 		return '7'.$CheckByte.$HashStr;
 	}
+	
+	private function getMajesticInfo()
+	{
+		if ($this->majesticInfo === null)
+		{
+			$client = new Piwik_SEO_MajesticClient();
+			$this->majesticInfo = $client->getBacklinkStats($this->url);
+		}
+		
+		return $this->majesticInfo;
+	}
 }
diff --git a/plugins/SEO/images/majesticseo.png b/plugins/SEO/images/majesticseo.png
new file mode 100644
index 0000000000000000000000000000000000000000..a42875c250f7081411bb823bc4fc904b99e3735d
GIT binary patch
literal 674
zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s77>k44ofy`glX=O&z`&N|
z?e4<x9|RZdT|SwCfq}EYBeIx*fm;}a85w5Hkzin8U@!6Xb!ETHEXk^Es(04Ll!1Xs
z$<xI##Nu@AWb15EM~SxiJKx;AySY6>FU!xMbNY-2a}}B*x|dF6+`8o>d&G^*=l&nm
zBRIm!#F~UQbo5Raxo6;hG{#58a`xoAw!5v94Ogoup5$g`{#?udzGlz-4cDT~PD^c&
zoMJeGMPPxHg0g_AK$7>)wB0%4U8yJbIqqX{a?U*>dzZVSnqf_2k?bbRj^!tp-&f*q
zU-4Lcp`s(3!dEfzTakJPmIah8ux6iYKH=Yj&o7jJMKmA#YjwhkgY#ZM>37ErlT_(0
zQH^PHo_-Nd(cSd?^Ng_eyw%Qy+HU=48#)ilGq2~orx@^C(ClZDXPROq``vhk85Zep
zzh%EEVrwX0IRDt;&mFUh_8p!aFux^1Ch>Z@&T4%Rx&2H2Z1DQHvv;QdP0u0|C;u-G
ze7`;P_jN4c?7gtSWYcx^%B@G@jMnnq{n_&1mcflnj)8jvq>t`5@~C)zWFEto1s@;i
z{5s=c+RV}WuDyWi6YC-AglWc`mftUb)BkSXqOzX~SI#%qv!w6~NjAwf+%jOuGP!lN
zL8{MTg;of&0<((K&W=+T{(dgsrM)1=CwA_d@Qu@EPts7~XpJmkxXHtjCwFw+(<S~<
zH!P0p<owCMBbNGf#XW&T_5LOAP5xa~^ANxJ;1|cs&GT<qu)lYBdTlRf_S&N=dM+n*
zQsVDAt*l+}`#_XN_?@y-98HBBY6qlQUWPM7?@-nH?{ITnP;fb~gogQnoE?n+m`@9|
bJkt-5JI*@ICRvGrfq}u()z4*}Q$iB}ER`Pa

literal 0
HcmV?d00001

diff --git a/plugins/SEO/templates/index.tpl b/plugins/SEO/templates/index.tpl
index 6abe3cce97..9d7c31602b 100644
--- a/plugins/SEO/templates/index.tpl
+++ b/plugins/SEO/templates/index.tpl
@@ -23,7 +23,7 @@
 	   			<table cellspacing='2' style='margin:auto;line-height:1.5em;padding-top:10px'>
 	   			{foreach from=$ranks item=rank}
 	   			<tr>
-	   				<td><img style='vertical-align:middle;margin-right:6px;' src='{$rank.logo}' border='0' alt="{$rank.label}"> {$rank.label}
+	   				<td>{if !empty($rank.logo_link)}<a href="{$rank.logo_link}" target="_blank">{/if}<img style='vertical-align:middle;margin-right:6px;' src='{$rank.logo}' border='0' alt="{$rank.label}">{if !empty($rank.logo_link)}</a>{/if} {$rank.label}
 	   				</td><td>
 	   					<div style='margin-left:15px'>
 		   					{if isset($rank.rank)}{$rank.rank}{else}-{/if}
-- 
GitLab