Source of file SeoHeroToolAnalysisAdmin.php
Size: 72,105 Bytes - Last Modified: 2021-12-23T10:06:43+00:00
/var/www/docs.ssmods.com/process/src/src/SeoHeroToolAnalysisAdmin.php
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652 | <?php namespace nomidi\SeoHeroToolAnalysis; use DOMDocument; use Page; use SilverStripe\Admin\LeftAndMain; use SilverStripe\Control\Session; use SilverStripe\Core\Config\Config; use SilverStripe\ORM\ArrayList; use SilverStripe\Security\Permission; use SilverStripe\View\ArrayData; use SilverStripe\View\Requirements; class SeoHeroToolAnalysisAdmin extends LeftAndMain { private static $url_segment = 'shtpro-admin'; private static $allowed_actions = array('Analysis'); public $dom; public $rules = array('good' => 0, 'notice' => 0, 'wrong' => 0, 'total' => 0); public $pageHTML; public $pageBody; public $pageImages; public $pageLinks; public $pageTitle; public $pageID; public $wordCount; public $pageURL; public $pageURLSegment; public $siteRunsLocally; public $pageSpeedKey; public $pageSpeedTimeStamp; public $linkToWebsite = 'http://seo-hero-tools.com/toollink/'; public $linkToPageSpeedInsights = 'https://developers.google.com/speed/pagespeed/insights/?url='; public $linkToW3CPage = 'https://validator.w3.org/nu/?doc='; public $W3CTimeStamp; public function canView($member = null) { if (Permission::check('ADMIN') || Permission::check('SHT_ANALYSIS_VIEW')) { return true; } else { return false; } } /* ^ Function Analysis checks the actual site */ public function Analysis() { $PageID = $this->request->param('ID'); $Page = Page::get()->byID($PageID); if (!$Page || !$Page->ID) { $render = $this->owner->customise(array( 'AccessError' => _t('SeoHeroToolAnalysis.CanNotAccessCurrentPage', 'This page can not be accessed by the Analyse function. Please check the rights and if there are any authentication necessary.'), 'SHTAnalysisPath' => '/' .SEO_HERO_TOOL_ANALYSIS_PATH, ))->renderWith('nomidi\SeoHeroToolAnalysis\SeoHeroToolAnalysisPage'); return $render; } $this->pageID = $PageID; $URL = $Page->AbsoluteLink(); $this->URL = $URL; $this->pageURLSegment = $Page->URLSegment; $versions = $Page->allVersions(); $publishInformation = ''; if (!$versions->First()->WasPublished) { $render = $this->owner->customise(array( 'AccessError' => _t('SeoHeroToolAnalysis.ActualVersionNotPublished', 'The actual version of this Page is not published. Please publish it as the Seo Hero Tool can just access published websites.'), 'SHTAnalysisPath' => '/' .SEO_HERO_TOOL_ANALYSIS_PATH, ))->renderWith('nomidi\SeoHeroToolAnalysis\SeoHeroToolAnalysisPage'); } Requirements::clear(); Requirements::css('nomidi/silverstripe-seo-hero-tool-analysis:client/dist/css/style.css'); if ($this->loadPage($this->URL, $Page) == false) { $render = $this->owner->customise(array( 'AccessError' => _t('SeoHeroToolAnalysis.CanNotAccessCurrentPage', 'This page can not be accessed by the Analyse function. Please check the rights and if there are any authentication necessary.'), 'SHTAnalysisPath' => '/' .SEO_HERO_TOOL_ANALYSIS_PATH, ))->renderWith('nomidi\SeoHeroToolAnalysis\SeoHeroToolAnalysisPage'); return $render; } $this->checkIfSiteRunsLocally(); $contentID = Config::inst()->get('SeoHeroToolAnalysis', 'contentID'); if ($contentID) { $htmlForWordCount = $this->dom->getElementByID($contentID)->nodeValue; } else { $htmlForWordCount = $this->pageBody; } $this->wordCount = str_word_count(preg_replace('#\<(.+?)\>#', ' ', $htmlForWordCount)); $shtpTitle = $this->checkTitle(); $shtpSkipToMainContent = $this->checkSkipToMainContent(); $shtpAMPLink = $this->checkAMPLink(); $shtpMeta = $this->checkMeta($Page); $shtpURL = $this->checkURL($Page); $shtpUsefulFiles = $this->checkForUsefulFiles(); $shtpWordCount = $this->checkWordCount(); $shtpDirectoryDepth = $this->checkLinkDirectoryDepth($Page); $shtpHeadlineStructure = $this->checkHeadlineStructure(); $shtpLinks = $this->checkLinks($Page); $shtpImages = $this->checkImages(); $shtpStrong = $this->checkStrong(); $shtpStructuredData = $this->checkStructuredData($Page); $Keywords = new SeoHeroToolAnalysisKeyword(); $shtpKeywords = $Keywords->checkKeywords($Page, $this->dom); $keywordRules = $Keywords->getKeywordResults(); $debugMode = Config::inst()->get('SeoHeroToolAnalysis', 'Debug'); $pageSpeedResults = ''; $shtpw3c = ''; $pageSpeedResults = $this->checkPageSpeed($URL); $shtpw3c = $this->getW3CValidation($URL); if (!$this->siteRunsLocally) { $shtpPageSpeedLink = $this->linkToPageSpeedInsights.urlencode($URL); $shtpW3CLink = $this->linkToW3CPage.urlencode($URL); $pageSpeedMessage = ''; $W3CMessage = ''; } else { $shtpPageSpeedLink = ''; $shtpW3CLink = ''; $pageSpeedMessage = _t('SeoHeroToolAnalysis.PageSpeedLocally', 'The site runs locally and therefore a PageSpeed can not be calculated.'); $W3CMessage = _t('SeoHeroToolAnalysis.W3CLocally', 'The site runs locally and therefore a W3C Check can not be performed'); } $shtpCountArray = $this->getCountArray(); $render = $this->owner->customise(array( 'WordCount' => $this->wordCount, 'PageLink' => $URL, 'TitleResults' => $shtpTitle, 'MetaResults' => $shtpMeta, 'URLResults' => $shtpURL, 'DirectoryDepthResults' => $shtpDirectoryDepth, 'WordCountResults' => $shtpWordCount, 'HeadlineResults' => $shtpHeadlineStructure, 'LinkResults' => $shtpLinks, 'W3CResults' => $shtpw3c, 'PageSpeedResults' => $pageSpeedResults, 'StrongResults' => $shtpStrong, 'ImageResults' => $shtpImages, 'KeywordResults' => $shtpKeywords, 'UsefulFilesResults' => $shtpUsefulFiles, 'StructuredDataResults' => $shtpStructuredData, 'SkipMainContentResults' => $shtpSkipToMainContent, 'AMPLinkResult' => $shtpAMPLink, 'CountResults' => $shtpCountArray, 'RulesWrong' => $this->rules['wrong'], 'RulesNotice' => $this->rules['notice'], 'RulesGood' => $this->rules['good'], 'RulesTotal' => $this->rules['total'], 'KeywordRulesWrong' => $keywordRules['wrong'], 'KeywordRulesNotice' => $keywordRules['notice'], 'KeywordRulesGood' => $keywordRules['good'], 'KeywordRulesTotal' => $keywordRules['total'], 'LinkToWebsite' => $this->linkToWebsite, 'PageSpeedMessage' => $pageSpeedMessage, 'PageSpeedLink' => $shtpPageSpeedLink, 'PageSpeedTimestamp' => $this->pageSpeedTimeStamp, 'ActualPublishedStatus' => $publishInformation, 'W3CLink' => $shtpW3CLink, 'W3CMessage' => $W3CMessage, 'W3CTimeStamp' => $this->W3CTimeStamp, 'DebugMode' => $debugMode, 'ContentLocale'=>'de-DE', 'SHTAnalysisPath' => '/' .SEO_HERO_TOOL_ANALYSIS_PATH, ))->renderWith('nomidi\SeoHeroToolAnalysis\SeoHeroToolAnalysisPage'); return $render; } /* Checks if in the configuration the setting is set that this site runs locally. If yes the W3C Check will be skipped. Furhtermore the Structured Data check will not display the link to googles structured data tool. */ private function checkIfSiteRunsLocally() { $this->siteRunsLocally = Config::inst()->get('SeoHeroToolAnalysis', 'Local'); } private function updateRules($type = 3) { $this->rules['total']++; switch ($type) { case '1': $this->rules['wrong']++; break; case '2': $this->rules['notice']++; break; default: $this->rules['good']++; } } private function getCountArray() { $shtpCountArray = new ArrayList(); $shtpCountArray->push(array( 'CountLabel'=>_t('SeoHeroToolAnalysis.NumberOfWords', 'Number of Words'), 'CountValue'=> $this->wordCount )); $shtpCountArray->push(array( 'CountLabel'=>_t('SeoHeroToolAnalysis.NumberOfImages', 'Number of Images'), 'CountValue'=> $this->pageImages->length )); $shtpCountArray->push(array( 'CountLabel'=>_t('SeoHeroToolAnalysis.NumberOfLinks', 'Number of Links'), 'CountValue'=> $this->pageLinks->length )); return array('UnsortedListEntries'=>$shtpCountArray); } /* Function checkTitle() checks if the title of the page has the correct length and will return the appropiate message. */ private function checkTitle() { $lengthOfTitle = strlen($this->pageTitle); $UnsortedListEntries = new ArrayList(); $titleHelpLink = ""; $lengthRecommendation = _t('SeoHeroToolAnalysis.TitleLengthRecommendation', 'Recommendation 44 - 56 Characters'); $returnLength = $lengthRecommendation.' - '._t('SeoHeroToolAnalysis.Length', 'Length').': ' . $lengthOfTitle; if ($lengthOfTitle == 0) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.TitleIsEmpty', 'The title of this site is empty or does not exist! ').$returnLength, 'IconMess' => '1', 'HelpLink' => 'TitleIsEmpty', ) )); $this->updateRules(1); } elseif ($lengthOfTitle < 8) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.TitleLengthShort', 'The title of this site is too short! ').$returnLength, 'IconMess' => '1', 'HelpLink' => 'TitleLengthShort', ) )); $this->updateRules(1); } elseif ($lengthOfTitle >= 8 && $lengthOfTitle < 44) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.TitleLengthOK', 'The title of this site is fine, but it could be longer. '). $returnLength, 'IconMess' => '2', 'HelpLink' => 'TitleLengthOK', ) )); $this->updateRules(2); } elseif ($lengthOfTitle >= 44 && $lengthOfTitle < 56) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.TitleLengthGood', 'The title of this site is perfect. Well Done! '). $returnLength, 'IconMess' => '3', 'HelpLink' => 'TitleLengthGood', ) )); $this->updateRules(3); } else { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.TitleLengthLong', 'The title of this site is too long. '). $returnLength, 'IconMess' => '1', 'HelpLink' => 'TitleLengthLong', ) )); $this->updateRules(1); } return array( 'Headline' => _t('SeoHeroToolAnalysis.Title', 'Title'), 'UnsortedListEntries' => $UnsortedListEntries); } /* The function checkMeta($Page) checks if the Meta description has a good length and will return the appropiate answer. @param : $Page - the actual Page */ private function checkMeta($Page) { $metaDescription = $Page->BetterMetaDescription(); $lengthOfMetaDescription = strlen($metaDescription); $metaDescHelpLink = 'http://www.searchmetrics.com/de/glossar/meta-description/'; $UnsortedListEntries = new ArrayList(); $lengthRecommendation = _t('SeoHeroToolAnalysis.MetaLengthRecommendation', '(Optimal length is between 120 - 140 Characters)'); $returnLength = $lengthRecommendation.' - '._t('SeoHeroToolAnalysis.Length', 'Length').': ' . $lengthOfMetaDescription; if ($lengthOfMetaDescription == 0) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.NoMetaDescription', 'The Meta-Description is empty.'), 'IconMess' => '1', 'HelpLink' => 'NoMetaDescription', ) )); $this->updateRules(1); } elseif ($lengthOfMetaDescription < 79) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.MetaLengthShort', 'The Meta-Description is too short.'). $returnLength, 'IconMess' => '1', 'HelpLink' => 'MetaLengthShort', ) )); $this->updateRules(1); } elseif ($lengthOfMetaDescription >= 80 && $lengthOfMetaDescription < 120) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.MetaLengthOK', 'The Meta-Description is fine, but it could be longer. '). $returnLength, 'IconMess' => '2', 'HelpLink' => 'MetaLengthOK', ) )); $this->updateRules(2); } elseif ($lengthOfMetaDescription >= 120 && $lengthOfMetaDescription <= 140) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.MetaLengthGood', 'The length of the Meta-Description is perfect. Well Done! '). $returnLength, 'IconMess' => '3', 'HelpLink' => 'MetaLengthGood', ) )); $this->updateRules(3); } else { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.MetaLengthLong', 'The Meta-Description is too long. ') . $returnLength, 'IconMess' => '1', 'HelpLink' => 'MetaLengthLong', ) )); $this->updateRules(1); } return array( 'Headline' => _t('SeoHeroToolAnalysis.Metadescription', 'Meta-Description'), 'UnsortedListEntries' => $UnsortedListEntries); } /* The function checkURL($Page) will check if tha URL has a good length and will return the appropiate answer. @param $Page the actual Page */ private function checkURL($Page) { $URL = $Page->URLSegment; $UnsortedListEntries = new ArrayList(); $lengthOfURL = strlen($URL); $urlHelpLink = 'http://www.seo-scene.de/seo/geheimnis-perfekte-seo-urls-1417.html'; $lengthRecommendation = _t('SeoHeroToolAnalysis.URLLengthRecommendation', '(Optimal length is between 20 - 120 Characters)'); $returnLength = $lengthRecommendation.' - '._t('SeoHeroToolAnalysis.Length', 'Length').': ' . $lengthOfURL; if ($URL == "home") { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.URLLengthHome', 'The URL of the homepage has to be "home"'), 'IconMess' => '3', 'HelpLink' => 'URLLengthHome', ) )); $this->updateRules(3); } elseif ($lengthOfURL < 10) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.URLLengthShort', 'The length of the URL is way too short. ').$returnLength, 'IconMess' => '1', 'HelpLink' => 'URLLengthShort', ) )); $this->updateRules(1); } elseif ($lengthOfURL >= 10 && $lengthOfURL < 20) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.URLLengthOK', 'The length of the URL is fine but it could be longer. ').$returnLength, 'IconMess' => '2', 'HelpLink' => 'URLLengthOK', ) )); $this->updateRules(2); } elseif ($lengthOfURL >= 20 && $lengthOfURL <= 120) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.URLLengthGood', 'The length of the URL is perfect. Well Done! ').$returnLength, 'IconMess' => '3', 'HelpLink' => 'URLLengthGood', ) )); $this->updateRules(3); } else { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.URLLengthLong', 'The length of the URL is too long. ').$returnLength, 'IconMess' => '1', 'HelpLink' => 'URLLengthLong', ) )); $this->updateRules(1); } return array( 'Headline' => _t('SeoHeroToolAnalysis.URL', 'URL Parameter'), 'UnsortedListEntries' => $UnsortedListEntries); } private function checkWordCount() { $UnsortedListEntries = new ArrayList(); $lengthRecommendation = _t('SeoHeroToolAnalysis.WordCountRecommendation', '(A page should contain more than 200 words)'); $returnLength = $lengthRecommendation.' - '._t('SeoHeroToolAnalysis.Length', 'Length').': ' . $this->wordCount; if ($this->wordCount < 50) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.ContentLengthShort', 'The sites text is way too short. ') .$returnLength, 'IconMess' => '1', 'HelpLink' => 'ContentLengthShort' ) )); $this->updateRules(1); } elseif ($this->wordCount >= 50 && $this->wordCount <= 200) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.ContentLengthOK', 'The site has less than 200 words. '). $returnLength, 'IconMess' => '2', 'HelpLink' => 'ContentLengthOK' ) )); $this->updateRules(2); } else { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.ContentLengthGood', 'The site has more than 200 words. Well Done. '). $returnLength, 'IconMess' => '3', 'HelpLink' => 'ContentLengthGood' ) )); $this->updateRules(3); } return array( 'Headline' => _t('SeoHeroToolAnalysis.WordCount', 'Word Count'), 'UnsortedListEntries' => $UnsortedListEntries); } /* The function checkLinkDirectoryDepth() checks if the depth of the directory is fine and will return the appropiate answer. A too deep directory structure is not liked by search engines. @param $Page - the actual page */ private function checkLinkDirectoryDepth($Page) { $UnsortedListEntries = new ArrayList(); $folders = substr_count($Page->Link(), "/"); $addText = _t('SeoHeroToolAnalysis.DirectoryDepth', ' DirectoryDepth').': '.$folders; if ($folders > 5) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.TooHighDirectoryDepth', 'The Directory Depth should be as small as possible. '). $addText, 'IconMess' => '2', 'HelpLink' => 'TooHighDirectoryDepth' ) )); $this->updateRules(2); } else { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.FineDirectoryDepth', 'The Directory Depth is perfect. '). $addText, 'IconMess' => '3', 'HelpLink' => 'FineDirectoryDepth' ) )); $this->updateRules(3); } return array( 'Headline' => _t('SeoHeroToolAnalysis.DirectoryDepth', ' DirectoryDepth'), 'UnsortedListEntries' => $UnsortedListEntries); } /* The function helperForHeadlineCheck() checks the occurence and length of the following tags: h1, h2, h3. It will return then the appropiate results. Will be called by the function checkHeadlineStructure() */ private function helperForHeadlineCheck() { $UnsortedListEntries = new ArrayList(); $headlineArray = array( 'h1' => $this->dom->getElementsByTagName('h1'), 'h2' => $this->dom->getElementsByTagName('h2'), 'h3' => $this->dom->getElementsByTagName('h3') ); foreach ($headlineArray as $key => $value) { $headlineCount = $value->length; if ($headlineCount == 0) { if ($key == 'h1') { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.NoH1', 'There is no h1-Tag.'), 'IconMess' => '1', 'HelpLink' => 'NoH1' ) )); $this->updateRules(1); } else { $UnsortedListEntries->push(new ArrayData( array( 'Content' => sprintf(_t('SeoHeroToolAnalysis.NoHX', 'No %1$s Tag Found'), $key), 'IconMess' => '2', 'HelpLink' => 'NoHX' ) )); $this->updateRules(2); } } elseif ($headlineCount > 1 && $key == 'h1') { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.TooMuchH1', 'There is more than one h1-Tag'), 'IconMess' => '2', 'HelpLink' => 'TooMuchH1' ) )); $this->updateRules(2); } elseif ($headlineCount == 1 && $key == 'h1') { $sc = \Silverstripe\SiteConfig\SiteConfig::get()->First(); $headlineContent = $value->item(0)->nodeValue; $headlineLength = strlen($headlineContent); $lengthRecommendation = _t('SeoHeroToolAnalysis.HeadLineRecommendation', '(optimal length between 15 and 80 Characters)'); $addText = $lengthRecommendation.' - '._t('SeoHeroToolAnalysis.Length', 'Length').': ' . $headlineLength.' - '.$headlineContent; if ($headlineLength == 0) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.h1LengthEmpty', 'The h1 Headline is empty').' '.$addText, 'IconMess' => '1', 'HelpLink' => 'h1LengthEmpty' ) )); $this->updateRules(1); } elseif ($headlineLength < 15) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.h1LengthTooShort', 'The h1 Headline it quite short.').' '.$addText, 'IconMess' => '2', 'HelpLink' => 'h1LengthTooShort' ) )); $this->updateRules(2); } elseif ($headlineLength > 80) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.h1LengthTooLong', 'The h1 Headline it quite long.').' '.$addText, 'IconMess' => '2', 'HelpLink' => 'h1LengthTooLong' ) )); $this->updateRules(2); } else { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.h1LengthGood', 'The h1 tag has a good length').' '.$addText, 'IconMess' => '3', 'HelpLink' => 'h1LengthGood' ) )); $this->updateRules(3); } if ($headlineContent == $sc->Title && $this->pageURLSegment != 'home') { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.h1SameSiteConfigTitle', 'The h1 tag and general site title are the same. Please change the h1 content.').' - '.$headlineContent, 'IconMess' => '1', 'HelpLink' => 'h1SameSiteConfigTitle' ) )); $this->updateRules(1); } } else { $countHeadlines = 1; foreach ($value as $singleHeadline) { $headlineContent = $singleHeadline->textContent; $headlineLength = strlen($headlineContent); $lengthRecommendation = _t('SeoHeroToolAnalysis.HeadLineRecommendation', '(optimal length between 15 and 80 Characters)'); $addText = $lengthRecommendation.' - '._t('SeoHeroToolAnalysis.Length', 'Length').': ' . $headlineLength.' - '.$headlineContent; if ($headlineLength == 0) { $i = 0; $searchedHeadlinePos = 0; for ($i = 0; $i < $countHeadlines; $i++) { if ($i > 0) { $searchedHeadlinePos = strpos($this->pageHTML, '<'.$key.'>', $searchedHeadlinePos + 4); } else { $searchedHeadlinePos = strpos($this->pageHTML, '<'.$key.'>'); } } if ($searchedHeadlinePos) { $searchHeadlineEndPos = strpos($this->pageHTML, "</".$key.">", $searchedHeadlinePos); $headlineTagContent = substr($this->pageHTML, $searchedHeadlinePos+4, $searchHeadlineEndPos - $searchedHeadlinePos-4); if ($headlineTagContent != '') { $presumableTag = substr($headlineTagContent, 1, strpos($headlineTagContent, ' ')); $UnsortedListEntries->push(new ArrayData( array( 'Content' => sprintf(_t('SeoHeroToolAnalysis.headlineWitSomeContent', 'The Headline %1$s has some kind of content but no text. Found the following tag in the Headline ').': '.$presumableTag, $key), 'IconMess' => '2', 'HelpLink' => 'headlineWitSomeContent' ) )); $this->updateRules(2); } else { $UnsortedListEntries->push(new ArrayData( array( 'Content' => sprintf(_t('SeoHeroToolAnalysis.headlineLengthEmpty', 'The Headline %1$s is empty').' '.$addText, $key), 'IconMess' => '1', 'HelpLink' => 'headlineLengthEmpty' ) )); $this->updateRules(1); } } else { $UnsortedListEntries->push(new ArrayData( array( 'Content' => sprintf(_t('SeoHeroToolAnalysis.headlineLengthEmpty', 'The Headline %1$s is empty').' '.$addText, $key), 'IconMess' => '1', 'HelpLink' => 'headlineLengthEmpty' ) )); $this->updateRules(1); } } elseif ($headlineLength < 10) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => $key._t('SeoHeroToolAnalysis.headlineLengthTooShort', ' Headline is quite short.').' '.$addText, 'IconMess' => '2', 'HelpLink' => 'headlineLengthTooShort' ) )); $this->updateRules(2); } elseif ($headlineLength > 80) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => $key._t('SeoHeroToolAnalysis.headlineLengthTooLong', ' Headline is quite long.') .' '.$addText, 'IconMess' => '2', 'HelpLink' => 'headlineLengthTooLong' ) )); $this->updateRules(2); } else { $UnsortedListEntries->push(new ArrayData( array( 'Content' => $key._t('SeoHeroToolAnalysis.headlineLenghtGood', ' Headline has a good length.').' '.$addText, 'IconMess' => '3', 'HelpLink' => 'headlineLenghtGood' ) )); $this->updateRules(3); } $countHeadlines++; } } } return $UnsortedListEntries; } /* The function checkHeadlineStructure() checks the headline structure of the actual document. FOr example if h1, h2 h3 are present (with the help of function helperForHeadlineCheck()) and if there are skipped hX tags. */ private function checkHeadlineStructure() { $UnsortedListEntries = new ArrayList(); $UnsortedListEntries = $this->helperForHeadlineCheck(); $headlineStructure = array( '6' => $this->dom->getElementsByTagName('h6'), '5' => $this->dom->getElementsByTagName('h5'), '4' => $this->dom->getElementsByTagName('h4'), '3' => $this->dom->getElementsByTagName('h3'), '2' => $this->dom->getElementsByTagName('h2'), '1' => $this->dom->getElementsByTagName('h1') ); $headlineStructureError = 0; foreach ($headlineStructure as $hsKey => $hsVal) { if ($hsKey > 1 && $hsVal->length >= 1 && $headlineStructure[$hsKey - 1]->length == 0) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => sprintf( _t('SeoHeroToolAnalysis.HeadlineStructureError', 'There is at least H%1$d Headline but no H%2$d Headline.'), $hsKey, $hsKey - 1 ), 'IconMess' => '1', 'HelpLinnk' => 'HeadlineStructureError' ) )); $headlineStructureError = 1; $this->updateRules(1); } elseif ($hsKey == 1 && $headlineStructureError == 0) { $UnsortedListEntries->push(new ArrayData( array( 'Content' =>_t('SeoHeroToolAnalysis.NoHeadlineStructureError', 'The Headline Structure does not contain obvious Errors.'), 'IconMess' => '3', 'HelpLink' => 'NoHeadlineStructureError' ) )); $this->updateRules(3); } } return array( 'Headline' => _t('SeoHeroToolAnalysis.Headlines', 'Headlines'), 'UnsortedListEntries' => $UnsortedListEntries); } private function checkSibling($object) { foreach ($this->nextElementSibling($object)->childNodes as $morenodes) { return $morenodes; } return false; } private function nextElementSibling($node) { while ($node && ($node = $node->nextSibling)) { if ($node instanceof DOMElement) { break; } } return $node; } private function checkNodeValue($object) { $innerHTML = $this->dom->saveHTML($object); $innerText = trim(strip_tags($innerHTML)); if (strlen($innerText) >= 1) { return $innerText; } else { foreach ($object->childNodes as $child) { $innerElement = $this->dom->saveHTML($child); $imgneedle = '<img'; $imgres = stripos($innerElement, $imgneedle); $svgneedle = '<svg'; $svgres = stripos($innerElement, $svgneedle); $resend = false; if ($imgres !== false) { $resend = strpos($innerElement, '>', $imgres); $resstart = $imgres; } elseif ($svgres !== false) { $resend = strpos($innerElement, '>', $svgres); $resstart = $svgres; } if ($resend) { return htmlentities(substr($innerElement, $resstart, $resend+1-$resstart)); } } #svg und img noch abfragen return false; } /*elseif (is_object($object->nextSibling) && $sibling = $this->checkSibling($object)) { print_r($object); if (strlen(trim($sibling->nodeValue)) >= 1) { return $sibling->nodeValue; } else { return false; } } else { return false; }*/ } /* The function checkLinks() checks the links for titles and if there is content within the <a>-tags. @param $Page - the actual Page */ private function checkLinks($Page) { $UnsortedListEntries = new ArrayList(); $documentLinks = $this->pageLinks; $linkError = 0; $linkSameTitleNameMessage = ''; foreach ($documentLinks as $link) { $linkName = $this->checkNodeValue($link); $linkline = '<code class="html tag start-tag">'.htmlentities($this->dom->saveHTML($link)).'</code>'; if (!$linkName) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => sprintf( _t('SeoHeroToolAnalysis.LinkNoAttrTitleAndNoLinkDescription', 'Please check the following area for a Link with an empty "a" tag<em>%s</em>'), $linkline ), 'IconMess' => '1', 'HelpLink' => 'LinkNoAttrTitleAndNoLinkDescription' ) )); $this->updateRules(1); } if (!$link->hasAttribute('title')) { if ($linkName == "") { $linkNoAttrTitle = $linkline; } else { $linkNoAttrTitle = $linkName; } $UnsortedListEntries->push(new ArrayData( array( 'Content' => sprintf( _t('SeoHeroToolAnalysis.LinkNoAttrTitle', 'The Link %s has no title attribute'), $linkNoAttrTitle ), 'IconMess' => '1', 'HelpLink' => 'LinkNoAttrTitle' ) )); $linkError = 1; $this->updateRules(1); } else { $linkTitle = $link->getAttribute('title'); if ($linkName == $linkTitle) { $linkSameTitleNameMessage.= $linkName.'<br/>'; } } } if ($linkError == 0 && $documentLinks->length > 0) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.AllLinksHaveTitle', 'All links have a title attribute'), 'IconMess' => '3', 'HelpLink' => 'AllLinksHaveTitle' ) )); $this->updateRules(3); } if ($linkSameTitleNameMessage != '') { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.LinksWithSameTitleAndName', 'There are links with the same Name and Title Attribute. Those are these links:').'<br/>'.$linkSameTitleNameMessage, 'IconMess' => '2', 'HelpLink' => 'LinksWithSameTitleAndName' ) )); $this->updateRules(2); } return array( 'Headline' => _t('SeoHeroToolAnalysis.Links', 'Links'), 'UnsortedListEntries' => $UnsortedListEntries); } /* The function PageExists() checks if the actual page exists. @param $URL - the actual page. */ private function PageExists($URL) { $header = @get_headers($URL); if (is_array($header)) { if (preg_match('/^HTTP\\/\\d+\\.\\d+\\s+2\\d\\d\\s+.*$/', $header[0]) || preg_match('/^HTTP\\/\\d+\\.\\d+\\s+3\\d\\d\\s+.*$/', $header[0])) { return true; } else { return false; } } } /* The function loadPage() loads the actual DOM. @param $URL - the actual page */ private function loadPage($URL) { $this->dom = new DOMDocument('1.0', 'UTF-8'); libxml_use_internal_errors(true); $linkExists = $this->PageExists($URL); if ($linkExists == false) { return false; } $html = file_get_contents($URL); $this->dom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8')); $this->dom->preserveWhiteSpace = false; $this->pageLinks = $this->dom->getElementsByTagName('a'); $this->pageHTML = $this->dom->saveHTML(); if (isset($this->dom->getElementsByTagName('body')->item(0)->nodeValue)) { $this->pageBody = $this->dom->getElementsByTagName('body')->item(0)->nodeValue; } else { $this->pageBody = ''; } $this->pageImages = $this->dom->getElementsByTagName('img'); if (isset($this->dom->getElementsByTagName('title')->item(0)->nodeValue)) { $this->pageTitle = $this->dom->getElementsByTagName('title')->item(0)->nodeValue; } else { $this->pageTitle = ''; } return true; } /* The function checkStrong() checks if there are any B / Strong elements on the website */ private function checkStrong() { $UnsortedListEntries = new ArrayList(); $domStrong = $this->dom->getElementsByTagName('strong'); $domStrongCount = $domStrong->length; if ($domStrongCount == 0) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.FoundNoStrongElements', 'Found no strong elements on website (B / STRONG).'), 'IconMess' => '2', 'HelpLink' => 'FoundNoStrongElements' ) )); $this->updateRules(2); } else { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.FoundOneOrMoreStrongElements', 'Found one or more strong elements on website (B / STRONG).'), 'IconMess' => '3', 'HelpLink' => 'FoundOneOrMoreStrongElements' ) )); $this->updateRules(3); } return array( 'Headline' => _t('SeoHeroToolAnalysis.strongElements', 'Strong elements'), 'UnsortedListEntries' => $UnsortedListEntries); } /* The function checkImages() checks if there are images on the site and also if the images contain alt-tags. */ private function checkImages() { $UnsortedListEntries = new ArrayList(); $domImageCount = $this->pageImages->length; $imagesWithoutAltTag = 0; $imagesWithSameAltTagAndFilename = 0; $message = ''; $sameNameMessage = ''; if ($domImageCount >= 1) { foreach ($this->pageImages as $img) { $imgAltTag = $img->getAttribute('alt'); $imgFileName = $img->getAttribute('src'); $imgFileNameWithoutPath = substr($imgFileName, strrpos($imgFileName, '/')+1); $imgFileNameWithoutExtension = substr($imgFileNameWithoutPath, 0, strrpos($imgFileNameWithoutPath, '.')); if (trim($imgAltTag) == '') { $message .= sprintf(_t('SeoHeroToolAnalysis.ImageWithoutAltTag', 'The Image %1$s does not contain an Alt-Tag.').'<br/>', $imgFileName); $imagesWithoutAltTag++; } if ($imgAltTag == $imgFileNameWithoutPath || $imgAltTag == $imgFileNameWithoutExtension) { $sameNameMessage .= sprintf(_t('SeoHeroToolAnalysis.ImageWithSameAltAndFileName', 'The Image %1$s has the same filename and alt tag.'), $imgFileName); $imagesWithSameAltTagAndFilename++; } } if ($imagesWithoutAltTag == 0) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.AllImagesWithAltTag', 'All Images contain Alt-Tags.'), 'IconMess' => '3', 'HelpLink' => 'AllImagesWithAltTag' ) )); $this->updateRules(3); } elseif ($imagesWithoutAltTag >= 1) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => sprintf(_t('SeoHeroToolAnalysis.ImagesWithoutAltTagMessage', '%1$d out of %2$d Images are not having alt-Tags. The images are the following:').' <br/>'.$message, $imagesWithoutAltTag, $domImageCount), 'IconMess' => '1', 'HelpLink' => 'ImagesWithoutAltTagMessage' ) )); $this->updateRules(1); } if ($imagesWithSameAltTagAndFilename == 0) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.AllImagesWithDiffferentFilenameAndAltTag', 'All Images have for filename and alt-tag different values.'), 'IconMess' => '3', 'HelpLink' => 'AllImagesWithDiffferentFilenameAndAltTag' ) )); $this->updateRules(3); } elseif ($imagesWithSameAltTagAndFilename >= 1) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => sprintf(_t('SeoHeroToolAnalysis.ImagesWithoutDifferentFilenameAndAltTag', '%1$d out of %2$d Images have the same alt-tag and filename. Those images are the following:').' <br/>'.$sameNameMessage, $imagesWithSameAltTagAndFilename, $domImageCount), 'IconMess' => '2', 'HelpLink' => 'ImagesWithoutDifferentFilenameAndAltTag' ) )); $this->updateRules(2); } } else { $imageCountText = _t('SeoHeroToolAnalysis.NoImagesFound', 'This page does not contain any pictures'); $UnsortedListEntries->push(new ArrayData( array( 'Content' => $imageCountText, 'IconMess' => '2', 'HelpLink' => 'NoImagesFound' ) )); $this->updateRules(2); } return array( 'Headline' => _t('SeoHeroToolAnalysis.Images', 'Images'), 'UnsortedListEntries' => $UnsortedListEntries); } /* The function getW3CValidation() checks if the site runs locally. In that case no check will be performed. Otherweise the SeoHeroToolAnalysisW3CValidator will be called to check the actual Page for HTML Error and Warning. The result will be returned. @param $URL - the actual Page */ private function getW3CValidation($URL) { $UnsortedListEntries = new ArrayList(); $nonDocumentError = 0; if ($this->siteRunsLocally) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.SiteRunsLocally', 'The website runs locally. No W3C Validation possible.'), 'IconMess' => '2', 'HelpLink' => 'SiteRunsLocally' ) )); $this->updateRules(2); return array( 'Headline' => _t('SeoHeroToolAnalysis.W3CResult', 'W3C Validator Result'), 'UnsortedListEntries' => $UnsortedListEntries); } if (!$this->getAPIRequest('W3C')) { $results = SeoHeroToolAnalysisW3CValidator::checkData($URL); $messages = $results->messages; if (isset($messages[0]->type) && $messages[0]->type == 'non-document-error') { $nonDocumentError = 1; } $error = 0; $warning = 0; foreach ($messages as $mes) { if ($mes->type == 'error') { $error++; } elseif ($mes->type == 'warning') { $warning++; } } $errorsAndWarnings = array('error'=>$error,'warning'=>$warning); $this->setAPIRequestValue('W3C', $errorsAndWarnings); } $sessionVal = $this->getAPIRequestValue('W3C'); $this->W3CTimeStamp = date("d.m.Y H:i:s", $sessionVal[0]); $W3CResults = $sessionVal[1]; $foundHTMLErrors = $sessionVal[1]['error']; $foundHTMLWarnings = $sessionVal[1]['warning']; /* If the site is hosted locally there will be a "Name or service not known message" */ if ($nonDocumentError == 1) { if ($this->siteRunsLocally) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.W3CLocally', 'The site runs locally and therefore a W3C Check can not be performed'), 'IconMess' => '2', 'HelpLink' => 'W3CLocally' ) )); $this->updateRules(2); $nonDocumentError = 1; } else { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.W3CNon-Document-Error', 'The Document can not be scanned, maybe the website runs locally?'), 'IconMess' => '2', 'HelpLink' => 'W3CNon-Document-Error' ) )); $this->updateRules(2); $nonDocumentError = 1; } } if ($foundHTMLErrors == 0 && $foundHTMLWarnings == 0 && $nonDocumentError == 0) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.W3CNNoErrorsAndWarning', 'The Validator did not find any Errors or Warnings in your Document.'), 'IconMess' => '3', 'HelpLink' => 'W3CNNoErrorsAndWarning' ) )); $this->updateRules(3); } elseif ($nonDocumentError == 0) { if ($foundHTMLErrors == 1) { $messageFoundHTMLErrors = _t('SeoHeroToolAnalysis.W3CErrorSingular', 'one HTML error'); } elseif ($foundHTMLErrors > 1) { $messageFoundHTMLErrors = _t('SeoHeroToolAnalysis.W3CErrorPlural', 'several HTML errors'); } else { $messageFoundHTMLErrors = _t('SeoHeroToolAnalysis.W3CErrorNone', 'no HTML errors'); } if ($foundHTMLWarnings == 1) { $messageFoundHTMLWarnings = _t('SeoHeroToolAnalysis.W3CWarningSingular', 'one HTML warning'); } elseif ($foundHTMLWarnings > 1) { $messageFoundHTMLWarnings = _t('SeoHeroToolAnalysis.W3CWarningPlural', 'several HTML warnings'); } else { $messageFoundHTMLWarnings = _t('SeoHeroToolAnalysis.W3CWarningNone', 'no HTML warnings'); } $UnsortedListEntries->push(new ArrayData( array( 'Content' => sprintf( _t('SeoHeroToolAnalysis.W3CCountMessage', 'Es wurden auf der Seite %1$s und %2$s gefunden'), $messageFoundHTMLWarnings, $messageFoundHTMLErrors), 'IconMess' => '1', 'HelpLink' => 'W3CCountMessage' ) )); $this->updateRules(1); } return array( 'Headline' => _t('SeoHeroToolAnalysis.W3CResult', 'W3C Validator Result'), 'UnsortedListEntries' => $UnsortedListEntries); } /* The function checkSkipToMainContent() checks if there is within the body a link called "Skip to Main Content" which allows ScreenReaders to skip for example the navigation of the actual site. */ private function checkSkipToMainContent() { $UnsortedListEntries = new ArrayList(); $search = 'skip to main content'; if (!strpos(strtolower($this->pageBody), $search)) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.NoSkipToMainContentFound', 'No skip to main content link found on page.'), 'IconMess' => '2', 'HelpLink' => 'NoSkipToMainContentFound' ) )); $this->updateRules(2); } else { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.SkipToMainContentFound', 'Skip to main content found on page.'), 'IconMess' => '3', 'HelpLink' => 'SkipToMainContentFound' ) )); $this->updateRules(3); } return array( 'Headline' => _t('SeoHeroToolAnalysis.SkipToMainContent', 'Skip to main content'), 'UnsortedListEntries' => $UnsortedListEntries ); } /** * Check that given URL is valid and exists. * @param string $url URL to check * @return bool TRUE when valid | FALSE anyway */ private function urlExists($url) { // Remove all illegal characters from a url $url = filter_var($url, FILTER_SANITIZE_URL); // Validate URI if (filter_var($url, FILTER_VALIDATE_URL) === false // check only for http/https schemes. || !in_array(strtolower(parse_url($url, PHP_URL_SCHEME)), ['http','https'], true) ) { return false; } // Check that URL exists $file_headers = @get_headers($url); return !(!$file_headers || $file_headers[0] === 'HTTP/1.1 404 Not Found'); } /* The function checkAMPLink() checks if there is an link to an amp Page */ private function checkAMPLink() { $UnsortedListEntries = new ArrayList(); $search = 'rel="amphtml"'; if (!strpos(strtolower($this->pageHTML), $search)) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.NoAMPLinkFound', 'No AMP Link found on page.'), 'IconMess' => '2', 'HelpLink' => 'NoAMPLinkFound' ) )); $this->updateRules(2); } else { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.AMPLinkFound', 'AMP Link found on page.'), 'IconMess' => '3', 'HelpLink' => 'NoAMPLinkFound' ) )); $this->updateRules(3); //überprüfe ob die SEite existiert $searchPattern = "/<link rel=\"amphtml\" href=\"([^<]*)\">/s"; preg_match_all($searchPattern, $this->pageHTML, $aMatch); $ampPage = $aMatch[1][0]; if ($this->urlExists($ampPage)) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.AMPPageFound', 'AMP Page exists.'), 'IconMess' => '3', 'HelpLink' => 'NoAMPLinkFound' ) )); $this->updateRules(3); } else { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.NoAMPPageFound', 'The AMP Page dos not exists.'), 'IconMess' => '1', 'HelpLink' => 'NoAMPLinkFound' ) )); $this->updateRules(1); } } return array( 'Headline' => _t('SeoHeroToolAnalysis.AMPLink', 'AMP Link found'), 'UnsortedListEntries' => $UnsortedListEntries ); } /* The function checkAMPLinkPageExists() checks if there an amp link in head if the page exists */ private function checkAMPLinkPageExists() { $UnsortedListEntries = new ArrayList(); } /* The function checkForUsefulFiles() checks if in the main folder of this project there is a robots.txt and sitemap.xml file and if they are accessible. */ private function checkForUsefulFiles() { $UnsortedListEntries = new ArrayList(); if (isset($_SERVER['HTTPS'])) { $server = 'https://'.$_SERVER['SERVER_NAME']; $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.SiteIsHTTPS', 'Site can be reached via HTTPS.'), 'IconMess' => '3', 'HelpLink' => 'SiteIsHTTPS' ) )); $this->updateRules(3); } else { $server = 'http://'.$_SERVER['SERVER_NAME']; $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.SiteIsHTTP', 'Site can be reached via HTTP. HTTPS would be better.'), 'IconMess' => '2', 'HelpLink' => 'SiteIsHTTP' ) )); $this->updateRules(2); } # check robots.txt $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $server.'/robots.txt'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HEADER, 1); curl_setopt($ch, CURLOPT_NOBODY, 1); $curlResponse = curl_exec($ch); curl_close($ch); $curlResponeArray = explode("\n", $curlResponse); if (strpos($curlResponeArray[0], ' 200 OK')) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.FoundRobotstxt', 'Found Robots.txt file. Content was not checked.'), 'IconMess' => '3', 'HelpLink' => 'FoundRobotstxt' ) )); $this->updateRules(3); } elseif (strpos($curlResponeArray[0], ' 404')) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.FoundNoRobotstxt', 'No Robots.txt existing. HTTP Response is ').': '.$curlResponeArray[0], 'IconMess' => '1', 'HelpLink' => 'FoundNoRobotstxt' ) )); $this->updateRules(1); } else { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.ProblemWithRobotstxt', 'Robots.txt file was not accessible. Please check this as this files helps searchengines. - The HTTP Response is').': '.$curlResponeArray[0], 'IconMess' => '2', 'HelpLink' => 'ProblemWithRobotstxt' ) )); $this->updateRules(2); } # check sitemap.xml $chsm = curl_init(); curl_setopt($chsm, CURLOPT_URL, $server.'/sitemap.xml'); curl_setopt($chsm, CURLOPT_RETURNTRANSFER, 1); curl_setopt($chsm, CURLOPT_HEADER, 1); curl_setopt($chsm, CURLOPT_NOBODY, 1); $chsmResponse = curl_exec($chsm); curl_close($chsm); $chsmResponseArray = explode("\n", $chsmResponse); if (strpos($chsmResponseArray[0], ' 200 OK')) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.FoundSitemapXML', 'Found Robots.txt file. Content was not checked.'), 'IconMess' => '3', 'HelpLink' => 'FoundSitemapXML' ) )); $this->updateRules(3); } elseif (strpos($chsmResponseArray[0], ' 404')) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.FoundNoSitemapXML', 'No Sitemap.xml existing. HTTP Response is ').': '.$chsmResponseArray[0], 'IconMess' => '1', 'HelpLink' => 'FoundNoSitemapXML' ) )); $this->updateRules(1); } else { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.ProblemWithSitemapXML', 'Sitemap.xml file was not accessible. Please check this as this files helps searchengines. - The HTTP Response is').': '.$chsmResponseArray[0], 'IconMess' => '2', 'HelpLink' => 'ProblemWithSitemapXML' ) )); $this->updateRules(2); } return array( 'Headline' => _t('SeoHeroToolAnalysis.UsefulFiles', 'Files for Search Engines'), 'UnsortedListEntries' => $UnsortedListEntries ); } /* The function checkStructuredData checks if there is structed data on the actual page. If so there will be a link to the google testing tool displayed, in case the website is not running locally @param $Page - actual page */ private function checkStructuredData($Page) { $UnsortedListEntries = new ArrayList(); if ($this->siteRunsLocally) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.SitesRunsLocally', 'Website runs locally. Therefore it is not possible to check for structured Data.'), 'IconMess' => '2', 'HelpLink' => 'SitesRunsLocally' ) )); $this->updateRules(2); return array( 'Headline' => _t('SeoHeroToolAnalyse.StructuredData', 'Structured Data'), 'UnsortedListEntries' => $UnsortedListEntries ); } $searchPattern = "/<script type=\"application\/ld\+json\">([^<]*)<\/script>/s"; preg_match_all($searchPattern, $this->pageHTML, $aMatch); $foundstData = $aMatch[1]; $sDatas = $aMatch[1]; if (count($sDatas) >= 1) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.StructuredDataFound', ' Found structured Data. You can check the structured Data here').': '.'<a href="https://search.google.com/structured-data/testing-tool?hl=de#url=' . urldecode($Page->AbsoluteLink()) . '" target="_blank">Structured Data Google</a>', 'IconMess' => '3', 'HelpLink' => 'StructuredDataFound' ) )); $this->updateRules(3); } else { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.NoStructuredDataFound', 'No structured Data found.'), 'IconMess' => '2', 'HelpLink' => 'NoStructuredDataFound' ) )); $this->updateRules(2); } return array( 'Headline' => _t('SeoHeroToolAnalysis.StructuredData', 'Structured Data'), 'UnsortedListEntries' => $UnsortedListEntries ); } private function checkHTTPHeader($URL) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $URL); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HEADER, 1); curl_setopt($ch, CURLOPT_NOBODY, 1); $curlResponse = curl_exec($ch); curl_close($ch); } private function checkPageSpeed($URL) { $UnsortedListEntries = new ArrayList(); $pageSpeedInformationCounter = 0; if ($this->siteRunsLocally) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.SiteRunsLocallyPageSpeed', 'The website runs locally. No PageSpeed check possible.'), 'IconMess' => '2', 'HelpLink' => 'SiteRunsLocallyPageSpeed' ) )); $this->updateRules(2); return array( 'Headline' => _t('SeoHeroToolAnalysis.PageSpeed', 'PageSpeed Result'), 'UnsortedListEntries' => $UnsortedListEntries); } $PageSpeedAPI = Config::inst()->get('SeoHeroToolAnalysis', 'PageSpeedAPI'); if ($PageSpeedAPI != '') { if (!$this->getAPIRequest('PageSpeed')) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, 'https://www.googleapis.com/pagespeedonline/v2/runPagespeed?url='.urlencode($URL).'&strategy=desktop'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $curlResponse = curl_exec($ch); curl_close($ch); $decodedInformation = json_decode($curlResponse); if ($decodedInformation->error->code == '500') { $scoreValue = array('desktop'=>0,'mobile'=>0,'error'=>500); $this->setAPIRequestValue('PageSpeed', $scoreValue); } elseif ($decodedInformation->error->code == '400') { $scoreValue = array('desktop'=>0,'mobile'=>0,'error'=>400); $this->setAPIRequestValue('PageSpeed', $scoreValue); } else { $desktopScore = $decodedInformation->ruleGroups->SPEED->score; $chmob = curl_init(); curl_setopt($chmob, CURLOPT_URL, 'https://www.googleapis.com/pagespeedonline/v2/runPagespeed?url='.urlencode($URL).'&strategy=mobile'); curl_setopt($chmob, CURLOPT_RETURNTRANSFER, 1); $mobResponse = curl_exec($chmob); curl_close($chmob); $decodedMobInformation = json_decode($mobResponse); $mobileScore = $decodedMobInformation->ruleGroups->SPEED->score; $scoreValue = array('desktop'=>$desktopScore,'mobile'=>$mobileScore); $this->setAPIRequestValue('PageSpeed', $scoreValue); } } $pageSpeedInformation = $this->getAPIRequestValue('PageSpeed'); $this->pageSpeedTimeStamp = date("d.m.Y H:i:s", $pageSpeedInformation[0]); if (isset($pageSpeedInformation[1]['error']) && $pageSpeedInformation[1]['error'] == '500') { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.PageSpeedNoResults500', 'The response delivers an Error Code 500. Please check site manually!'), 'IconMess' => '1', 'HelpLink' => 'PageSpeedNoResults' ) )); $this->updateRules(1); return array( 'Headline' => _t('SeoHeroToolAnalysis.PageSpeed', 'PageSpeed Result'), 'UnsortedListEntries' => $UnsortedListEntries); } elseif (isset($pageSpeedInformation[1]['error']) && $pageSpeedInformation[1]['error'] == '400') { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.PageSpeedNoResults400', 'The response delivers an Error Code 400. Please check site manually!'), 'IconMess' => '1', 'HelpLink' => 'PageSpeedNoResults' ) )); $this->updateRules(1); return array( 'Headline' => _t('SeoHeroToolAnalysis.PageSpeed', 'PageSpeed Result'), 'UnsortedListEntries' => $UnsortedListEntries); } $desktopValue = $pageSpeedInformation[1]['desktop']; $mobileValue = $pageSpeedInformation[1]['mobile']; $pageSpeedInformationCounter++; if ($desktopValue >= 85) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.GoodDesktopPageSpeed', 'The PageSpeed is quite good.').' '.$desktopValue, 'IconMess' => '3', 'HelpLink' => 'GoodDesktopPageSpeed' ) )); $this->updateRules(3); } elseif ($desktopValue < 85 && $desktopValue > 60) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.MediocreDesktopPageSpeed', 'The PageSpeed is mediocre. This could be probably done better.').' '.$desktopValue, 'IconMess' => '2', 'HelpLink' => 'MediocreDesktopPageSpeed' ) )); $this->updateRules(2); } else { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.BadDesktopPageSpeed', 'The PageSpeed is far from good. Please check how you can enhance this value.').' '.$desktopValue, 'IconMess' => '1', 'HelpLink' => 'BadDesktopPageSpeed' ) )); $this->updateRules(1); } if ($mobileValue >= 85) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.GoodMobilePageSpeed', 'The PageSpeed for mobile devices is quite good.').' '.$mobileValue, 'IconMess' => '3', 'HelpLink' => 'GoodMobilePageSpeed' ) )); $this->updateRules(3); } elseif ($mobileValue < 85 && $mobileValue > 60) { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.MediocreMobilePageSpeed', 'The PageSpeed for mobile devices is mediocre. This could be probably done better.').' '.$mobileValue, 'IconMess' => '2', 'HelpLink' => 'MediocreMobilePageSpeed' ) )); $this->updateRules(2); } else { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.BadMobilePageSpeed', 'The PageSpeed for mobile devices is far from good. Please check how you can enhance this value.').' '.$mobileValue, 'IconMess' => '1', 'HelpLink' => 'BadMobilePageSpeed' ) )); $this->updateRules(1); } } if ($pageSpeedInformationCounter == 0) { if ($PageSpeedAPI != '') { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.PageSpeedNoResultsNoError', 'The Document can not be scanned, but no error was found in the return'), 'IconMess' => '2', 'HelpLink' => 'PageSpeedNoInformation' ) )); $this->updateRules(2); } else { $UnsortedListEntries->push(new ArrayData( array( 'Content' => _t('SeoHeroToolAnalysis.PageSpeedKeyNotSet', 'The Document can not be scanned as the PageSpeed API-key is not set'), 'IconMess' => '2', 'HelpLink' => 'PageSpeedNoInformation' ) )); $this->updateRules(2); } } return array( 'Headline' => _t('SeoHeroToolAnalysis.PageSpeed', 'PageSpeed'), 'UnsortedListEntries' => $UnsortedListEntries); } /* Function getRequest checks if a given value shall be requested again or not. Returns true or false */ private static function getAPIRequest($APIFunction) { $ses = new Session(isset($variables['_SESSION']) ? $variables['_SESSION'] : null); //$random = rand(5,15); //$random = $this->pageID; $sessionVal = $ses->get($APIFunction); if (isset($sessionVal) && $sessionVal != '') { if ($sessionVal[0] < time()-30) { return false; } return true; } else { return false; } } private function getAPIRequestValue($APIFunction) { $ses = new Session(isset($variables['_SESSION']) ? $variables['_SESSION'] : null); $sessionVal = $ses->get($APIFunction); if (isset($sessionVal) && $sessionVal != '') { return $sessionVal; } } private function setAPIRequestValue($APIFunction, $Value) { $ses = new Session(isset($variables['_SESSION']) ? $variables['_SESSION'] : null); $ses->set($APIFunction, array(time(),$Value)); } private function resetAPIRequestValue($APIFunction) { if ($this->getAPIRequest($APIFunction)) { Session::clear($APIFunction); } } } |