├── .gitignore ├── organic-search-analytics ├── version.txt ├── log │ └── .gitignore ├── config │ └── .gitignore ├── robots.txt ├── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ └── fontawesome-webfont.woff2 ├── css │ └── lib │ │ └── jquery │ │ └── images │ │ ├── ui-icons_0078ae_256x240.png │ │ ├── ui-icons_056b93_256x240.png │ │ ├── ui-icons_f5e175_256x240.png │ │ ├── ui-icons_f7a50d_256x240.png │ │ ├── ui-icons_fcd113_256x240.png │ │ ├── ui-icons_ffffff_256x240.png │ │ ├── ui-bg_gloss-wave_45_e14f1c_500x100.png │ │ └── ui-bg_inset-hard_100_ffffff_1x100.png ├── js │ ├── settings.js │ ├── lib │ │ └── jqplot │ │ │ └── plugins │ │ │ ├── jqplot.ciParser.min.js │ │ │ ├── jqplot.mobile.min.js │ │ │ ├── jqplot.trendline.min.js │ │ │ ├── jqplot.canvasAxisLabelRenderer.min.js │ │ │ ├── jqplot.canvasAxisTickRenderer.min.js │ │ │ ├── jqplot.blockRenderer.min.js │ │ │ ├── jqplot.json2.min.js │ │ │ ├── jqplot.ohlcRenderer.min.js │ │ │ ├── jqplot.dragable.min.js │ │ │ ├── jqplot.pointLabels.min.js │ │ │ ├── jqplot.enhancedLegendRenderer.min.js │ │ │ └── jqplot.BezierCurveRenderer.min.js │ ├── script.js │ └── report.js ├── apis │ ├── Google │ │ ├── Exception.php │ │ ├── Auth │ │ │ ├── Exception.php │ │ │ ├── Abstract.php │ │ │ ├── LoginTicket.php │ │ │ ├── Simple.php │ │ │ ├── AppIdentity.php │ │ │ ├── AssertionCredentials.php │ │ │ └── ComputeEngine.php │ │ ├── Cache │ │ │ ├── Exception.php │ │ │ ├── Null.php │ │ │ ├── Abstract.php │ │ │ ├── Apc.php │ │ │ └── Memcache.php │ │ ├── Task │ │ │ ├── Exception.php │ │ │ └── Retryable.php │ │ ├── Logger │ │ │ ├── Exception.php │ │ │ ├── Null.php │ │ │ ├── Psr.php │ │ │ └── File.php │ │ ├── Signer │ │ │ ├── Abstract.php │ │ │ └── P12.php │ │ ├── Verifier │ │ │ ├── Abstract.php │ │ │ └── Pem.php │ │ ├── autoload.php │ │ ├── Service.php │ │ ├── Service │ │ │ ├── Genomics.php │ │ │ ├── Cloudsearch.php │ │ │ ├── Playmoviespartner.php │ │ │ ├── Exception.php │ │ │ ├── GroupsMigration.php │ │ │ ├── Admin.php │ │ │ └── Webfonts.php │ │ ├── IO │ │ │ ├── Exception.php │ │ │ └── Curl.php │ │ ├── Collection.php │ │ ├── Utils.php │ │ └── Http │ │ │ ├── Batch.php │ │ │ └── REST.php │ └── Bing │ │ └── Webmasters.php ├── inc │ ├── html │ │ ├── _alert.php │ │ ├── _saveReport.php │ │ ├── _foot.php │ │ └── _head.php │ └── code │ │ ├── gapiOauth.php │ │ ├── debugLogger.php │ │ ├── globalIncludes.php │ │ ├── wmtimport.php │ │ ├── mysql.php │ │ └── core.php ├── ajax.php ├── index.php ├── data-capture.php ├── data-capture-run.php ├── settings-configure.php └── data-delete.php ├── README.md └── organic-search-analytics.sql /.gitignore: -------------------------------------------------------------------------------- 1 | *.p12 2 | .DS_STORE -------------------------------------------------------------------------------- /organic-search-analytics/version.txt: -------------------------------------------------------------------------------- 1 | 2.5.4 2 | -------------------------------------------------------------------------------- /organic-search-analytics/log/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | */ 3 | !.gitignore -------------------------------------------------------------------------------- /organic-search-analytics/config/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | */ 3 | !.gitignore -------------------------------------------------------------------------------- /organic-search-analytics/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: / -------------------------------------------------------------------------------- /organic-search-analytics/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PromInc/organic-search-analytics/HEAD/organic-search-analytics/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /organic-search-analytics/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PromInc/organic-search-analytics/HEAD/organic-search-analytics/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /organic-search-analytics/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PromInc/organic-search-analytics/HEAD/organic-search-analytics/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /organic-search-analytics/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PromInc/organic-search-analytics/HEAD/organic-search-analytics/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /organic-search-analytics/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PromInc/organic-search-analytics/HEAD/organic-search-analytics/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /organic-search-analytics/css/lib/jquery/images/ui-icons_0078ae_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PromInc/organic-search-analytics/HEAD/organic-search-analytics/css/lib/jquery/images/ui-icons_0078ae_256x240.png -------------------------------------------------------------------------------- /organic-search-analytics/css/lib/jquery/images/ui-icons_056b93_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PromInc/organic-search-analytics/HEAD/organic-search-analytics/css/lib/jquery/images/ui-icons_056b93_256x240.png -------------------------------------------------------------------------------- /organic-search-analytics/css/lib/jquery/images/ui-icons_f5e175_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PromInc/organic-search-analytics/HEAD/organic-search-analytics/css/lib/jquery/images/ui-icons_f5e175_256x240.png -------------------------------------------------------------------------------- /organic-search-analytics/css/lib/jquery/images/ui-icons_f7a50d_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PromInc/organic-search-analytics/HEAD/organic-search-analytics/css/lib/jquery/images/ui-icons_f7a50d_256x240.png -------------------------------------------------------------------------------- /organic-search-analytics/css/lib/jquery/images/ui-icons_fcd113_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PromInc/organic-search-analytics/HEAD/organic-search-analytics/css/lib/jquery/images/ui-icons_fcd113_256x240.png -------------------------------------------------------------------------------- /organic-search-analytics/css/lib/jquery/images/ui-icons_ffffff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PromInc/organic-search-analytics/HEAD/organic-search-analytics/css/lib/jquery/images/ui-icons_ffffff_256x240.png -------------------------------------------------------------------------------- /organic-search-analytics/css/lib/jquery/images/ui-bg_gloss-wave_45_e14f1c_500x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PromInc/organic-search-analytics/HEAD/organic-search-analytics/css/lib/jquery/images/ui-bg_gloss-wave_45_e14f1c_500x100.png -------------------------------------------------------------------------------- /organic-search-analytics/css/lib/jquery/images/ui-bg_inset-hard_100_ffffff_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PromInc/organic-search-analytics/HEAD/organic-search-analytics/css/lib/jquery/images/ui-bg_inset-hard_100_ffffff_1x100.png -------------------------------------------------------------------------------- /organic-search-analytics/js/settings.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function(){ 2 | 3 | $("#sites_google_new_text").on( 'input', function() { 4 | updateNewSiteText( this ); 5 | }); 6 | $("#sites_google_new_text").on( 'keyup', function() { 7 | updateNewSiteText( this ); 8 | }); 9 | 10 | function updateNewSiteText( object ) { 11 | $("#sites_google_new_check").val( object.value ); 12 | if( object.value.length > 0 ) { 13 | $("#sites_google_new_check").prop('checked', true); 14 | } else { 15 | $("#sites_google_new_check").prop('checked', false); 16 | } 17 | } 18 | 19 | }); -------------------------------------------------------------------------------- /organic-search-analytics/apis/Google/Exception.php: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 17 | 18 |
19 | 20 | -------------------------------------------------------------------------------- /organic-search-analytics/js/lib/jqplot/plugins/jqplot.ciParser.min.js: -------------------------------------------------------------------------------- 1 | /* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com 2 | jsDate | (c) 2010-2013 Chris Leonello 3 | */(function(a){a.jqplot.ciParser=function(g,l){var m=[],o,n,h,f,e,c;if(typeof(g)=="string"){g=a.jqplot.JSON.parse(g,d)}else{if(typeof(g)=="object"){for(e in g){for(h=0;h=0){i=/^\/Date\((-?[0-9]+)\)\/$/.exec(k);if(i){return parseInt(i[1],10)}}return k}}for(var b in g){o=[];n=g[b];switch(b){case"PriceTicks":for(h=0;h 22 | */ 23 | abstract class Google_Signer_Abstract 24 | { 25 | /** 26 | * Signs data, returns the signature as binary data. 27 | */ 28 | abstract public function sign($data); 29 | } 30 | -------------------------------------------------------------------------------- /organic-search-analytics/js/lib/jqplot/plugins/jqplot.mobile.min.js: -------------------------------------------------------------------------------- 1 | /* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com 2 | jsDate | (c) 2010-2013 Chris Leonello 3 | */(function(b){function a(e,d,c){this.bindCustomEvents=function(){this.eventCanvas._elem.bind("vclick",{plot:this},this.onClick);this.eventCanvas._elem.bind("dblclick",{plot:this},this.onDblClick);this.eventCanvas._elem.bind("taphold",{plot:this},this.onDblClick);this.eventCanvas._elem.bind("vmousedown",{plot:this},this.onMouseDown);this.eventCanvas._elem.bind("vmousemove",{plot:this},this.onMouseMove);this.eventCanvas._elem.bind("mouseenter",{plot:this},this.onMouseEnter);this.eventCanvas._elem.bind("mouseleave",{plot:this},this.onMouseLeave);if(this.captureRightClick){this.eventCanvas._elem.bind("vmouseup",{plot:this},this.onRightClick);this.eventCanvas._elem.get(0).oncontextmenu=function(){return false}}else{this.eventCanvas._elem.bind("vmouseup",{plot:this},this.onMouseUp)}};this.plugins.mobile=true}b.jqplot.postInitHooks.push(a)})(jQuery); -------------------------------------------------------------------------------- /organic-search-analytics/apis/Google/Verifier/Abstract.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | abstract class Google_Verifier_Abstract 24 | { 25 | /** 26 | * Checks a signature, returns true if the signature is correct, 27 | * false otherwise. 28 | */ 29 | abstract public function verify($data, $signature); 30 | } 31 | -------------------------------------------------------------------------------- /organic-search-analytics/inc/html/_saveReport.php: -------------------------------------------------------------------------------- 1 |
2 |
Save this Report to Quick Links >>
3 | 25 |
-------------------------------------------------------------------------------- /organic-search-analytics/apis/Google/autoload.php: -------------------------------------------------------------------------------- 1 | parseQueryString( urldecode( $_POST['formData'] ) ); 9 | 10 | /* Report Name */ 11 | if( !isset( $formData['reportName'] ) || strlen( $formData['reportName'] ) <= 0 ) { 12 | $name = "untitled"; 13 | } else { 14 | $name = $formData['reportName']; 15 | } 16 | 17 | /* Report Category */ 18 | if( $formData['reportCatType'] == "new" ) { 19 | /* New Category */ 20 | $reportCategory = $formData['reportCatNew']; 21 | } else { 22 | /* Existing Category */ 23 | $reportCategory = intval( $formData['reportCatExisting'] ); 24 | } 25 | 26 | /* Report Parameters */ 27 | $reportParams = json_decode( urldecode( $formData['reportParams'] ) ); 28 | 29 | /* Load report class */ 30 | $report = new Reports(); 31 | 32 | /* Save Report */ 33 | $report->saveReport($reportParams->domain, $formData['reportName'], $reportCategory, $reportParams); 34 | break; 35 | } 36 | } 37 | 38 | ?> -------------------------------------------------------------------------------- /organic-search-analytics/apis/Google/Logger/Null.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 |
11 | Developed by Brian Prom 12 |
13 |
14 | 15 | ver 16 | 17 | 27 |
28 |
29 |
30 | 31 | -------------------------------------------------------------------------------- /organic-search-analytics/apis/Google/Auth/Abstract.php: -------------------------------------------------------------------------------- 1 | 25 | * 26 | */ 27 | abstract class Google_Auth_Abstract 28 | { 29 | /** 30 | * An utility function that first calls $this->auth->sign($request) and then 31 | * executes makeRequest() on that signed request. Used for when a request 32 | * should be authenticated 33 | * @param Google_Http_Request $request 34 | * @return Google_Http_Request $request 35 | */ 36 | abstract public function authenticatedRequest(Google_Http_Request $request); 37 | abstract public function sign(Google_Http_Request $request); 38 | } 39 | -------------------------------------------------------------------------------- /organic-search-analytics/apis/Google/Cache/Null.php: -------------------------------------------------------------------------------- 1 | client = $client; 31 | } 32 | 33 | /** 34 | * Return the associated Google_Client class. 35 | * @return Google_Client 36 | */ 37 | public function getClient() 38 | { 39 | return $this->client; 40 | } 41 | 42 | /** 43 | * Create a new HTTP Batch handler for this service 44 | * 45 | * @return Google_Http_Batch 46 | */ 47 | public function createBatch() 48 | { 49 | return new Google_Http_Batch( 50 | $this->client, 51 | false, 52 | $this->rootUrl, 53 | $this->batchPath 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /organic-search-analytics/apis/Bing/Webmasters.php: -------------------------------------------------------------------------------- 1 | 13 | * @link: http://promincproductions.com/blog/brian/ 14 | */ 15 | 16 | class BingWebmasters 17 | { 18 | 19 | const URL_BING_WEBMASTERS_JSON = 'https://ssl.bing.com/webmaster/api.svc/json/'; 20 | 21 | /** 22 | * Get data from Bing Webmaster Tools API 23 | * 24 | * List of avaiable methods: 25 | * https://msdn.microsoft.com/en-us/library/jj572365.aspx 26 | * 27 | * @param $api_key String API Key 28 | * @param $method String API Method 29 | * @param $siteUrl String Website URL that correlates to Bing Webmaster Tools 30 | * 31 | * @returns Object 32 | */ 33 | public function requestApi( $api_key, $method, $siteUrl = NULL ) { 34 | $url = self::URL_BING_WEBMASTERS_JSON.$method.'?apikey='.$api_key; 35 | 36 | if( $siteUrl ) { 37 | $url .= '&siteUrl='.$siteUrl; 38 | } 39 | 40 | $ch = curl_init(); 41 | curl_setopt($ch, CURLOPT_URL, $url); 42 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 43 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 44 | 45 | if( ! $result = curl_exec($ch) ) { 46 | $alert = array("type"=>"error", "message"=>"Error communicating with the Bing API"); 47 | } 48 | 49 | curl_close($ch); 50 | 51 | return $result; 52 | } 53 | 54 | } 55 | ?> -------------------------------------------------------------------------------- /organic-search-analytics/apis/Google/Service/Genomics.php: -------------------------------------------------------------------------------- 1 | 22 | * An API to store, process, explore, and share DNA sequence reads, reference- 23 | * based alignments, and variant calls.

24 | * 25 | *

26 | * For more information about this service, see the API 27 | * Documentation 28 | *

29 | * 30 | * @author Google, Inc. 31 | */ 32 | class Google_Service_Genomics extends Google_Service 33 | { 34 | 35 | 36 | 37 | 38 | 39 | /** 40 | * Constructs the internal representation of the Genomics service. 41 | * 42 | * @param Google_Client $client 43 | */ 44 | public function __construct(Google_Client $client) 45 | { 46 | parent::__construct($client); 47 | $this->rootUrl = 'https://genomics.googleapis.com/'; 48 | $this->servicePath = ''; 49 | $this->version = 'v1'; 50 | $this->serviceName = 'genomics'; 51 | 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /organic-search-analytics/apis/Google/Service/Cloudsearch.php: -------------------------------------------------------------------------------- 1 | 22 | * The Google Cloud Search API defines an application interface to index 23 | * documents that contain structured data and to search those indexes. It 24 | * supports full text search.

25 | * 26 | *

27 | * For more information about this service, see the API 28 | * Documentation 29 | *

30 | * 31 | * @author Google, Inc. 32 | */ 33 | class Google_Service_Cloudsearch extends Google_Service 34 | { 35 | 36 | 37 | 38 | 39 | 40 | /** 41 | * Constructs the internal representation of the Cloudsearch service. 42 | * 43 | * @param Google_Client $client 44 | */ 45 | public function __construct(Google_Client $client) 46 | { 47 | parent::__construct($client); 48 | $this->servicePath = ''; 49 | $this->version = 'v1'; 50 | $this->serviceName = 'cloudsearch'; 51 | 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /organic-search-analytics/apis/Google/Service/Playmoviespartner.php: -------------------------------------------------------------------------------- 1 | 22 | * An API providing Google Play Movies Partners a way to get the delivery status 23 | * of their titles.

24 | * 25 | *

26 | * For more information about this service, see the API 27 | * Documentation 28 | *

29 | * 30 | * @author Google, Inc. 31 | */ 32 | class Google_Service_Playmoviespartner extends Google_Service 33 | { 34 | 35 | 36 | 37 | 38 | 39 | /** 40 | * Constructs the internal representation of the Playmoviespartner service. 41 | * 42 | * @param Google_Client $client 43 | */ 44 | public function __construct(Google_Client $client) 45 | { 46 | parent::__construct($client); 47 | $this->rootUrl = 'https://playmoviespartner.googleapis.com/'; 48 | $this->servicePath = ''; 49 | $this->version = 'v1'; 50 | $this->serviceName = 'playmoviespartner'; 51 | 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /organic-search-analytics/apis/Google/Cache/Abstract.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | abstract class Google_Cache_Abstract 24 | { 25 | 26 | abstract public function __construct(Google_Client $client); 27 | 28 | /** 29 | * Retrieves the data for the given key, or false if they 30 | * key is unknown or expired 31 | * 32 | * @param String $key The key who's data to retrieve 33 | * @param boolean|int $expiration Expiration time in seconds 34 | * 35 | */ 36 | abstract public function get($key, $expiration = false); 37 | 38 | /** 39 | * Store the key => $value set. The $value is serialized 40 | * by this function so can be of any type 41 | * 42 | * @param string $key Key of the data 43 | * @param string $value data 44 | */ 45 | abstract public function set($key, $value); 46 | 47 | /** 48 | * Removes the key/data pair for the given $key 49 | * 50 | * @param String $key 51 | */ 52 | abstract public function delete($key); 53 | } 54 | -------------------------------------------------------------------------------- /organic-search-analytics/inc/code/gapiOauth.php: -------------------------------------------------------------------------------- 1 | 20 | * @link: http://promincproductions.com/blog/brian/ 21 | */ 22 | 23 | class GAPIoAuth 24 | { 25 | 26 | /** 27 | * Connect to Google API via oAuth 2.0 28 | * 29 | * @return Object Returns oAuth response 30 | */ 31 | public function LogIn() 32 | { 33 | /* OAUTH 2.0 */ 34 | $private_key = self::getPrivateKey(); 35 | $scopes = array('https://www.googleapis.com/auth/webmasters.readonly'); 36 | $credentials = new Google_Auth_AssertionCredentials( 37 | Config::OAUTH_CREDENTIALS_EMAIL, 38 | $scopes, 39 | $private_key 40 | ); 41 | 42 | $client = new Google_Client(); 43 | $client->setAssertionCredentials($credentials); 44 | if ($client->getAuth()->isAccessTokenExpired()) { 45 | $client->getAuth()->refreshTokenWithAssertion(); 46 | } 47 | 48 | return $client; 49 | } 50 | 51 | 52 | public function getPrivateKey() { 53 | return file_get_contents( $GLOBALS['basedir'].'/config/'.Config::OAUTH_CREDENTIALS_PRIVATE_KEY_FILE_NAME ); 54 | } 55 | 56 | } 57 | ?> -------------------------------------------------------------------------------- /organic-search-analytics/inc/code/debugLogger.php: -------------------------------------------------------------------------------- 1 | 20 | * @link: http://promincproductions.com/blog/brian/ 21 | */ 22 | 23 | class DebugLogger 24 | { 25 | 26 | 27 | const LOG_DIR_DEFAULT = "log"; 28 | const LOG_FILE_DEFAULT = "general.log"; 29 | 30 | 31 | /** 32 | * Log message to file 33 | * 34 | * @param message 35 | * @param level 36 | * @param dir 37 | * @param file 38 | */ 39 | public function debugLog($message, $level, $file = self::LOG_FILE_DEFAULT, $dir = self::LOG_DIR_DEFAULT) { 40 | error_log( 41 | $this->makeLogMessage($message,$level=Core::DEBUG), 42 | 3, 43 | $dir.Core::DS.$file 44 | ); 45 | } 46 | 47 | 48 | /** 49 | * Generate log message 50 | * 51 | * @param message 52 | * @param level 53 | * 54 | * @returns String Date Level Message File Line, 55 | */ 56 | public function makeLogMessage($message = "", $level = Core::DEBUG) { 57 | $trace = debug_backtrace(); 58 | return date('c'). 59 | Core::TAB. 60 | $level. 61 | Core::TAB. 62 | $message. 63 | Core::TAB. 64 | "in ".$trace[1]['file']. 65 | " line ".$trace[1]['line']. 66 | Core::NEWLINE 67 | ; 68 | } 69 | 70 | 71 | } 72 | ?> -------------------------------------------------------------------------------- /organic-search-analytics/apis/Google/IO/Exception.php: -------------------------------------------------------------------------------- 1 | = 0) { 44 | parent::__construct($message, $code, $previous); 45 | } else { 46 | parent::__construct($message, $code); 47 | } 48 | 49 | if (is_array($retryMap)) { 50 | $this->retryMap = $retryMap; 51 | } 52 | } 53 | 54 | /** 55 | * Gets the number of times the associated task can be retried. 56 | * 57 | * NOTE: -1 is returned if the task can be retried indefinitely 58 | * 59 | * @return integer 60 | */ 61 | public function allowedRetries() 62 | { 63 | if (isset($this->retryMap[$this->code])) { 64 | return $this->retryMap[$this->code]; 65 | } 66 | 67 | return 0; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /organic-search-analytics/index.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

Organic Search Analytics | Capture and Import

7 |
The below tools expedite grabbing data from Google Webmaster Tools
8 | 46 | 47 | -------------------------------------------------------------------------------- /organic-search-analytics/inc/code/globalIncludes.php: -------------------------------------------------------------------------------- 1 | "warning", "message"=>"Go to the Settings Configuration page and click the Save button."); 25 | } 26 | 27 | include_once( $GLOBALS['basedir'].'inc/code/mysql.php' ); //Database Connection 28 | include_once( $GLOBALS['basedir'].'inc/code/gapiOauth.php' ); //Google API Oauth 29 | include_once( $GLOBALS['basedir'].'inc/code/wmtimport.php' ); //WMT CSV import functions 30 | include_once( $GLOBALS['basedir'].'inc/code/dataCapture.php' ); //Data capturing functions 31 | include_once( $GLOBALS['basedir'].'inc/code/reports.php' ); //Reporting functions 32 | 33 | include_once( $GLOBALS['basedir'].'apis/Google/autoload.php' ); //Google API 34 | include_once( $GLOBALS['basedir'].'apis/Bing/Webmasters.php' ); //Bing Search API 35 | 36 | /* Load classes */ 37 | $mysql = new MySQL(); //Load MySQL 38 | $dataCapture = new DataCapture(); //Load Data Capturing tools 39 | 40 | $GLOBALS['db'] = $core->mysql_connect_db(); // Connect to DB 41 | } else { 42 | $alert = array("type"=>"warning", "message"=>"Please set your configuration settings before procedding. Configure Now"); 43 | } 44 | ?> 45 | -------------------------------------------------------------------------------- /organic-search-analytics/apis/Google/Auth/LoginTicket.php: -------------------------------------------------------------------------------- 1 | 26 | */ 27 | class Google_Auth_LoginTicket 28 | { 29 | const USER_ATTR = "sub"; 30 | 31 | // Information from id token envelope. 32 | private $envelope; 33 | 34 | // Information from id token payload. 35 | private $payload; 36 | 37 | /** 38 | * Creates a user based on the supplied token. 39 | * 40 | * @param string $envelope Header from a verified authentication token. 41 | * @param string $payload Information from a verified authentication token. 42 | */ 43 | public function __construct($envelope, $payload) 44 | { 45 | $this->envelope = $envelope; 46 | $this->payload = $payload; 47 | } 48 | 49 | /** 50 | * Returns the numeric identifier for the user. 51 | * @throws Google_Auth_Exception 52 | * @return 53 | */ 54 | public function getUserId() 55 | { 56 | if (array_key_exists(self::USER_ATTR, $this->payload)) { 57 | return $this->payload[self::USER_ATTR]; 58 | } 59 | throw new Google_Auth_Exception("No user_id in token"); 60 | } 61 | 62 | /** 63 | * Returns attributes from the login ticket. This can contain 64 | * various information about the user session. 65 | * @return array 66 | */ 67 | public function getAttributes() 68 | { 69 | return array("envelope" => $this->envelope, "payload" => $this->payload); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /organic-search-analytics/apis/Google/Auth/Simple.php: -------------------------------------------------------------------------------- 1 | client = $client; 34 | } 35 | 36 | /** 37 | * Perform an authenticated / signed apiHttpRequest. 38 | * This function takes the apiHttpRequest, calls apiAuth->sign on it 39 | * (which can modify the request in what ever way fits the auth mechanism) 40 | * and then calls apiCurlIO::makeRequest on the signed request 41 | * 42 | * @param Google_Http_Request $request 43 | * @return Google_Http_Request The resulting HTTP response including the 44 | * responseHttpCode, responseHeaders and responseBody. 45 | */ 46 | public function authenticatedRequest(Google_Http_Request $request) 47 | { 48 | $request = $this->sign($request); 49 | return $this->io->makeRequest($request); 50 | } 51 | 52 | public function sign(Google_Http_Request $request) 53 | { 54 | $key = $this->client->getClassConfig($this, 'developer_key'); 55 | if ($key) { 56 | $this->client->getLogger()->debug( 57 | 'Simple API Access developer key authentication' 58 | ); 59 | $request->setQueryParam('key', $key); 60 | } 61 | return $request; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /organic-search-analytics/js/lib/jqplot/plugins/jqplot.trendline.min.js: -------------------------------------------------------------------------------- 1 | /* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com 2 | jsDate | (c) 2010-2013 Chris Leonello 3 | */(function(f){f.jqplot.Trendline=function(){this.show=f.jqplot.config.enablePlugins;this.color="#666666";this.renderer=new f.jqplot.LineRenderer();this.rendererOptions={marker:{show:false}};this.label="";this.type="linear";this.shadow=true;this.markerRenderer={show:false};this.lineWidth=1.5;this.shadowAngle=45;this.shadowOffset=1;this.shadowAlpha=0.07;this.shadowDepth=3;this.isTrendline=true};f.jqplot.postSeriesInitHooks.push(e);f.jqplot.postDrawSeriesHooks.push(g);f.jqplot.addLegendRowHooks.push(a);function a(k){var j=null;if(k.trendline&&k.trendline.show){var i=k.trendline.label.toString();if(i){j={label:i,color:k.trendline.color}}}return j}function e(m,k,j,i,l){if(this._type&&(this._type==="line"||this._type=="bar")){this.trendline=new f.jqplot.Trendline();i=i||{};f.extend(true,this.trendline,{color:this.color},j.trendline,i.trendline);this.trendline.renderer.init.call(this.trendline,null)}}function g(m,i){i=f.extend(true,{},this.trendline,i);if(this.trendline&&i.show){var k;var l=i.data||this.data;k=c(l,this.trendline.type);var j=i.gridData||this.renderer.makeGridData.call(this,k.data);this.trendline.renderer.draw.call(this.trendline,m,j,{showLine:true,shadow:this.trendline.shadow})}}function b(w,v,n){var u=(n==null)?"linear":n;var s=w.length;var t;var z;var o=0;var m=0;var r=0;var q=0;var l=0;var j=[];var k=[];if(u=="linear"){k=w;j=v}else{if(u=="exp"||u=="exponential"){for(var p=0;p 26 | */ 27 | class Google_Verifier_Pem extends Google_Verifier_Abstract 28 | { 29 | private $publicKey; 30 | 31 | /** 32 | * Constructs a verifier from the supplied PEM-encoded certificate. 33 | * 34 | * $pem: a PEM encoded certificate (not a file). 35 | * @param $pem 36 | * @throws Google_Auth_Exception 37 | * @throws Google_Exception 38 | */ 39 | public function __construct($pem) 40 | { 41 | if (!function_exists('openssl_x509_read')) { 42 | throw new Google_Exception('Google API PHP client needs the openssl PHP extension'); 43 | } 44 | $this->publicKey = openssl_x509_read($pem); 45 | if (!$this->publicKey) { 46 | throw new Google_Auth_Exception("Unable to parse PEM: $pem"); 47 | } 48 | } 49 | 50 | public function __destruct() 51 | { 52 | if ($this->publicKey) { 53 | openssl_x509_free($this->publicKey); 54 | } 55 | } 56 | 57 | /** 58 | * Verifies the signature on data. 59 | * 60 | * Returns true if the signature is valid, false otherwise. 61 | * @param $data 62 | * @param $signature 63 | * @throws Google_Auth_Exception 64 | * @return bool 65 | */ 66 | public function verify($data, $signature) 67 | { 68 | $hash = defined("OPENSSL_ALGO_SHA256") ? OPENSSL_ALGO_SHA256 : "sha256"; 69 | $status = openssl_verify($data, $signature, $this->publicKey, $hash); 70 | if ($status === -1) { 71 | throw new Google_Auth_Exception('Signature verification error: ' . openssl_error_string()); 72 | } 73 | return $status === 1; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /organic-search-analytics/js/lib/jqplot/plugins/jqplot.canvasAxisLabelRenderer.min.js: -------------------------------------------------------------------------------- 1 | /* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com 2 | jsDate | (c) 2010-2013 Chris Leonello 3 | */(function(a){a.jqplot.CanvasAxisLabelRenderer=function(b){this.angle=0;this.axis;this.show=true;this.showLabel=true;this.label="";this.fontFamily='"Trebuchet MS", Arial, Helvetica, sans-serif';this.fontSize="11pt";this.fontWeight="normal";this.fontStretch=1;this.textColor="#666666";this.enableFontSupport=true;this.pt2px=null;this._elem;this._ctx;this._plotWidth;this._plotHeight;this._plotDimensions={height:null,width:null};a.extend(true,this,b);if(b.angle==null&&this.axis!="xaxis"&&this.axis!="x2axis"){this.angle=-90}var c={fontSize:this.fontSize,fontWeight:this.fontWeight,fontStretch:this.fontStretch,fillStyle:this.textColor,angle:this.getAngleRad(),fontFamily:this.fontFamily};if(this.pt2px){c.pt2px=this.pt2px}if(this.enableFontSupport){if(a.jqplot.support_canvas_text()){this._textRenderer=new a.jqplot.CanvasFontRenderer(c)}else{this._textRenderer=new a.jqplot.CanvasTextRenderer(c)}}else{this._textRenderer=new a.jqplot.CanvasTextRenderer(c)}};a.jqplot.CanvasAxisLabelRenderer.prototype.init=function(b){a.extend(true,this,b);this._textRenderer.init({fontSize:this.fontSize,fontWeight:this.fontWeight,fontStretch:this.fontStretch,fillStyle:this.textColor,angle:this.getAngleRad(),fontFamily:this.fontFamily})};a.jqplot.CanvasAxisLabelRenderer.prototype.getWidth=function(d){if(this._elem){return this._elem.outerWidth(true)}else{var f=this._textRenderer;var c=f.getWidth(d);var e=f.getHeight(d);var b=Math.abs(Math.sin(f.angle)*e)+Math.abs(Math.cos(f.angle)*c);return b}};a.jqplot.CanvasAxisLabelRenderer.prototype.getHeight=function(d){if(this._elem){return this._elem.outerHeight(true)}else{var f=this._textRenderer;var c=f.getWidth(d);var e=f.getHeight(d);var b=Math.abs(Math.cos(f.angle)*e)+Math.abs(Math.sin(f.angle)*c);return b}};a.jqplot.CanvasAxisLabelRenderer.prototype.getAngleRad=function(){var b=this.angle*Math.PI/180;return b};a.jqplot.CanvasAxisLabelRenderer.prototype.draw=function(c,f){if(this._elem){if(a.jqplot.use_excanvas&&window.G_vmlCanvasManager.uninitElement!==undefined){window.G_vmlCanvasManager.uninitElement(this._elem.get(0))}this._elem.emptyForce();this._elem=null}var e=f.canvasManager.getCanvas();this._textRenderer.setText(this.label,c);var b=this.getWidth(c);var d=this.getHeight(c);e.width=b;e.height=d;e.style.width=b;e.style.height=d;e=f.canvasManager.initCanvas(e);this._elem=a(e);this._elem.css({position:"absolute"});this._elem.addClass("jqplot-"+this.axis+"-label");e=null;return this._elem};a.jqplot.CanvasAxisLabelRenderer.prototype.pack=function(){this._textRenderer.draw(this._elem.get(0).getContext("2d"),this.label)}})(jQuery); -------------------------------------------------------------------------------- /organic-search-analytics/apis/Google/Logger/Psr.php: -------------------------------------------------------------------------------- 1 | setLogger($logger); 45 | } 46 | } 47 | 48 | /** 49 | * Sets the PSR-3 logger where logging will be delegated. 50 | * 51 | * NOTE: The `$logger` should technically implement 52 | * `Psr\Log\LoggerInterface`, but we don't explicitly require this so that 53 | * we can be compatible with PHP 5.2. 54 | * 55 | * @param Psr\Log\LoggerInterface $logger The PSR-3 logger 56 | */ 57 | public function setLogger(/*Psr\Log\LoggerInterface*/ $logger) 58 | { 59 | $this->logger = $logger; 60 | } 61 | 62 | /** 63 | * {@inheritdoc} 64 | */ 65 | public function shouldHandle($level) 66 | { 67 | return isset($this->logger) && parent::shouldHandle($level); 68 | } 69 | 70 | /** 71 | * {@inheritdoc} 72 | */ 73 | public function log($level, $message, array $context = array()) 74 | { 75 | if (!$this->shouldHandle($level)) { 76 | return false; 77 | } 78 | 79 | if ($context) { 80 | $this->reverseJsonInContext($context); 81 | } 82 | 83 | $levelName = is_int($level) ? array_search($level, self::$levels) : $level; 84 | $this->logger->log($levelName, $message, $context); 85 | } 86 | 87 | /** 88 | * {@inheritdoc} 89 | */ 90 | protected function write($message, array $context = array()) 91 | { 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /organic-search-analytics/apis/Google/Collection.php: -------------------------------------------------------------------------------- 1 | modelData[$this->collection_key]) 19 | && is_array($this->modelData[$this->collection_key])) { 20 | reset($this->modelData[$this->collection_key]); 21 | } 22 | } 23 | 24 | public function current() 25 | { 26 | $this->coerceType($this->key()); 27 | if (is_array($this->modelData[$this->collection_key])) { 28 | return current($this->modelData[$this->collection_key]); 29 | } 30 | } 31 | 32 | public function key() 33 | { 34 | if (isset($this->modelData[$this->collection_key]) 35 | && is_array($this->modelData[$this->collection_key])) { 36 | return key($this->modelData[$this->collection_key]); 37 | } 38 | } 39 | 40 | public function next() 41 | { 42 | return next($this->modelData[$this->collection_key]); 43 | } 44 | 45 | public function valid() 46 | { 47 | $key = $this->key(); 48 | return $key !== null && $key !== false; 49 | } 50 | 51 | public function count() 52 | { 53 | if (!isset($this->modelData[$this->collection_key])) { 54 | return 0; 55 | } 56 | return count($this->modelData[$this->collection_key]); 57 | } 58 | 59 | public function offsetExists($offset) 60 | { 61 | if (!is_numeric($offset)) { 62 | return parent::offsetExists($offset); 63 | } 64 | return isset($this->modelData[$this->collection_key][$offset]); 65 | } 66 | 67 | public function offsetGet($offset) 68 | { 69 | if (!is_numeric($offset)) { 70 | return parent::offsetGet($offset); 71 | } 72 | $this->coerceType($offset); 73 | return $this->modelData[$this->collection_key][$offset]; 74 | } 75 | 76 | public function offsetSet($offset, $value) 77 | { 78 | if (!is_numeric($offset)) { 79 | return parent::offsetSet($offset, $value); 80 | } 81 | $this->modelData[$this->collection_key][$offset] = $value; 82 | } 83 | 84 | public function offsetUnset($offset) 85 | { 86 | if (!is_numeric($offset)) { 87 | return parent::offsetUnset($offset); 88 | } 89 | unset($this->modelData[$this->collection_key][$offset]); 90 | } 91 | 92 | private function coerceType($offset) 93 | { 94 | $typeKey = $this->keyType($this->collection_key); 95 | if (isset($this->$typeKey) && !is_object($this->modelData[$this->collection_key][$offset])) { 96 | $type = $this->$typeKey; 97 | $this->modelData[$this->collection_key][$offset] = 98 | new $type($this->modelData[$this->collection_key][$offset]); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /organic-search-analytics/js/lib/jqplot/plugins/jqplot.canvasAxisTickRenderer.min.js: -------------------------------------------------------------------------------- 1 | /* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com 2 | jsDate | (c) 2010-2013 Chris Leonello 3 | */(function(a){a.jqplot.CanvasAxisTickRenderer=function(b){this.mark="outside";this.showMark=true;this.showGridline=true;this.isMinorTick=false;this.angle=0;this.markSize=4;this.show=true;this.showLabel=true;this.labelPosition="auto";this.label="";this.value=null;this._styles={};this.formatter=a.jqplot.DefaultTickFormatter;this.formatString="";this.prefix="";this.fontFamily='"Trebuchet MS", Arial, Helvetica, sans-serif';this.fontSize="10pt";this.fontWeight="normal";this.fontStretch=1;this.textColor="#666666";this.enableFontSupport=true;this.pt2px=null;this._elem;this._ctx;this._plotWidth;this._plotHeight;this._plotDimensions={height:null,width:null};a.extend(true,this,b);var c={fontSize:this.fontSize,fontWeight:this.fontWeight,fontStretch:this.fontStretch,fillStyle:this.textColor,angle:this.getAngleRad(),fontFamily:this.fontFamily};if(this.pt2px){c.pt2px=this.pt2px}if(this.enableFontSupport){if(a.jqplot.support_canvas_text()){this._textRenderer=new a.jqplot.CanvasFontRenderer(c)}else{this._textRenderer=new a.jqplot.CanvasTextRenderer(c)}}else{this._textRenderer=new a.jqplot.CanvasTextRenderer(c)}};a.jqplot.CanvasAxisTickRenderer.prototype.init=function(b){a.extend(true,this,b);this._textRenderer.init({fontSize:this.fontSize,fontWeight:this.fontWeight,fontStretch:this.fontStretch,fillStyle:this.textColor,angle:this.getAngleRad(),fontFamily:this.fontFamily})};a.jqplot.CanvasAxisTickRenderer.prototype.getWidth=function(d){if(this._elem){return this._elem.outerWidth(true)}else{var f=this._textRenderer;var c=f.getWidth(d);var e=f.getHeight(d);var b=Math.abs(Math.sin(f.angle)*e)+Math.abs(Math.cos(f.angle)*c);return b}};a.jqplot.CanvasAxisTickRenderer.prototype.getHeight=function(d){if(this._elem){return this._elem.outerHeight(true)}else{var f=this._textRenderer;var c=f.getWidth(d);var e=f.getHeight(d);var b=Math.abs(Math.cos(f.angle)*e)+Math.abs(Math.sin(f.angle)*c);return b}};a.jqplot.CanvasAxisTickRenderer.prototype.getTop=function(b){if(this._elem){return this._elem.position().top}else{return null}};a.jqplot.CanvasAxisTickRenderer.prototype.getAngleRad=function(){var b=this.angle*Math.PI/180;return b};a.jqplot.CanvasAxisTickRenderer.prototype.setTick=function(b,d,c){this.value=b;if(c){this.isMinorTick=true}return this};a.jqplot.CanvasAxisTickRenderer.prototype.draw=function(c,f){if(!this.label){this.label=this.prefix+this.formatter(this.formatString,this.value)}if(this._elem){if(a.jqplot.use_excanvas&&window.G_vmlCanvasManager.uninitElement!==undefined){window.G_vmlCanvasManager.uninitElement(this._elem.get(0))}this._elem.emptyForce();this._elem=null}var e=f.canvasManager.getCanvas();this._textRenderer.setText(this.label,c);var b=this.getWidth(c);var d=this.getHeight(c);e.width=b;e.height=d;e.style.width=b;e.style.height=d;e.style.textAlign="left";e.style.position="absolute";e=f.canvasManager.initCanvas(e);this._elem=a(e);this._elem.css(this._styles);this._elem.addClass("jqplot-"+this.axis+"-tick");e=null;return this._elem};a.jqplot.CanvasAxisTickRenderer.prototype.pack=function(){this._textRenderer.draw(this._elem.get(0).getContext("2d"),this.label)}})(jQuery); -------------------------------------------------------------------------------- /organic-search-analytics/js/lib/jqplot/plugins/jqplot.blockRenderer.min.js: -------------------------------------------------------------------------------- 1 | /* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com 2 | jsDate | (c) 2010-2013 Chris Leonello 3 | */(function(a){a.jqplot.BlockRenderer=function(){a.jqplot.LineRenderer.call(this)};a.jqplot.BlockRenderer.prototype=new a.jqplot.LineRenderer();a.jqplot.BlockRenderer.prototype.constructor=a.jqplot.BlockRenderer;a.jqplot.BlockRenderer.prototype.init=function(b){this.css={padding:"2px",border:"1px solid #999",textAlign:"center"};this.escapeHtml=false;this.insertBreaks=true;this.varyBlockColors=false;a.extend(true,this,b);if(this.css.backgroundColor){this.color=this.css.backgroundColor}else{if(this.css.background){this.color=this.css.background}else{if(!this.varyBlockColors){this.css.background=this.color}}}this.canvas=new a.jqplot.BlockCanvas();this.shadowCanvas=new a.jqplot.BlockCanvas();this.canvas._plotDimensions=this._plotDimensions;this.shadowCanvas._plotDimensions=this._plotDimensions;this._type="block";this.moveBlock=function(l,j,i,e){var c=this.canvas._elem.children(":eq("+l+")");this.data[l][0]=j;this.data[l][1]=i;this._plotData[l][0]=j;this._plotData[l][1]=i;this._stackData[l][0]=j;this._stackData[l][1]=i;this.gridData[l][0]=this._xaxis.series_u2p(j);this.gridData[l][1]=this._yaxis.series_u2p(i);var k=c.outerWidth();var f=c.outerHeight();var d=this.gridData[l][0]-k/2+"px";var g=this.gridData[l][1]-f/2+"px";if(e){if(parseInt(e,10)){e=parseInt(e,10)}c.animate({left:d,top:g},e)}else{c.css({left:d,top:g})}c=null}};a.jqplot.BlockRenderer.prototype.draw=function(q,o,r){if(this.plugins.pointLabels){this.plugins.pointLabels.show=false}var f,c,l,o,p,k,n,g,e,m;var b=(r!=undefined)?r:{};var j=new a.jqplot.ColorGenerator(this.seriesColors);this.canvas._elem.empty();for(f=0;f")}k=a.extend(true,{},this.css,k);c=a('
');this.canvas._elem.append(c);this.escapeHtml?c.text(p):c.html(p);delete k.position;delete k.marginRight;delete k.marginLeft;if(!k.background&&!k.backgroundColor&&!k.backgroundImage){k.background=j.next()}c.css(k);n=c.outerWidth();g=c.outerHeight();e=o[0]-n/2+"px";m=o[1]-g/2+"px";c.css({left:e,top:m});c=null}};a.jqplot.BlockCanvas=function(){a.jqplot.ElemContainer.call(this);this._ctx};a.jqplot.BlockCanvas.prototype=new a.jqplot.ElemContainer();a.jqplot.BlockCanvas.prototype.constructor=a.jqplot.BlockCanvas;a.jqplot.BlockCanvas.prototype.createElement=function(i,e,c){this._offsets=i;var b="jqplot-blockCanvas";if(e!=undefined){b=e}var g;if(this._elem){g=this._elem.get(0)}else{g=document.createElement("div")}if(c!=undefined){this._plotDimensions=c}var d=this._plotDimensions.width-this._offsets.left-this._offsets.right+"px";var f=this._plotDimensions.height-this._offsets.top-this._offsets.bottom+"px";this._elem=a(g);this._elem.css({position:"absolute",width:d,height:f,left:this._offsets.left,top:this._offsets.top});this._elem.addClass(b);return this._elem};a.jqplot.BlockCanvas.prototype.setContext=function(){this._ctx={canvas:{width:0,height:0},clearRect:function(){return null}};return this._ctx}})(jQuery); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Organic Search Analytics Importer 2 | ## Report Bug Fixes (May 25th, 2017) 3 | ### New Features (2.5.4) - current master branch 4 | - Javascript errors were preventing features and functionality of the report settings and chart display. 5 | 6 | ## Report Chart Updates (May 17th, 2017) 7 | ### New Features (2.5.3) 8 | - Bug fixes and data layout and style updates to make the charts more useful 9 | 10 | ## CRON Features (April 20th, 2017) 11 | ### New Features (2.5.2) 12 | - Ability to run the import via CRON by calling data-capture-run.php 13 | 14 | ## Version 2.5.1 Now Available!!! (April 9th, 2017) 15 | ### New Features (2.5.1) 16 | - Bug fix that prevented Bing data from capturing after 2.5.0 17 | - Fix for the debug logger that will show which file/line the error was thrown on 18 | - Reduce PHP errors 19 | - Provide clearer messaging when API connections fail 20 | 21 | > **Notice** You will need to run an update script from the home page for this version to run smoothly as it needs to update the database. Go to the homepage of your installation, choose **Upgrade Scripts** and then choose **Run Update for Version 2.5.0 to 2.5.1**. Failure to run this script will result in undesired results across the utility. 22 | 23 | ### New Features (2.5.0) 24 | - Choose which dimensions to capture in the settings 25 | - Capture Page from Google Search Analytics 26 | - Reporting Bug Fixes and Enhancements 27 | 28 | > **Notice** You will need to run an update script from the home page for this version to run smoothly as it needs to update the database. Go to the homepage of your installation, choose **Upgrade Scripts** and then choose **Run Update for Version 2.x.x to 2.5.0**. Failure to run this script will result in undesired results across the utility. 29 | 30 | 31 | ## Features 32 | Import organic search analytics from Google Search Console (Google Webmaster Tools) and Bing Search Keywords (Bing Webmaster Tools) into a local database to keep a historical, local, and combined record of your (past the 90 days that Google offers). 33 | 34 | This archive of organic search analytics will allow you to study long term SEO trends and shifts and can help to monitor and gauge performance of a websites over time. 35 | 36 | Once setup, this tool will do all of the hard work, allowing the 'savy marketer' or the tech guy that wants to impress the boss to quickly get to data that Google no longer provides. 37 | 38 | Reporting features allow you to get to data that makes a difference in your marketing decisions. The reports are flexible and can be saved for ease of access/use. 39 | 40 | ## Requirements 41 | - PHP (5.4.1+) 42 | - MySQL 43 | 44 | ## Installation 45 | [Installation Instructions can be found in the Wiki](https://github.com/PromInc/organic-search-analytics/wiki/Installation). 46 | 47 | ## Upgrade Information 48 | Upgrading between certain versions requires an additional step. Instructions on how to perform the upgrade can be found in the ['Upgrading between versions' Wiki page](https://github.com/PromInc/organic-search-analytics/wiki/Upgrading-between-Versions). 49 | 50 | ## Additional Information 51 | More information can be found in the [Wiki](https://github.com/PromInc/organic-search-analytics/wiki). 52 | 53 | ## Related Information 54 | - [Google's definition of it's Search Analytics metrics](https://support.google.com/webmasters/answer/7042828) 55 | -------------------------------------------------------------------------------- /organic-search-analytics/apis/Google/Service/Exception.php: -------------------------------------------------------------------------------- 1 | = 0) { 53 | parent::__construct($message, $code, $previous); 54 | } else { 55 | parent::__construct($message, $code); 56 | } 57 | 58 | $this->errors = $errors; 59 | 60 | if (is_array($retryMap)) { 61 | $this->retryMap = $retryMap; 62 | } 63 | } 64 | 65 | /** 66 | * An example of the possible errors returned. 67 | * 68 | * { 69 | * "domain": "global", 70 | * "reason": "authError", 71 | * "message": "Invalid Credentials", 72 | * "locationType": "header", 73 | * "location": "Authorization", 74 | * } 75 | * 76 | * @return [{string, string}] List of errors return in an HTTP response or []. 77 | */ 78 | public function getErrors() 79 | { 80 | return $this->errors; 81 | } 82 | 83 | /** 84 | * Gets the number of times the associated task can be retried. 85 | * 86 | * NOTE: -1 is returned if the task can be retried indefinitely 87 | * 88 | * @return integer 89 | */ 90 | public function allowedRetries() 91 | { 92 | if (isset($this->retryMap[$this->code])) { 93 | return $this->retryMap[$this->code]; 94 | } 95 | 96 | $errors = $this->getErrors(); 97 | 98 | if (!empty($errors) && isset($errors[0]['reason']) && 99 | isset($this->retryMap[$errors[0]['reason']])) { 100 | return $this->retryMap[$errors[0]['reason']]; 101 | } 102 | 103 | return 0; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /organic-search-analytics/apis/Google/Cache/Apc.php: -------------------------------------------------------------------------------- 1 | 29 | */ 30 | class Google_Cache_Apc extends Google_Cache_Abstract 31 | { 32 | /** 33 | * @var Google_Client the current client 34 | */ 35 | private $client; 36 | 37 | public function __construct(Google_Client $client) 38 | { 39 | if (! function_exists('apc_add') ) { 40 | $error = "Apc functions not available"; 41 | 42 | $client->getLogger()->error($error); 43 | throw new Google_Cache_Exception($error); 44 | } 45 | 46 | $this->client = $client; 47 | } 48 | 49 | /** 50 | * @inheritDoc 51 | */ 52 | public function get($key, $expiration = false) 53 | { 54 | $ret = apc_fetch($key); 55 | if ($ret === false) { 56 | $this->client->getLogger()->debug( 57 | 'APC cache miss', 58 | array('key' => $key) 59 | ); 60 | return false; 61 | } 62 | if (is_numeric($expiration) && (time() - $ret['time'] > $expiration)) { 63 | $this->client->getLogger()->debug( 64 | 'APC cache miss (expired)', 65 | array('key' => $key, 'var' => $ret) 66 | ); 67 | $this->delete($key); 68 | return false; 69 | } 70 | 71 | $this->client->getLogger()->debug( 72 | 'APC cache hit', 73 | array('key' => $key, 'var' => $ret) 74 | ); 75 | 76 | return $ret['data']; 77 | } 78 | 79 | /** 80 | * @inheritDoc 81 | */ 82 | public function set($key, $value) 83 | { 84 | $var = array('time' => time(), 'data' => $value); 85 | $rc = apc_store($key, $var); 86 | 87 | if ($rc == false) { 88 | $this->client->getLogger()->error( 89 | 'APC cache set failed', 90 | array('key' => $key, 'var' => $var) 91 | ); 92 | throw new Google_Cache_Exception("Couldn't store data"); 93 | } 94 | 95 | $this->client->getLogger()->debug( 96 | 'APC cache set', 97 | array('key' => $key, 'var' => $var) 98 | ); 99 | } 100 | 101 | /** 102 | * @inheritDoc 103 | * @param String $key 104 | */ 105 | public function delete($key) 106 | { 107 | $this->client->getLogger()->debug( 108 | 'APC cache delete', 109 | array('key' => $key) 110 | ); 111 | apc_delete($key); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /organic-search-analytics/js/lib/jqplot/plugins/jqplot.json2.min.js: -------------------------------------------------------------------------------- 1 | /* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com 2 | jsDate | (c) 2010-2013 Chris Leonello 3 | */(function($){$.jqplot.JSON=window.JSON;if(!window.JSON){$.jqplot.JSON={}}function f(n){return n<10?"0"+n:n}if(typeof Date.prototype.toJSON!=="function"){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf()}}var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==="string"?c:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+string+'"'}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==="object"&&typeof value.toJSON==="function"){value=value.toJSON(key)}if(typeof rep==="function"){value=rep.call(holder,key,value)}switch(typeof value){case"string":return quote(value);case"number":return isFinite(value)?String(value):"null";case"boolean":case"null":return String(value);case"object":if(!value){return"null"}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==="[object Array]"){length=value.length;for(i=0;i 28 | */ 29 | class Google_Signer_P12 extends Google_Signer_Abstract 30 | { 31 | // OpenSSL private key resource 32 | private $privateKey; 33 | 34 | // Creates a new signer from a .p12 file. 35 | public function __construct($p12, $password) 36 | { 37 | if (!function_exists('openssl_x509_read')) { 38 | throw new Google_Exception( 39 | 'The Google PHP API library needs the openssl PHP extension' 40 | ); 41 | } 42 | 43 | // If the private key is provided directly, then this isn't in the p12 44 | // format. Different versions of openssl support different p12 formats 45 | // and the key from google wasn't being accepted by the version available 46 | // at the time. 47 | if (!$password && strpos($p12, "-----BEGIN RSA PRIVATE KEY-----") !== false) { 48 | $this->privateKey = openssl_pkey_get_private($p12); 49 | } elseif ($password === 'notasecret' && strpos($p12, "-----BEGIN PRIVATE KEY-----") !== false) { 50 | $this->privateKey = openssl_pkey_get_private($p12); 51 | } else { 52 | // This throws on error 53 | $certs = array(); 54 | if (!openssl_pkcs12_read($p12, $certs, $password)) { 55 | throw new Google_Auth_Exception( 56 | "Unable to parse the p12 file. " . 57 | "Is this a .p12 file? Is the password correct? OpenSSL error: " . 58 | openssl_error_string() 59 | ); 60 | } 61 | // TODO(beaton): is this part of the contract for the openssl_pkcs12_read 62 | // method? What happens if there are multiple private keys? Do we care? 63 | if (!array_key_exists("pkey", $certs) || !$certs["pkey"]) { 64 | throw new Google_Auth_Exception("No private key found in p12 file."); 65 | } 66 | $this->privateKey = openssl_pkey_get_private($certs['pkey']); 67 | } 68 | 69 | if (!$this->privateKey) { 70 | throw new Google_Auth_Exception("Unable to load private key"); 71 | } 72 | } 73 | 74 | public function __destruct() 75 | { 76 | if ($this->privateKey) { 77 | openssl_pkey_free($this->privateKey); 78 | } 79 | } 80 | 81 | public function sign($data) 82 | { 83 | if (version_compare(PHP_VERSION, '5.3.0') < 0) { 84 | throw new Google_Auth_Exception( 85 | "PHP 5.3.0 or higher is required to use service accounts." 86 | ); 87 | } 88 | $hash = defined("OPENSSL_ALGO_SHA256") ? OPENSSL_ALGO_SHA256 : "sha256"; 89 | if (!openssl_sign($data, $signature, $this->privateKey, $hash)) { 90 | throw new Google_Auth_Exception("Unable to sign data"); 91 | } 92 | return $signature; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /organic-search-analytics.sql: -------------------------------------------------------------------------------- 1 | -- phpMyAdmin SQL Dump 2 | -- version 4.0.9 3 | -- http://www.phpmyadmin.net 4 | -- 5 | -- Host: 127.0.0.1 6 | -- Generation Time: Sep 29, 2015 at 11:43 PM 7 | -- Server version: 5.5.34 8 | -- PHP Version: 5.4.22 9 | 10 | SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; 11 | SET time_zone = "+00:00"; 12 | 13 | 14 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 15 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 16 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 17 | /*!40101 SET NAMES utf8 */; 18 | 19 | -- 20 | -- Database: `organic-search-analytics` 21 | -- 22 | 23 | -- -------------------------------------------------------- 24 | 25 | -- 26 | -- Table structure for table `report_saved` 27 | -- 28 | 29 | CREATE TABLE IF NOT EXISTS `report_saved` ( 30 | `id` int(11) NOT NULL AUTO_INCREMENT, 31 | `domain` varchar(256) NOT NULL, 32 | `name` varchar(256) NOT NULL, 33 | `category` int(11) NOT NULL, 34 | `paramaters` varchar(1000) NOT NULL, 35 | PRIMARY KEY (`id`) 36 | ) ENGINE=InnoDB DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci AUTO_INCREMENT=0 ; 37 | 38 | -- -------------------------------------------------------- 39 | 40 | -- 41 | -- Table structure for table `report_saved_categories` 42 | -- 43 | 44 | CREATE TABLE IF NOT EXISTS `report_saved_categories` ( 45 | `id` int(11) NOT NULL AUTO_INCREMENT, 46 | `name` varchar(256) NOT NULL, 47 | `description` varchar(1000) NOT NULL, 48 | PRIMARY KEY (`id`) 49 | ) ENGINE=InnoDB DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci AUTO_INCREMENT=0 ; 50 | 51 | -- -------------------------------------------------------- 52 | 53 | -- 54 | -- Table structure for table `search_analytics` 55 | -- 56 | 57 | CREATE TABLE IF NOT EXISTS `search_analytics` ( 58 | `id` int(11) NOT NULL AUTO_INCREMENT, 59 | `domain` varchar(256) NOT NULL, 60 | `date` date NOT NULL, 61 | `search_engine` varchar(50) NOT NULL, 62 | `search_type` varchar(24) NULL DEFAULT NULL, 63 | `device_type` varchar(24) NULL DEFAULT NULL, 64 | `country` varchar(10) NULL DEFAULT NULL, 65 | `query` varchar(500) NULL DEFAULT NULL, 66 | `page` VARCHAR(500) NULL DEFAULT NULL, 67 | `impressions` int(11) NOT NULL, 68 | `clicks` int(11) NOT NULL, 69 | `ctr` float NOT NULL, 70 | `avg_position` float NOT NULL, 71 | `avg_position_click` float NULL, 72 | PRIMARY KEY (`id`) 73 | ) ENGINE=MyISAM DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci AUTO_INCREMENT=0 ; 74 | 75 | -- -------------------------------------------------------- 76 | 77 | -- 78 | -- Table structure for table `settings` 79 | -- 80 | 81 | CREATE TABLE IF NOT EXISTS `settings` ( 82 | `id` int(11) NOT NULL AUTO_INCREMENT, 83 | `type` varchar(256) NOT NULL, 84 | `value` varchar(256) NOT NULL, 85 | `data` varchar(500) NOT NULL, 86 | PRIMARY KEY (`id`) 87 | ) ENGINE=InnoDB DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci AUTO_INCREMENT=0 ; 88 | 89 | INSERT INTO `settings` 90 | (type, value, data) 91 | VALUES 92 | ('settings', 'google_search_console_dimensions_query', 'On'), 93 | ('settings', 'google_search_console_dimensions_page', 'Off'), 94 | ('settings', 'google_search_console_dimensions_device', 'On'), 95 | ('settings', 'google_search_console_dimensions_country', 'On') ; 96 | 97 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 98 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; 99 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; 100 | -------------------------------------------------------------------------------- /organic-search-analytics/data-capture.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

Organic Search Analytics | Capture Data

7 | 8 |
9 |
Import all non-imported data: 10 | Run 11 | 12 |
13 |
14 |
15 | 16 |

Google Search Console

17 |
18 |

Search Analytics

19 |
    20 |
  • Google updates API data every day.
  • 21 |
  • Google data is imported to the database on a daily basis.
  • 22 |
  • Any dates with data not in the database are listed below and ready for import.
  • 23 |
  • Google offers data for approximelty days
  • 24 |
  • The most recent data is days from today.
  • 25 |
26 | 27 |

Domains:

28 | getSettings("sites_google", "1"); 30 | $catId = "googleSearchAnalytics"; 31 | ?> 32 |
33 | $values ) { 36 | echo '
    '; 37 | echo '
  • '.$domain.'
  • '; 38 | 39 | $googleSearchAnalyticsDates = $dataCapture->checkNeededDataGoogleSearchAnalytics($domain); 40 | 41 | if( count( $googleSearchAnalyticsDates['datesWithNoData'] ) > 0 ) { 42 | 43 | echo '
      '; 44 | foreach( $googleSearchAnalyticsDates['datesWithNoData'] as $date ) { 45 | echo '
    • Import data for: ' . $date . ' >
    • '."\n"; 46 | } 47 | echo "
    "; 48 | } else { 49 | echo "

    The database is up to date.

    "; 50 | } 51 | echo '
'; 52 | } 53 | } else { 54 | echo ''; 55 | } 56 | ?> 57 |
58 |
59 | 60 | 61 |

Bing Webmaster Tools

62 |
63 |

Search Keywords

64 |
    65 |
  • Bing adds new data to their API feed every Saturday.
  • 66 |
  • Bing data is recorded on a weekly basis.
  • 67 |
  • Any dates with data not recorded in the database will be added when the import is run.
  • 68 |
69 | 70 |

Domains:

71 | getSettings("sites_bing", "1"); 73 | $catId = "bingSearchKeywords"; 74 | ?> 75 |
76 | $values ) { 79 | echo '
    '; 80 | echo '
  • '.$domain.'
  • '; 81 | $date = date( "Y-m-d", $now ); 82 | 83 | echo '
      '; 84 | echo '
    • Import data >
    • '."\n"; 85 | echo '
    '; 86 | echo '
'; 87 | } 88 | } else { 89 | echo ''; 90 | } 91 | ?> 92 |
93 |
94 | 95 | -------------------------------------------------------------------------------- /organic-search-analytics/data-capture-run.php: -------------------------------------------------------------------------------- 1 | 0 ) { 7 | $params = $_GET; 8 | } elseif( isset( $argv ) && is_array( $argv ) && count( $argv ) >= 4 ) { 9 | $params = array(); 10 | $params['type'] = $argv[1]; 11 | $params['domain'] = $argv[2]; 12 | $params['date'] = $argv[3]; 13 | if( isset( $argv[4] ) ) { 14 | $params['mode'] = $argv[4]; 15 | } 16 | if( isset( $argv[5] ) ) { 17 | $params['row_limit'] = $argv[5]; 18 | } 19 | if( isset( $argv[6] ) ) { 20 | $params['dimensions'] = $argv[6]; 21 | } 22 | if( isset( $argv[7] ) ) { 23 | $params['filters'] = $argv[7]; 24 | } 25 | if( isset( $argv[8] ) ) { 26 | $params['aggregation_type'] = $argv[8]; 27 | } 28 | if( isset( $argv[8] ) ) { 29 | $params['search_type'] = $argv[8]; 30 | } 31 | } 32 | 33 | 34 | if( isset($params) && isset($params['type']) && isset($params['domain']) && isset($params['date']) && preg_match("/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/",$params['date']) ) { 35 | /* Set the max allowed execution time for the page to allow for longer procesing times. */ 36 | ini_set('max_execution_time', 600); //300 seconds = 5 minutes 37 | 38 | /* Set overrides from URL paramters */ 39 | $overrideSettings = array(); 40 | if( isset( $params['mode'] ) ) { $overrideSettings['mode'] = $params['mode']; } 41 | if( isset( $params['row_limit'] ) ) { $overrideSettings['row_limit'] = $params['row_limit']; } 42 | if( isset( $params['dimensions'] ) ) { $overrideSettings['dimensions'] = explode( ',', $params['dimensions'] ); } 43 | if( isset( $params['search_type'] ) ) { $overrideSettings['search_type'] = explode( ',', $params['search_type'] ); } 44 | if( isset( $params['filters'] ) ) { 45 | $filters = explode( '|', $params['filters'] ); 46 | foreach( $filters as $filter ) { 47 | $filterArgs = explode( ',', $filter ); 48 | if( count( $filterArgs > 1 ) ) { 49 | $filterParmas = array( 'dimension' => $filterArgs[0], 'expression' => $filterArgs[1] ); 50 | if( isset( $filterArgs[2] ) ) { $filterParmas['operator'] = $filterArgs[2]; } else { $filterParmas['operator'] = 'contains'; } 51 | $overrideSettings['filters'][] = $filterParmas; 52 | } 53 | } 54 | } 55 | if( isset( $params['aggregation_type'] ) ) { $overrideSettings['aggregation_type'] = $params['aggregation_type']; } 56 | 57 | switch( $params['type'] ) { 58 | case 'googleSearchAnalytics': 59 | $recordsImported = $dataCapture->downloadGoogleSearchAnalytics( $params['domain'],$params['date'], $overrideSettings ); 60 | if( !isset( $params['mode'] ) || $params['mode'] != 'return' ) { 61 | switch( $recordsImported ) { 62 | case -1: 63 | echo "There was an error in authorizing your API connection."; 64 | break; 65 | default: 66 | echo number_format( $recordsImported ) . " records succesfully imported to the database for " . $params['domain'] . " for date: " . $params['date'] . "."; 67 | } 68 | } 69 | break; 70 | case 'bingSearchKeywords': 71 | $recordsImported = $dataCapture->downloadBingSearchKeywords($params['domain'],$params['date'], $overrideSettings); 72 | if( !isset( $params['mode'] ) || $params['mode'] != 'return' ) { 73 | switch( $recordsImported ) { 74 | case -1: 75 | echo "There was an error in authorizing your API connection."; 76 | break; 77 | default: 78 | echo number_format( $recordsImported ) . " records succesfully imported to the database for " . $params['domain'] . "."; 79 | } 80 | } 81 | break; 82 | } 83 | 84 | } else { 85 | echo "

ERROR: Invalid request. Domain: " . $params['domain'] . ", Date: " . $params['date'] . "

"; 86 | } 87 | ?> -------------------------------------------------------------------------------- /organic-search-analytics/inc/html/_head.php: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | <?php echo $titleTag; ?> 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 | 54 |
55 | 63 |
64 |
65 |
66 | 67 |
-------------------------------------------------------------------------------- /organic-search-analytics/apis/Google/Service/GroupsMigration.php: -------------------------------------------------------------------------------- 1 | 22 | * Groups Migration Api.

23 | * 24 | *

25 | * For more information about this service, see the API 26 | * Documentation 27 | *

28 | * 29 | * @author Google, Inc. 30 | */ 31 | class Google_Service_GroupsMigration extends Google_Service 32 | { 33 | /** Manage messages in groups on your domain. */ 34 | const APPS_GROUPS_MIGRATION = 35 | "https://www.googleapis.com/auth/apps.groups.migration"; 36 | 37 | public $archive; 38 | 39 | 40 | /** 41 | * Constructs the internal representation of the GroupsMigration service. 42 | * 43 | * @param Google_Client $client 44 | */ 45 | public function __construct(Google_Client $client) 46 | { 47 | parent::__construct($client); 48 | $this->rootUrl = 'https://www.googleapis.com/'; 49 | $this->servicePath = 'groups/v1/groups/'; 50 | $this->version = 'v1'; 51 | $this->serviceName = 'groupsmigration'; 52 | 53 | $this->archive = new Google_Service_GroupsMigration_Archive_Resource( 54 | $this, 55 | $this->serviceName, 56 | 'archive', 57 | array( 58 | 'methods' => array( 59 | 'insert' => array( 60 | 'path' => '{groupId}/archive', 61 | 'httpMethod' => 'POST', 62 | 'parameters' => array( 63 | 'groupId' => array( 64 | 'location' => 'path', 65 | 'type' => 'string', 66 | 'required' => true, 67 | ), 68 | ), 69 | ), 70 | ) 71 | ) 72 | ); 73 | } 74 | } 75 | 76 | 77 | /** 78 | * The "archive" collection of methods. 79 | * Typical usage is: 80 | * 81 | * $groupsmigrationService = new Google_Service_GroupsMigration(...); 82 | * $archive = $groupsmigrationService->archive; 83 | * 84 | */ 85 | class Google_Service_GroupsMigration_Archive_Resource extends Google_Service_Resource 86 | { 87 | 88 | /** 89 | * Inserts a new mail into the archive of the Google group. (archive.insert) 90 | * 91 | * @param string $groupId The group ID 92 | * @param array $optParams Optional parameters. 93 | * @return Google_Service_GroupsMigration_Groups 94 | */ 95 | public function insert($groupId, $optParams = array()) 96 | { 97 | $params = array('groupId' => $groupId); 98 | $params = array_merge($params, $optParams); 99 | return $this->call('insert', array($params), "Google_Service_GroupsMigration_Groups"); 100 | } 101 | } 102 | 103 | 104 | 105 | 106 | class Google_Service_GroupsMigration_Groups extends Google_Model 107 | { 108 | protected $internal_gapi_mappings = array( 109 | ); 110 | public $kind; 111 | public $responseCode; 112 | 113 | 114 | public function setKind($kind) 115 | { 116 | $this->kind = $kind; 117 | } 118 | public function getKind() 119 | { 120 | return $this->kind; 121 | } 122 | public function setResponseCode($responseCode) 123 | { 124 | $this->responseCode = $responseCode; 125 | } 126 | public function getResponseCode() 127 | { 128 | return $this->responseCode; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /organic-search-analytics/js/lib/jqplot/plugins/jqplot.ohlcRenderer.min.js: -------------------------------------------------------------------------------- 1 | /* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com 2 | jsDate | (c) 2010-2013 Chris Leonello 3 | */(function(a){a.jqplot.OHLCRenderer=function(){a.jqplot.LineRenderer.call(this);this.candleStick=false;this.tickLength="auto";this.bodyWidth="auto";this.openColor=null;this.closeColor=null;this.wickColor=null;this.fillUpBody=false;this.fillDownBody=true;this.upBodyColor=null;this.downBodyColor=null;this.hlc=false;this.lineWidth=1.5;this._tickLength;this._bodyWidth};a.jqplot.OHLCRenderer.prototype=new a.jqplot.LineRenderer();a.jqplot.OHLCRenderer.prototype.constructor=a.jqplot.OHLCRenderer;a.jqplot.OHLCRenderer.prototype.init=function(e){e=e||{};this.lineWidth=e.lineWidth||1.5;a.jqplot.LineRenderer.prototype.init.call(this,e);this._type="ohlc";var b=this._yaxis._dataBounds;var f=this._plotData;if(f[0].length<5){this.renderer.hlc=true;for(var c=0;cb.max||b.max==null){b.max=f[c][1]}}}else{for(var c=0;cb.max||b.max==null){b.max=f[c][2]}}}};a.jqplot.OHLCRenderer.prototype.draw=function(A,N,j){var J=this.data;var v=this._xaxis.min;var z=this._xaxis.max;var l=0;var K=J.length;var p=this._xaxis.series_u2p;var G=this._yaxis.series_u2p;var D,E,f,M,F,n,O,C;var y;var u=this.renderer;var s=(j!=undefined)?j:{};var k=(s.shadow!=undefined)?s.shadow:this.shadow;var B=(s.fill!=undefined)?s.fill:this.fill;var c=(s.fillAndStroke!=undefined)?s.fillAndStroke:this.fillAndStroke;u.bodyWidth=(s.bodyWidth!=undefined)?s.bodyWidth:u.bodyWidth;u.tickLength=(s.tickLength!=undefined)?s.tickLength:u.tickLength;A.save();if(this.show){var m,q,g,Q,t;for(var D=0;Dq){if(u.wickColor){y.color=u.wickColor}else{if(u.downBodyColor){y.color=u.downBodyColor}}f=a.extend(true,{},s,y);u.shapeRenderer.draw(A,[[m,g],[m,q]],f);u.shapeRenderer.draw(A,[[m,t],[m,Q]],f);y={};M=q;F=t-q;if(u.fillDownBody){y.fillRect=true}else{y.strokeRect=true;n=n-this.lineWidth;O=m-n/2}if(u.downBodyColor){y.color=u.downBodyColor;y.fillStyle=u.downBodyColor}C=[O,M,n,F]}else{if(u.wickColor){y.color=u.wickColor}f=a.extend(true,{},s,y);u.shapeRenderer.draw(A,[[m,g],[m,Q]],f);y={};y.fillRect=false;y.strokeRect=false;O=[m-n/2,q];M=[m+n/2,t];n=null;F=null;C=[O,M]}}f=a.extend(true,{},s,y);u.shapeRenderer.draw(A,C,f)}else{E=s.color;if(u.openColor){s.color=u.openColor}if(!u.hlc){u.shapeRenderer.draw(A,[[m-u._tickLength,q],[m,q]],s)}s.color=E;if(u.wickColor){s.color=u.wickColor}u.shapeRenderer.draw(A,[[m,g],[m,Q]],s);s.color=E;if(u.closeColor){s.color=u.closeColor}u.shapeRenderer.draw(A,[[m,t],[m+u._tickLength,t]],s);s.color=E}}}A.restore()};a.jqplot.OHLCRenderer.prototype.drawShadow=function(b,d,c){};a.jqplot.OHLCRenderer.checkOptions=function(d,c,b){if(!b.highlighter){b.highlighter={showMarker:false,tooltipAxes:"y",yvalues:4,formatString:'
date:%s
open:%s
hi:%s
low:%s
close:%s
'}}}})(jQuery); -------------------------------------------------------------------------------- /organic-search-analytics/js/lib/jqplot/plugins/jqplot.dragable.min.js: -------------------------------------------------------------------------------- 1 | /* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com 2 | jsDate | (c) 2010-2013 Chris Leonello 3 | */(function(d){d.jqplot.Dragable=function(g){this.markerRenderer=new d.jqplot.MarkerRenderer({shadow:false});this.shapeRenderer=new d.jqplot.ShapeRenderer();this.isDragging=false;this.isOver=false;this._ctx;this._elem;this._point;this._gridData;this.color;this.constrainTo="none";d.extend(true,this,g)};function b(){d.jqplot.GenericCanvas.call(this);this.isDragging=false;this.isOver=false;this._neighbor;this._cursors=[]}b.prototype=new d.jqplot.GenericCanvas();b.prototype.constructor=b;d.jqplot.Dragable.parseOptions=function(i,h){var g=h||{};this.plugins.dragable=new d.jqplot.Dragable(g.dragable);this.isDragable=d.jqplot.config.enablePlugins};d.jqplot.Dragable.postPlotDraw=function(){if(this.plugins.dragable&&this.plugins.dragable.highlightCanvas){this.plugins.dragable.highlightCanvas.resetCanvas();this.plugins.dragable.highlightCanvas=null}this.plugins.dragable={previousCursor:"auto",isOver:false};this.plugins.dragable.dragCanvas=new b();this.eventCanvas._elem.before(this.plugins.dragable.dragCanvas.createElement(this._gridPadding,"jqplot-dragable-canvas",this._plotDimensions,this));var g=this.plugins.dragable.dragCanvas.setContext()};d.jqplot.preParseSeriesOptionsHooks.push(d.jqplot.Dragable.parseOptions);d.jqplot.postDrawHooks.push(d.jqplot.Dragable.postPlotDraw);d.jqplot.eventListenerHooks.push(["jqplotMouseMove",e]);d.jqplot.eventListenerHooks.push(["jqplotMouseDown",c]);d.jqplot.eventListenerHooks.push(["jqplotMouseUp",a]);function f(n,p){var q=n.series[p.seriesIndex];var m=q.plugins.dragable;var h=q.markerRenderer;var i=m.markerRenderer;i.style=h.style;i.lineWidth=h.lineWidth+2.5;i.size=h.size+5;if(!m.color){var l=d.jqplot.getColorComponents(h.color);var o=[l[0],l[1],l[2]];var k=(l[3]>=0.6)?l[3]*0.6:l[3]*(2-l[3]);m.color="rgba("+o[0]+","+o[1]+","+o[2]+","+k+")"}i.color=m.color;i.init();var g=(p.pointIndex>0)?p.pointIndex-1:0;var j=p.pointIndex+2;m._gridData=q.gridData.slice(g,j)}function e(o,l,h,t,m){if(m.plugins.dragable.dragCanvas.isDragging){var u=m.plugins.dragable.dragCanvas;var i=u._neighbor;var w=m.series[i.seriesIndex];var k=w.plugins.dragable;var r=w.gridData;var p=(k.constrainTo=="y")?i.gridData[0]:l.x;var n=(k.constrainTo=="x")?i.gridData[1]:l.y;var g=w._xaxis.series_p2u(p);var q=w._yaxis.series_p2u(n);var v=u._ctx;v.clearRect(0,0,v.canvas.width,v.canvas.height);if(i.pointIndex>0){k._gridData[1]=[p,n]}else{k._gridData[0]=[p,n]}m.series[i.seriesIndex].draw(u._ctx,{gridData:k._gridData,shadow:false,preventJqPlotSeriesDrawTrigger:true,color:k.color,markerOptions:{color:k.color,shadow:false},trendline:{show:false}});m.target.trigger("jqplotSeriesPointChange",[i.seriesIndex,i.pointIndex,[g,q],[p,n]])}else{if(t!=null){var j=m.series[t.seriesIndex];if(j.isDragable){var u=m.plugins.dragable.dragCanvas;if(!u.isOver){u._cursors.push(o.target.style.cursor);o.target.style.cursor="pointer"}u.isOver=true}}else{if(t==null){var u=m.plugins.dragable.dragCanvas;if(u.isOver){o.target.style.cursor=u._cursors.pop();u.isOver=false}}}}}function c(k,i,g,l,j){var m=j.plugins.dragable.dragCanvas;m._cursors.push(k.target.style.cursor);if(l!=null){var o=j.series[l.seriesIndex];var h=o.plugins.dragable;if(o.isDragable&&!m.isDragging){m._neighbor=l;m.isDragging=true;f(j,l);h.markerRenderer.draw(o.gridData[l.pointIndex][0],o.gridData[l.pointIndex][1],m._ctx);k.target.style.cursor="move";j.target.trigger("jqplotDragStart",[l.seriesIndex,l.pointIndex,i,g])}}else{var n=m._ctx;n.clearRect(0,0,n.canvas.width,n.canvas.height);m.isDragging=false}}function a(m,j,g,o,k){if(k.plugins.dragable.dragCanvas.isDragging){var p=k.plugins.dragable.dragCanvas;var q=p._ctx;q.clearRect(0,0,q.canvas.width,q.canvas.height);p.isDragging=false;var h=p._neighbor;var r=k.series[h.seriesIndex];var i=r.plugins.dragable;var n=(i.constrainTo=="y")?h.data[0]:g[r.xaxis];var l=(i.constrainTo=="x")?h.data[1]:g[r.yaxis];r.data[h.pointIndex][0]=n;r.data[h.pointIndex][1]=l;k.drawSeries({preventJqPlotSeriesDrawTrigger:true},h.seriesIndex);p._neighbor=null;m.target.style.cursor=p._cursors.pop();k.target.trigger("jqplotDragStop",[j,g])}}})(jQuery); -------------------------------------------------------------------------------- /organic-search-analytics/apis/Google/Auth/AppIdentity.php: -------------------------------------------------------------------------------- 1 | client = $client; 42 | } 43 | 44 | /** 45 | * Retrieve an access token for the scopes supplied. 46 | */ 47 | public function authenticateForScope($scopes) 48 | { 49 | if ($this->token && $this->tokenScopes == $scopes) { 50 | return $this->token; 51 | } 52 | 53 | $cacheKey = self::CACHE_PREFIX; 54 | if (is_string($scopes)) { 55 | $cacheKey .= $scopes; 56 | } else if (is_array($scopes)) { 57 | $cacheKey .= implode(":", $scopes); 58 | } 59 | 60 | $this->token = $this->client->getCache()->get($cacheKey); 61 | if (!$this->token) { 62 | $this->retrieveToken($scopes, $cacheKey); 63 | } else if ($this->token['expiration_time'] < time()) { 64 | $this->client->getCache()->delete($cacheKey); 65 | $this->retrieveToken($scopes, $cacheKey); 66 | } 67 | 68 | $this->tokenScopes = $scopes; 69 | return $this->token; 70 | } 71 | 72 | /** 73 | * Retrieve a new access token and store it in cache 74 | * @param mixed $scopes 75 | * @param string $cacheKey 76 | */ 77 | private function retrieveToken($scopes, $cacheKey) 78 | { 79 | $this->token = AppIdentityService::getAccessToken($scopes); 80 | if ($this->token) { 81 | $this->client->getCache()->set( 82 | $cacheKey, 83 | $this->token 84 | ); 85 | } 86 | } 87 | 88 | /** 89 | * Perform an authenticated / signed apiHttpRequest. 90 | * This function takes the apiHttpRequest, calls apiAuth->sign on it 91 | * (which can modify the request in what ever way fits the auth mechanism) 92 | * and then calls apiCurlIO::makeRequest on the signed request 93 | * 94 | * @param Google_Http_Request $request 95 | * @return Google_Http_Request The resulting HTTP response including the 96 | * responseHttpCode, responseHeaders and responseBody. 97 | */ 98 | public function authenticatedRequest(Google_Http_Request $request) 99 | { 100 | $request = $this->sign($request); 101 | return $this->client->getIo()->makeRequest($request); 102 | } 103 | 104 | public function sign(Google_Http_Request $request) 105 | { 106 | if (!$this->token) { 107 | // No token, so nothing to do. 108 | return $request; 109 | } 110 | 111 | $this->client->getLogger()->debug('App Identity authentication'); 112 | 113 | // Add the OAuth2 header to the request 114 | $request->setRequestHeaders( 115 | array('Authorization' => 'Bearer ' . $this->token['access_token']) 116 | ); 117 | 118 | return $request; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /organic-search-analytics/apis/Google/Logger/File.php: -------------------------------------------------------------------------------- 1 | getClassConfig('Google_Logger_File', 'file'); 59 | if (!is_string($file) && !is_resource($file)) { 60 | throw new Google_Logger_Exception( 61 | 'File logger requires a filename or a valid file pointer' 62 | ); 63 | } 64 | 65 | $mode = $client->getClassConfig('Google_Logger_File', 'mode'); 66 | if (!$mode) { 67 | $this->mode = $mode; 68 | } 69 | 70 | $this->lock = (bool) $client->getClassConfig('Google_Logger_File', 'lock'); 71 | $this->file = $file; 72 | } 73 | 74 | /** 75 | * {@inheritdoc} 76 | */ 77 | protected function write($message) 78 | { 79 | if (is_string($this->file)) { 80 | $this->open(); 81 | } elseif (!is_resource($this->file)) { 82 | throw new Google_Logger_Exception('File pointer is no longer available'); 83 | } 84 | 85 | if ($this->lock) { 86 | flock($this->file, LOCK_EX); 87 | } 88 | 89 | fwrite($this->file, (string) $message); 90 | 91 | if ($this->lock) { 92 | flock($this->file, LOCK_UN); 93 | } 94 | } 95 | 96 | /** 97 | * Opens the log for writing. 98 | * 99 | * @return resource 100 | */ 101 | private function open() 102 | { 103 | // Used for trapping `fopen()` errors. 104 | $this->trappedErrorNumber = null; 105 | $this->trappedErrorString = null; 106 | 107 | $old = set_error_handler(array($this, 'trapError')); 108 | 109 | $needsChmod = !file_exists($this->file); 110 | $fh = fopen($this->file, 'a'); 111 | 112 | restore_error_handler(); 113 | 114 | // Handles trapped `fopen()` errors. 115 | if ($this->trappedErrorNumber) { 116 | throw new Google_Logger_Exception( 117 | sprintf( 118 | "Logger Error: '%s'", 119 | $this->trappedErrorString 120 | ), 121 | $this->trappedErrorNumber 122 | ); 123 | } 124 | 125 | if ($needsChmod) { 126 | @chmod($this->file, $this->mode & ~umask()); 127 | } 128 | 129 | return $this->file = $fh; 130 | } 131 | 132 | /** 133 | * Closes the log stream resource. 134 | */ 135 | private function close() 136 | { 137 | if (is_resource($this->file)) { 138 | fclose($this->file); 139 | } 140 | } 141 | 142 | /** 143 | * Traps `fopen()` errors. 144 | * 145 | * @param integer $errno The error number 146 | * @param string $errstr The error string 147 | */ 148 | private function trapError($errno, $errstr) 149 | { 150 | $this->trappedErrorNumber = $errno; 151 | $this->trappedErrorString = $errstr; 152 | } 153 | 154 | public function __destruct() 155 | { 156 | $this->close(); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /organic-search-analytics/js/lib/jqplot/plugins/jqplot.pointLabels.min.js: -------------------------------------------------------------------------------- 1 | /* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com 2 | jsDate | (c) 2010-2013 Chris Leonello 3 | */(function(c){c.jqplot.PointLabels=function(e){this.show=c.jqplot.config.enablePlugins;this.location="n";this.labelsFromSeries=false;this.seriesLabelIndex=null;this.labels=[];this._labels=[];this.stackedValue=false;this.ypadding=6;this.xpadding=6;this.escapeHTML=true;this.edgeTolerance=-5;this.formatter=c.jqplot.DefaultTickFormatter;this.formatString="";this.hideZeros=false;this._elems=[];c.extend(true,this,e)};var a=["nw","n","ne","e","se","s","sw","w"];var d={nw:0,n:1,ne:2,e:3,se:4,s:5,sw:6,w:7};var b=["se","s","sw","w","nw","n","ne","e"];c.jqplot.PointLabels.init=function(j,h,f,g,i){var e=c.extend(true,{},f,g);e.pointLabels=e.pointLabels||{};if(this.renderer.constructor===c.jqplot.BarRenderer&&this.barDirection==="horizontal"&&!e.pointLabels.location){e.pointLabels.location="e"}this.plugins.pointLabels=new c.jqplot.PointLabels(e.pointLabels);this.plugins.pointLabels.setLabels.call(this)};c.jqplot.PointLabels.prototype.setLabels=function(){var f=this.plugins.pointLabels;var h;if(f.seriesLabelIndex!=null){h=f.seriesLabelIndex}else{if(this.renderer.constructor===c.jqplot.BarRenderer&&this.barDirection==="horizontal"){h=(this._plotData[0].length<3)?0:this._plotData[0].length-1}else{h=(this._plotData.length===0)?0:this._plotData[0].length-1}}f._labels=[];if(f.labels.length===0||f.labelsFromSeries){if(f.stackedValue){if(this._plotData.length&&this._plotData[0].length){for(var e=0;eB||s+C>m){z.remove()}z=null;f=null}}};c.jqplot.postSeriesInitHooks.push(c.jqplot.PointLabels.init);c.jqplot.postDrawSeriesHooks.push(c.jqplot.PointLabels.draw)})(jQuery); -------------------------------------------------------------------------------- /organic-search-analytics/apis/Google/Auth/AssertionCredentials.php: -------------------------------------------------------------------------------- 1 | serviceAccountName = $serviceAccountName; 63 | $this->scopes = is_string($scopes) ? $scopes : implode(' ', $scopes); 64 | $this->privateKey = $privateKey; 65 | $this->privateKeyPassword = $privateKeyPassword; 66 | $this->assertionType = $assertionType; 67 | $this->sub = $sub; 68 | $this->prn = $sub; 69 | $this->useCache = $useCache; 70 | } 71 | 72 | /** 73 | * Generate a unique key to represent this credential. 74 | * @return string 75 | */ 76 | public function getCacheKey() 77 | { 78 | if (!$this->useCache) { 79 | return false; 80 | } 81 | $h = $this->sub; 82 | $h .= $this->assertionType; 83 | $h .= $this->privateKey; 84 | $h .= $this->scopes; 85 | $h .= $this->serviceAccountName; 86 | return md5($h); 87 | } 88 | 89 | public function generateAssertion() 90 | { 91 | $now = time(); 92 | 93 | $jwtParams = array( 94 | 'aud' => Google_Auth_OAuth2::OAUTH2_TOKEN_URI, 95 | 'scope' => $this->scopes, 96 | 'iat' => $now, 97 | 'exp' => $now + self::MAX_TOKEN_LIFETIME_SECS, 98 | 'iss' => $this->serviceAccountName, 99 | ); 100 | 101 | if ($this->sub !== false) { 102 | $jwtParams['sub'] = $this->sub; 103 | } else if ($this->prn !== false) { 104 | $jwtParams['prn'] = $this->prn; 105 | } 106 | 107 | return $this->makeSignedJwt($jwtParams); 108 | } 109 | 110 | /** 111 | * Creates a signed JWT. 112 | * @param array $payload 113 | * @return string The signed JWT. 114 | */ 115 | private function makeSignedJwt($payload) 116 | { 117 | $header = array('typ' => 'JWT', 'alg' => 'RS256'); 118 | 119 | $payload = json_encode($payload); 120 | // Handle some overzealous escaping in PHP json that seemed to cause some errors 121 | // with claimsets. 122 | $payload = str_replace('\/', '/', $payload); 123 | 124 | $segments = array( 125 | Google_Utils::urlSafeB64Encode(json_encode($header)), 126 | Google_Utils::urlSafeB64Encode($payload) 127 | ); 128 | 129 | $signingInput = implode('.', $segments); 130 | $signer = new Google_Signer_P12($this->privateKey, $this->privateKeyPassword); 131 | $signature = $signer->sign($signingInput); 132 | $segments[] = Google_Utils::urlSafeB64Encode($signature); 133 | 134 | return implode(".", $segments); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /organic-search-analytics/apis/Google/Utils.php: -------------------------------------------------------------------------------- 1 | = 0x20) && ($ordinalValue <= 0x7F)): 68 | // characters U-00000000 - U-0000007F (same as ASCII) 69 | $ret ++; 70 | break; 71 | case (($ordinalValue & 0xE0) == 0xC0): 72 | // characters U-00000080 - U-000007FF, mask 110XXXXX 73 | // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 74 | $ret += 2; 75 | break; 76 | case (($ordinalValue & 0xF0) == 0xE0): 77 | // characters U-00000800 - U-0000FFFF, mask 1110XXXX 78 | // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 79 | $ret += 3; 80 | break; 81 | case (($ordinalValue & 0xF8) == 0xF0): 82 | // characters U-00010000 - U-001FFFFF, mask 11110XXX 83 | // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 84 | $ret += 4; 85 | break; 86 | case (($ordinalValue & 0xFC) == 0xF8): 87 | // characters U-00200000 - U-03FFFFFF, mask 111110XX 88 | // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 89 | $ret += 5; 90 | break; 91 | case (($ordinalValue & 0xFE) == 0xFC): 92 | // characters U-04000000 - U-7FFFFFFF, mask 1111110X 93 | // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 94 | $ret += 6; 95 | break; 96 | default: 97 | $ret ++; 98 | } 99 | } 100 | return $ret; 101 | } 102 | 103 | /** 104 | * Normalize all keys in an array to lower-case. 105 | * @param array $arr 106 | * @return array Normalized array. 107 | */ 108 | public static function normalize($arr) 109 | { 110 | if (!is_array($arr)) { 111 | return array(); 112 | } 113 | 114 | $normalized = array(); 115 | foreach ($arr as $key => $val) { 116 | $normalized[strtolower($key)] = $val; 117 | } 118 | return $normalized; 119 | } 120 | 121 | /** 122 | * Convert a string to camelCase 123 | * @param string $value 124 | * @return string 125 | */ 126 | public static function camelCase($value) 127 | { 128 | $value = ucwords(str_replace(array('-', '_'), ' ', $value)); 129 | $value = str_replace(' ', '', $value); 130 | $value[0] = strtolower($value[0]); 131 | return $value; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /organic-search-analytics/js/lib/jqplot/plugins/jqplot.enhancedLegendRenderer.min.js: -------------------------------------------------------------------------------- 1 | /* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com 2 | jsDate | (c) 2010-2013 Chris Leonello 3 | */(function(c){c.jqplot.EnhancedLegendRenderer=function(){c.jqplot.TableLegendRenderer.call(this)};c.jqplot.EnhancedLegendRenderer.prototype=new c.jqplot.TableLegendRenderer();c.jqplot.EnhancedLegendRenderer.prototype.constructor=c.jqplot.EnhancedLegendRenderer;c.jqplot.EnhancedLegendRenderer.prototype.init=function(d){this.numberRows=null;this.numberColumns=null;this.seriesToggle="normal";this.seriesToggleReplot=false;this.disableIEFading=true;c.extend(true,this,d);if(this.seriesToggle){c.jqplot.postDrawHooks.push(b)}};c.jqplot.EnhancedLegendRenderer.prototype.draw=function(m,y){var f=this;if(this.show){var r=this._series;var u;var w="position:absolute;";w+=(this.background)?"background:"+this.background+";":"";w+=(this.border)?"border:"+this.border+";":"";w+=(this.fontSize)?"font-size:"+this.fontSize+";":"";w+=(this.fontFamily)?"font-family:"+this.fontFamily+";":"";w+=(this.textColor)?"color:"+this.textColor+";":"";w+=(this.marginTop!=null)?"margin-top:"+this.marginTop+";":"";w+=(this.marginBottom!=null)?"margin-bottom:"+this.marginBottom+";":"";w+=(this.marginLeft!=null)?"margin-left:"+this.marginLeft+";":"";w+=(this.marginRight!=null)?"margin-right:"+this.marginRight+";":"";this._elem=c('
');if(this.seriesToggle){this._elem.css("z-index","3")}var C=false,q=false,d,o;if(this.numberRows){d=this.numberRows;if(!this.numberColumns){o=Math.ceil(r.length/d)}else{o=this.numberColumns}}else{if(this.numberColumns){o=this.numberColumns;d=Math.ceil(r.length/this.numberColumns)}else{d=r.length;o=1}}var B,z,e,l,k,n,p,t,h,g;var v=0;for(B=r.length-1;B>=0;B--){if(o==1&&r[B]._stack||r[B].renderer.constructor==c.jqplot.BezierCurveRenderer){q=true}}for(B=0;B0){C=true}else{C=false}}else{if(B==d-1){C=false}else{C=true}}p=(C)?this.rowSpacing:"0";l=c(document.createElement("td"));l.addClass("jqplot-table-legend jqplot-table-legend-swatch");l.css({textAlign:"center",paddingTop:p});h=c(document.createElement("div"));h.addClass("jqplot-table-legend-swatch-outline");g=c(document.createElement("div"));g.addClass("jqplot-table-legend-swatch");g.css({backgroundColor:x,borderColor:x});l.append(h.append(g));k=c(document.createElement("td"));k.addClass("jqplot-table-legend jqplot-table-legend-label");k.css("paddingTop",p);if(this.escapeHtml){k.text(n)}else{k.html(n)}if(q){if(this.showLabels){k.prependTo(e)}if(this.showSwatches){l.prependTo(e)}}else{if(this.showSwatches){l.appendTo(e)}if(this.showLabels){k.appendTo(e)}}if(this.seriesToggle){var A;if(typeof(this.seriesToggle)==="string"||typeof(this.seriesToggle)==="number"){if(!c.jqplot.use_excanvas||!this.disableIEFading){A=this.seriesToggle}}if(this.showSwatches){l.bind("click",{series:u,speed:A,plot:y,replot:this.seriesToggleReplot},a);l.addClass("jqplot-seriesToggle")}if(this.showLabels){k.bind("click",{series:u,speed:A,plot:y,replot:this.seriesToggleReplot},a);k.addClass("jqplot-seriesToggle")}if(!u.show&&u.showLabel){l.addClass("jqplot-series-hidden");k.addClass("jqplot-series-hidden")}}C=true}}v++}l=k=h=g=null}}return this._elem};var a=function(j){var i=j.data,m=i.series,k=i.replot,h=i.plot,f=i.speed,l=m.index,g=false;if(m.canvas._elem.is(":hidden")||!m.show){g=true}var e=function(){if(k){var n={};if(c.isPlainObject(k)){c.extend(true,n,k)}h.replot(n);if(g&&f){var d=h.series[l];if(d.shadowCanvas._elem){d.shadowCanvas._elem.hide().fadeIn(f)}d.canvas._elem.hide().fadeIn(f);d.canvas._elem.nextAll(".jqplot-point-label.jqplot-series-"+d.index).hide().fadeIn(f)}}else{var d=h.series[l];if(d.canvas._elem.is(":hidden")||!d.show){if(typeof h.options.legend.showSwatches==="undefined"||h.options.legend.showSwatches===true){h.legend._elem.find("td").eq(l*2).addClass("jqplot-series-hidden")}if(typeof h.options.legend.showLabels==="undefined"||h.options.legend.showLabels===true){h.legend._elem.find("td").eq((l*2)+1).addClass("jqplot-series-hidden")}}else{if(typeof h.options.legend.showSwatches==="undefined"||h.options.legend.showSwatches===true){h.legend._elem.find("td").eq(l*2).removeClass("jqplot-series-hidden")}if(typeof h.options.legend.showLabels==="undefined"||h.options.legend.showLabels===true){h.legend._elem.find("td").eq((l*2)+1).removeClass("jqplot-series-hidden")}}}};m.toggleDisplay(j,e)};var b=function(){if(this.legend.renderer.constructor==c.jqplot.EnhancedLegendRenderer&&this.legend.seriesToggle){var d=this.legend._elem.detach();this.eventCanvas._elem.after(d)}}})(jQuery); -------------------------------------------------------------------------------- /organic-search-analytics/js/script.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sends an AJAX request for data to capture 3 | * 4 | * @param catId String Category ID for which data is being captured 5 | * @param script String File name/path to run 6 | * @param domain String Domain name to capture data for 7 | * @param date String Date to search. YYYY-MM-DD 8 | * @param callback String Function to run upon success 9 | * 10 | * @returns Object Database records. MySQL object 11 | */ 12 | function ajaxScript(catId, script, domain, date, callback) { 13 | jQuery.ajax({ 14 | type : "GET", 15 | url : script, 16 | data : { 17 | type : catId, 18 | domain : domain, 19 | date : date 20 | }, 21 | beforeSend: function( xhr ) { 22 | $("#"+catId+" li[domain='"+domain+"'][date='"+date+"']").append('Processing...'); 23 | }, 24 | success: function(data) { 25 | if( callback ) { 26 | window[callback](catId, domain, date, data); 27 | } 28 | }, 29 | error: function(xhr, status, errorThrown) { 30 | $("#"+catId+" li[domain='"+domain+"'][date='"+date+"']").append('Error, try again.'); 31 | } 32 | }); 33 | } 34 | 35 | 36 | /** 37 | * Import all pending data 38 | */ 39 | importAllStop(); 40 | function importAllRun() { 41 | window.importAllProcessing = true; 42 | 43 | $("#importAllButtons .buttonImportAllRun").hide(); 44 | $("#importAllButtons .buttonImportAllStop").show(); 45 | 46 | /* Trigger the first import button */ 47 | if( $(".buttonImport").length > 0 ) { 48 | $(".buttonImport").eq(0).trigger("click"); 49 | } else { 50 | importAllStop(); 51 | window.importAllProcessing = false; 52 | } 53 | } 54 | 55 | 56 | /** 57 | * Stop importing all data process 58 | */ 59 | function importAllStop() { 60 | window.importAllProcessing = false; 61 | $("#importAllButtons .buttonImportAllStop").hide(); 62 | $("#importAllButtons .buttonImportAllRun").show(); 63 | } 64 | 65 | 66 | /** 67 | * Callback after Google Search Analytics are imported 68 | * If import all is triggered, iniitiate the next import 69 | * 70 | * @param catId String Category ID for which data is being captured 71 | * @param domain String Domain name to capture data for 72 | * @param date String Date to search. YYYY-MM-DD 73 | * @param data String Message to display 74 | */ 75 | function postGoogleSearchAnalyticsAjax(catId, domain, date, data) { 76 | $("#"+catId+" li[domain='"+domain+"'][date='"+date+"']").empty().append(data); 77 | 78 | if( window.importAllProcessing == true ) { 79 | importAllRun(); 80 | } 81 | } 82 | 83 | 84 | /** 85 | * Callback after Bing Search Keywords are imported 86 | * If import all is triggered, iniitiate the next import 87 | * 88 | * @param catId String Category ID for which data is being captured 89 | * @param domain String Domain name to capture data for 90 | * @param date String Date to search. YYYY-MM-DD 91 | * @param data String Message to display 92 | */ 93 | function postBingSearchKeywordsAjax(catId, domain, date, data) { 94 | $("#"+catId+" li[domain='"+domain+"'][date='"+date+"']").empty().append(data); 95 | 96 | /* If import all is set, continue processing */ 97 | if( window.importAllProcessing == true ) { 98 | importAllRun(catId); 99 | } 100 | } 101 | 102 | 103 | /** 104 | * Toggle of showing save to quick links form on report-custom.php 105 | */ 106 | function showReportSave( elem ) { 107 | jQuery( elem ).parent().find(".ajaxFromContent").toggle(); 108 | } 109 | 110 | 111 | /** 112 | * Submit form via AJAX 113 | */ 114 | jQuery(document).ready(function(){ 115 | jQuery(".ajaxForm").on("submit", function(e){ 116 | e.preventDefault(); /* prevent form submission */ 117 | var form = jQuery(this); 118 | jQuery.ajax({ 119 | type : "POST", 120 | url : "ajax.php", 121 | data : { 122 | requestType: form.attr( "type" ), 123 | formData: form.serialize() 124 | }, 125 | beforeSend: function( xhr ) { 126 | form.find( "input[type=submit]" ).val( "Saving..." ); 127 | }, 128 | success: function( data ) { 129 | ajaxFormCallback( form, 'SUCCESS', 'successText' ); 130 | }, 131 | error: function( xhr, status, errorThrown ) { 132 | ajaxFormCallback( form, 'ERROR', 'errorText' ); 133 | } 134 | }); 135 | }); 136 | }); 137 | 138 | 139 | /** 140 | * Display message after form saved via AJAX 141 | * 142 | * @param elem Object Form element 143 | * @param message String Message to display 144 | * @param status String Class name to apply to message. success, error, successText, errorText 145 | */ 146 | function ajaxFormCallback( elem, message, status ) { 147 | jQuery( elem ).parents( ".ajaxFormWrapper" ).empty().addClass( status ).text( message ); 148 | } -------------------------------------------------------------------------------- /organic-search-analytics/apis/Google/Auth/ComputeEngine.php: -------------------------------------------------------------------------------- 1 | 27 | */ 28 | class Google_Auth_ComputeEngine extends Google_Auth_Abstract 29 | { 30 | const METADATA_AUTH_URL = 31 | 'http://metadata/computeMetadata/v1/instance/service-accounts/default/token'; 32 | private $client; 33 | private $token; 34 | 35 | public function __construct(Google_Client $client, $config = null) 36 | { 37 | $this->client = $client; 38 | } 39 | 40 | /** 41 | * Perform an authenticated / signed apiHttpRequest. 42 | * This function takes the apiHttpRequest, calls apiAuth->sign on it 43 | * (which can modify the request in what ever way fits the auth mechanism) 44 | * and then calls apiCurlIO::makeRequest on the signed request 45 | * 46 | * @param Google_Http_Request $request 47 | * @return Google_Http_Request The resulting HTTP response including the 48 | * responseHttpCode, responseHeaders and responseBody. 49 | */ 50 | public function authenticatedRequest(Google_Http_Request $request) 51 | { 52 | $request = $this->sign($request); 53 | return $this->client->getIo()->makeRequest($request); 54 | } 55 | 56 | /** 57 | * @param string $token 58 | * @throws Google_Auth_Exception 59 | */ 60 | public function setAccessToken($token) 61 | { 62 | $token = json_decode($token, true); 63 | if ($token == null) { 64 | throw new Google_Auth_Exception('Could not json decode the token'); 65 | } 66 | if (! isset($token['access_token'])) { 67 | throw new Google_Auth_Exception("Invalid token format"); 68 | } 69 | $token['created'] = time(); 70 | $this->token = $token; 71 | } 72 | 73 | public function getAccessToken() 74 | { 75 | return json_encode($this->token); 76 | } 77 | 78 | /** 79 | * Acquires a new access token from the compute engine metadata server. 80 | * @throws Google_Auth_Exception 81 | */ 82 | public function acquireAccessToken() 83 | { 84 | $request = new Google_Http_Request( 85 | self::METADATA_AUTH_URL, 86 | 'GET', 87 | array( 88 | 'Metadata-Flavor' => 'Google' 89 | ) 90 | ); 91 | $request->disableGzip(); 92 | $response = $this->client->getIo()->makeRequest($request); 93 | 94 | if ($response->getResponseHttpCode() == 200) { 95 | $this->setAccessToken($response->getResponseBody()); 96 | $this->token['created'] = time(); 97 | return $this->getAccessToken(); 98 | } else { 99 | throw new Google_Auth_Exception( 100 | sprintf( 101 | "Error fetching service account access token, message: '%s'", 102 | $response->getResponseBody() 103 | ), 104 | $response->getResponseHttpCode() 105 | ); 106 | } 107 | } 108 | 109 | /** 110 | * Include an accessToken in a given apiHttpRequest. 111 | * @param Google_Http_Request $request 112 | * @return Google_Http_Request 113 | * @throws Google_Auth_Exception 114 | */ 115 | public function sign(Google_Http_Request $request) 116 | { 117 | if ($this->isAccessTokenExpired()) { 118 | $this->acquireAccessToken(); 119 | } 120 | 121 | $this->client->getLogger()->debug('Compute engine service account authentication'); 122 | 123 | $request->setRequestHeaders( 124 | array('Authorization' => 'Bearer ' . $this->token['access_token']) 125 | ); 126 | 127 | return $request; 128 | } 129 | 130 | /** 131 | * Returns if the access_token is expired. 132 | * @return bool Returns True if the access_token is expired. 133 | */ 134 | public function isAccessTokenExpired() 135 | { 136 | if (!$this->token || !isset($this->token['created'])) { 137 | return true; 138 | } 139 | 140 | // If the token is set to expire in the next 30 seconds. 141 | $expired = ($this->token['created'] 142 | + ($this->token['expires_in'] - 30)) < time(); 143 | 144 | return $expired; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /organic-search-analytics/apis/Google/Http/Batch.php: -------------------------------------------------------------------------------- 1 | client = $client; 45 | $this->root_url = rtrim($rootUrl ? $rootUrl : $this->client->getBasePath(), '/'); 46 | $this->batch_path = $batchPath ? $batchPath : 'batch'; 47 | $this->expected_classes = array(); 48 | $boundary = (false == $boundary) ? mt_rand() : $boundary; 49 | $this->boundary = str_replace('"', '', $boundary); 50 | } 51 | 52 | public function add(Google_Http_Request $request, $key = false) 53 | { 54 | if (false == $key) { 55 | $key = mt_rand(); 56 | } 57 | 58 | $this->requests[$key] = $request; 59 | } 60 | 61 | public function execute() 62 | { 63 | $body = ''; 64 | 65 | /** @var Google_Http_Request $req */ 66 | foreach ($this->requests as $key => $req) { 67 | $body .= "--{$this->boundary}\n"; 68 | $body .= $req->toBatchString($key) . "\n\n"; 69 | $this->expected_classes["response-" . $key] = $req->getExpectedClass(); 70 | } 71 | 72 | $body .= "--{$this->boundary}--"; 73 | 74 | $url = $this->root_url . '/' . $this->batch_path; 75 | $httpRequest = new Google_Http_Request($url, 'POST'); 76 | $httpRequest->setRequestHeaders( 77 | array('Content-Type' => 'multipart/mixed; boundary=' . $this->boundary) 78 | ); 79 | 80 | $httpRequest->setPostBody($body); 81 | $response = $this->client->getIo()->makeRequest($httpRequest); 82 | 83 | return $this->parseResponse($response); 84 | } 85 | 86 | public function parseResponse(Google_Http_Request $response) 87 | { 88 | $contentType = $response->getResponseHeader('content-type'); 89 | $contentType = explode(';', $contentType); 90 | $boundary = false; 91 | foreach ($contentType as $part) { 92 | $part = (explode('=', $part, 2)); 93 | if (isset($part[0]) && 'boundary' == trim($part[0])) { 94 | $boundary = $part[1]; 95 | } 96 | } 97 | 98 | $body = $response->getResponseBody(); 99 | if ($body) { 100 | $body = str_replace("--$boundary--", "--$boundary", $body); 101 | $parts = explode("--$boundary", $body); 102 | $responses = array(); 103 | 104 | foreach ($parts as $part) { 105 | $part = trim($part); 106 | if (!empty($part)) { 107 | list($metaHeaders, $part) = explode("\r\n\r\n", $part, 2); 108 | $metaHeaders = $this->client->getIo()->getHttpResponseHeaders($metaHeaders); 109 | 110 | $status = substr($part, 0, strpos($part, "\n")); 111 | $status = explode(" ", $status); 112 | $status = $status[1]; 113 | 114 | list($partHeaders, $partBody) = $this->client->getIo()->ParseHttpResponse($part, false); 115 | $response = new Google_Http_Request(""); 116 | $response->setResponseHttpCode($status); 117 | $response->setResponseHeaders($partHeaders); 118 | $response->setResponseBody($partBody); 119 | 120 | // Need content id. 121 | $key = $metaHeaders['content-id']; 122 | 123 | if (isset($this->expected_classes[$key]) && 124 | strlen($this->expected_classes[$key]) > 0) { 125 | $class = $this->expected_classes[$key]; 126 | $response->setExpectedClass($class); 127 | } 128 | 129 | try { 130 | $response = Google_Http_REST::decodeHttpResponse($response, $this->client); 131 | $responses[$key] = $response; 132 | } catch (Google_Service_Exception $e) { 133 | // Store the exception as the response, so successful responses 134 | // can be processed. 135 | $responses[$key] = $e; 136 | } 137 | } 138 | } 139 | 140 | return $responses; 141 | } 142 | 143 | return null; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /organic-search-analytics/js/report.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sends an AJAX request for data to capture 3 | * 4 | * @param catId String Category ID for which data is being captured 5 | * @param script String File name/path to run 6 | * @param domain String Domain name to capture data for 7 | * @param date String Date to search. YYYY-MM-DD 8 | * @param callback String Function to run upon success 9 | * 10 | * @returns Object Database records. MySQL object 11 | */ 12 | function ajaxScript(catId, script, domain, date, callback) { 13 | jQuery.ajax({ 14 | type : "GET", 15 | url : script, 16 | data : { 17 | type : catId, 18 | domain : domain, 19 | date : date 20 | }, 21 | beforeSend: function( xhr ) { 22 | $("#"+catId+" li[domain='"+domain+"'][date='"+date+"']").append('Processing...'); 23 | }, 24 | success: function(data) { 25 | if( callback ) { 26 | window[callback](catId, domain, date, data); 27 | } 28 | }, 29 | error: function(xhr, status, errorThrown) { 30 | $("#"+catId+" li[domain='"+domain+"'][date='"+date+"']").append('Error, try again.'); 31 | } 32 | }); 33 | } 34 | 35 | 36 | /** 37 | * Hides granularity when date is not the group by method 38 | * Displays the appropriate sortBy features 39 | */ 40 | var groupBySortSettings = { 41 | groupByDate: { 42 | defaultSortBy: "sortByDate", 43 | disable: ["sortByQuery","sortByPage"], 44 | hideGranularity: false 45 | }, 46 | groupByQuery: { 47 | defaultSortBy: "sortByQuery", 48 | disable: ["sortByDate","sortByQueries","sortByPage"], 49 | hideGranularity: true 50 | }, 51 | groupByPage: { 52 | defaultSortBy: "sortByPage", 53 | disable: ["sortByDate","sortByQuery","sortByPages"], 54 | hideGranularity: true 55 | } 56 | }; 57 | jQuery("#report-custom input:radio[name=groupBy]").change(function(e){ 58 | // Toggle granularity 59 | if( !groupBySortSettings[e.target.id]['hideGranularity'] ) { 60 | jQuery( "#paramGroup_granularity" ).show(); 61 | } else { 62 | jQuery( "#paramGroup_granularity" ).hide(); 63 | } 64 | 65 | // Disable non-applicable sort by options 66 | jQuery("#paramGroup_sortBy .sortByOption").show(); 67 | jQuery("input[name=sortBy]").attr("disabled",false); 68 | for( disableOption in groupBySortSettings[e.target.id]['disable'] ) { 69 | jQuery("#"+groupBySortSettings[e.target.id]['disable'][disableOption]).parent("span.sortByOption").hide(); 70 | jQuery("#"+groupBySortSettings[e.target.id]['disable'][disableOption]).attr("disabled",true).prop("checked",false); 71 | } 72 | 73 | // Set default option if an option is not set 74 | if( !jQuery('input[name=sortBy]').is(':checked') || jQuery("#"+jQuery('input[name=sortBy]:checked').attr("id")).attr("disabled") == "disabled" ) { 75 | jQuery( "#"+groupBySortSettings[e.target.id]["defaultSortBy"] ).prop("checked",true); 76 | } 77 | }); 78 | 79 | 80 | /** 81 | * Hides date pickers when date range is selected. 82 | */ 83 | jQuery("#report-custom input:radio[name=date_type]").change(function(e){ 84 | if( e.target.id == "date_type_hard_set" ) { 85 | jQuery( "#paramGroup_dateStart, #paramGroup_dateEnd" ).show(); 86 | updateDateRange(); 87 | } else { 88 | jQuery( "#paramGroup_dateStart, #paramGroup_dateEnd" ).hide(); 89 | } 90 | }); 91 | 92 | 93 | /** 94 | * Toggles expanding content 95 | * 96 | * Expects a parent element with class of "expandable" 97 | * Clicking on a child H2 element will toggle the display of 98 | * content with class of "expandingBox" 99 | */ 100 | jQuery(".expandable").each(function(){ 101 | var element = this; 102 | jQuery( '#' + element.id + ' h2' ).click(function() { 103 | jQuery( '#' + element.id + ' .expandingBox').toggle(); 104 | }); 105 | }); 106 | 107 | 108 | /** 109 | * Date Picker 110 | * 111 | * Prevent start date from being later than end date 112 | * 113 | * Directly modifies the jQuery UI element 114 | * 115 | * @param element String Element to modify 116 | * @param endDate Date Last date avaialble for selection 117 | */ 118 | /* Date Picker - Prevent start date from being later than end date */ 119 | function updateStartDate( element, endDate ) { 120 | $( element ).datepicker( "option", "maxDate", endDate ); 121 | } 122 | 123 | 124 | /** 125 | * Date Picker 126 | * 127 | * Update display for number of days in the date range 128 | */ 129 | /* Date Picker - Count number of days */ 130 | function updateDateRange() { 131 | var date_start = $( "#date_start_inline" ).datepicker( "getDate" ); 132 | var date_end = $( "#date_end_inline" ).datepicker( "getDate" ); 133 | var date_range = Math.round( Math.abs( ( date_start.getTime() - date_end.getTime() ) / ( 24*60*60*1000 ) ) ) + 1; 134 | var date_range_display = " (" + date_range + " day" + ( date_range > 1 ? "s" : "" ) + ")"; 135 | $("#date_range_count").empty().text( date_range_display ); 136 | } 137 | 138 | 139 | /** 140 | * Tooltips 141 | * 142 | * jQuery UI tool tips 143 | * 144 | * Add a tooltip to any