mikuli.cz

:)
git clone https://git.sr.ht/~ashymad/mikuli.cz
Log | Files | Refs

commit 7b5280733707292acc4ad0576130b8952f92d147
parent 3c0a3e09b8a32c2dcfe000f097c2f9b303797eb0
Author: markseu <mark2011@mayberg.se>
Date:   Sun, 22 Apr 2018 11:42:08 +0200

Updated system, spring remix with broken link checker

Diffstat:
Msystem/plugins/command.php | 278++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Msystem/plugins/update.php | 5++---
2 files changed, 243 insertions(+), 40 deletions(-)

diff --git a/system/plugins/command.php b/system/plugins/command.php @@ -1,13 +1,14 @@ <?php // Command plugin, https://github.com/datenstrom/yellow-plugins/tree/master/command -// Copyright (c) 2013-2017 Datenstrom, https://datenstrom.se +// Copyright (c) 2013-2018 Datenstrom, https://datenstrom.se // This file may be used and distributed under the terms of the public license. class YellowCommand { - const VERSION = "0.7.3"; + const VERSION = "0.7.4"; var $yellow; //access to API var $files; //number of files + var $links; //number of links var $errors; //number of errors var $locationsArgs; //locations with location arguments detected var $locationsArgsPagination; //locations with pagination arguments detected @@ -26,6 +27,7 @@ class YellowCommand { case "": $statusCode = $this->helpCommand(); break; case "build": $statusCode = $this->buildCommand($args); break; + case "check": $statusCode = $this->checkCommand($args); break; case "clean": $statusCode = $this->cleanCommand($args); break; case "version": $statusCode = $this->versionCommand($args); break; default: $statusCode = 0; @@ -37,6 +39,7 @@ class YellowCommand function onCommandHelp() { $help .= "build [DIRECTORY LOCATION]\n"; + $help .= "check [DIRECTORY LOCATION]\n"; $help .= "clean [DIRECTORY LOCATION]\n"; $help .= "version\n"; return $help; @@ -51,7 +54,7 @@ class YellowCommand return 200; } - // Build static files + // Build static website function buildCommand($args) { $statusCode = 0; @@ -60,7 +63,7 @@ class YellowCommand { if($this->checkStaticConfig()) { - $statusCode = $this->buildStatic($path, $location); + $statusCode = $this->buildStaticFiles($path, $location); } else { $statusCode = 500; $this->files = 0; $this->errors = 1; @@ -68,8 +71,7 @@ class YellowCommand echo "ERROR building files: Please configure StaticUrl in file '$fileName'!\n"; } echo "Yellow $command: $this->files file".($this->files!=1 ? 's' : ''); - echo ", $this->errors error".($this->errors!=1 ? 's' : ''); - echo ", status $statusCode\n"; + echo ", $this->errors error".($this->errors!=1 ? 's' : '')."\n"; } else { $statusCode = 400; echo "Yellow $command: Invalid arguments\n"; @@ -77,36 +79,41 @@ class YellowCommand return $statusCode; } - // Build static files and additional locations - function buildStatic($path, $location) + // Build static files + function buildStaticFiles($path, $locationFilter) { $path = rtrim(empty($path) ? $this->yellow->config->get("staticDir") : $path, '/'); - $this->files = $this->errors = $statusCode = 0; + $this->files = $this->errors = 0; $this->locationsArgs = $this->locationsArgsPagination = array(); - if(empty($location)) + $statusCode = empty($locationFilter) ? $this->cleanStaticFiles($path, $locationFilter) : 200; + $staticUrl = $this->yellow->config->get("staticUrl"); + list($scheme, $address, $base) = $this->yellow->lookup->getUrlInformation($staticUrl); + foreach($this->getContentLocations() as $location) + { + if(!preg_match("#^$base$locationFilter#", $location)) continue; + $statusCode = max($statusCode, $this->buildStaticFile($path, $location, true)); + } + foreach($this->locationsArgs as $location) { - $statusCode = $this->cleanStatic($path, $location); - foreach($this->getContentLocations() as $location) + if(!preg_match("#^$base$locationFilter#", $location)) continue; + $statusCode = max($statusCode, $this->buildStaticFile($path, $location, true)); + } + foreach($this->locationsArgsPagination as $location) + { + if(!preg_match("#^$base$locationFilter#", $location)) continue; + if(substru($location, -1)!=$this->yellow->toolbox->getLocationArgsSeparator()) { - $statusCode = max($statusCode, $this->buildStaticFile($path, $location, true)); + $statusCode = max($statusCode, $this->buildStaticFile($path, $location, false, true)); } - foreach($this->locationsArgs as $location) + for($pageNumber=2; $pageNumber<=999; ++$pageNumber) { - $statusCode = max($statusCode, $this->buildStaticFile($path, $location, true)); - } - foreach($this->locationsArgsPagination as $location) - { - if(substru($location, -1)!=$this->yellow->toolbox->getLocationArgsSeparator()) - { - $statusCode = max($statusCode, $this->buildStaticFile($path, $location, false, true)); - } - for($pageNumber=2; $pageNumber<=999; ++$pageNumber) - { - $statusCodeLocation = $this->buildStaticFile($path, $location.$pageNumber, false, true); - $statusCode = max($statusCode, $statusCodeLocation); - if($statusCodeLocation==100) break; - } + $statusCodeLocation = $this->buildStaticFile($path, $location.$pageNumber, false, true); + $statusCode = max($statusCode, $statusCodeLocation); + if($statusCodeLocation==100) break; } + } + if(empty($locationFilter)) + { foreach($this->getMediaLocations() as $location) { $statusCode = max($statusCode, $this->buildStaticFile($path, $location)); @@ -116,8 +123,6 @@ class YellowCommand $statusCode = max($statusCode, $this->buildStaticFile($path, $location)); } $statusCode = max($statusCode, $this->buildStaticFile($path, "/error/", false, false, true)); - } else { - $statusCode = $this->buildStaticFile($path, $location); } return $statusCode; } @@ -232,6 +237,161 @@ class YellowCommand } } } + + // Check static files for broken links + function checkCommand($args) + { + $statusCode = 0; + list($command, $path, $location) = $args; + if(empty($location) || $location[0]=='/') + { + if($this->checkStaticConfig()) + { + $statusCode = $this->checkStaticFiles($path, $location); + } else { + $statusCode = 500; + $this->files = $this->links = 0; + $fileName = $this->yellow->config->get("configDir").$this->yellow->config->get("configFile"); + echo "ERROR checking files: Please configure StaticUrl in file '$fileName'!\n"; + } + echo "Yellow $command: $this->files file".($this->files!=1 ? 's' : ''); + echo ", $this->links link".($this->links!=1 ? 's' : '')."\n"; + } else { + $statusCode = 400; + echo "Yellow $command: Invalid arguments\n"; + } + return $statusCode; + } + + // Check static files + function checkStaticFiles($path, $locationFilter) + { + $path = rtrim(empty($path) ? $this->yellow->config->get("staticDir") : $path, '/'); + $this->files = $this->links = 0; + $regex = "/^[^.]+$|".$this->yellow->config->get("staticDefaultFile")."$/"; + $fileNames = $this->yellow->toolbox->getDirectoryEntriesRecursive($path, $regex, false, false); + list($statusCodeFiles, $links) = $this->analyseStaticFiles($path, $locationFilter, $fileNames); + list($statusCodeLinks, $broken, $redirect) = $this->analyseLinks($path, $links); + if($statusCodeLinks!=200) + { + $this->showLinks($broken, "Broken links"); + $this->showLinks($redirect, "Redirect links"); + } + return max($statusCodeFiles, $statusCodeLinks); + } + + // Analyse static files, detect links + function analyseStaticFiles($path, $locationFilter, $fileNames) + { + $statusCode = 200; + $links = array(); + if(!empty($fileNames)) + { + $staticUrl = $this->yellow->config->get("staticUrl"); + list($scheme, $address, $base) = $this->yellow->lookup->getUrlInformation($staticUrl); + foreach($fileNames as $fileName) + { + if(is_readable($fileName)) + { + $locationSource = $this->getStaticLocation($path, $fileName); + if(!preg_match("#^$base$locationFilter#", $locationSource)) continue; + $fileData = $this->yellow->toolbox->readFile($fileName); + preg_match_all("/<(.*?)href=\"([^\"]+)\"(.*?)>/i", $fileData, $matches); + foreach($matches[2] as $match) + { + $location = rawurldecode($match); + if(preg_match("/^(.*?)#(.*)$/", $location, $tokens)) $location = $tokens[1]; + if(preg_match("/^(\w+):\/\/([^\/]+)(.*)$/", $location, $matches)) + { + $url = $location.(empty($matches[3]) ? "/" : ""); + if(!is_null($links[$url])) $links[$url] .= ","; + $links[$url] .= $locationSource; + if(defined("DEBUG") && DEBUG>=2) echo "YellowCommand::analyseStaticFiles detected url:$url<br/>\n"; + } else if($location[0]=='/') { + $url = "$scheme://$address$location"; + if(!is_null($links[$url])) $links[$url] .= ","; + $links[$url] .= $locationSource; + if(defined("DEBUG") && DEBUG>=2) echo "YellowCommand::analyseStaticFiles detected url:$url<br/>\n"; + } + } + ++$this->files; + } else { + $statusCode = 500; + echo "ERROR reading files: Can't read file '$fileName'!\n"; + } + } + $this->links = count($links); + } else { + $statusCode = 500; + echo "ERROR reading files: Can't find files in directory '$path'!\n"; + } + return array($statusCode, $links); + } + + // Analyse links, detect status + function analyseLinks($path, $links) + { + $statusCode = 200; + $broken = $redirect = $data = array(); + if(extension_loaded("curl")) + { + $staticUrl = $this->yellow->config->get("staticUrl"); + list($scheme, $address, $base) = $this->yellow->lookup->getUrlInformation($staticUrl); + $staticLocations = $this->getContentLocations(true); + uksort($links, "strnatcasecmp"); + foreach($links as $url=>$value) + { + if(defined("DEBUG") && DEBUG>=1) echo "YellowCommand::analyseLinks url:$url\n"; + if(preg_match("#^$staticUrl#", $url)) + { + $location = substru($url, 32); + $fileName = $path.substru($url, 32); + if(is_readable($fileName)) continue; + if(in_array($location, $staticLocations)) continue; + } + if(preg_match("/^(http|https):/", $url)) + { + $referer = "$scheme://$address".(($pos = strposu($value, ',')) ? substru($value, 0, $pos) : $value); + $statusCodeUrl = $this->getLinkStatus($url, $referer); + if($statusCodeUrl!=200) + { + $statusCode = max($statusCode, $statusCodeUrl); + $data[$url] = "$statusCodeUrl,$value"; + } + } + } + foreach($data as $url=>$value) + { + $locations = preg_split("/\s*,\s*/", $value); + $statusCodeUrl = array_shift($locations); + foreach($locations as $location) + { + if($statusCodeUrl>=300 && $statusCodeUrl<=399) { + $redirect["$scheme://$address$location -> $url - ".$this->getStatusFormatted($statusCodeUrl)] = $statusCodeUrl; + } else { + $broken["$scheme://$address$location -> $url - ".$this->getStatusFormatted($statusCodeUrl)] = $statusCodeUrl; + } + } + } + } else { + $statusCode = 500; + echo "ERROR checking links: Plugin 'command' requires cURL library!\n"; + } + return array($statusCode, $broken, $redirect); + } + + // Show links + function showLinks($data, $text) + { + if(!empty($data)) + { + echo "$text\n\n"; + uksort($data, "strnatcasecmp"); + $data = array_slice($data, 0, 99); + foreach($data as $key=>$value) echo "- $key\n"; + echo "\n"; + } + } // Clean static files function cleanCommand($args) @@ -240,7 +400,7 @@ class YellowCommand list($command, $path, $location) = $args; if(empty($location) || $location[0]=='/') { - $statusCode = $this->cleanStatic($path, $location); + $statusCode = $this->cleanStaticFiles($path, $location); echo "Yellow $command: Static file".(empty($location) ? "s" : "")." ".($statusCode!=200 ? "not " : "")."cleaned\n"; } else { $statusCode = 400; @@ -250,7 +410,7 @@ class YellowCommand } // Clean static files and directories - function cleanStatic($path, $location) + function cleanStaticFiles($path, $location) { $statusCode = 200; $path = rtrim(empty($path) ? $this->yellow->config->get("staticDir") : $path, '/'); @@ -259,7 +419,13 @@ class YellowCommand $statusCode = max($statusCode, $this->commandBroadcast("clean", "all")); $statusCode = max($statusCode, $this->cleanStaticDirectory($path)); } else { - $statusCode = $this->cleanStaticFile($path, $location); + if($this->yellow->lookup->isFileLocation($location)) + { + $fileName = $this->getStaticFile($path, $location, $statusCode); + $statusCode = $this->cleanStaticFile($fileName); + } else { + $statusCode = $this->cleanStaticDirectory($path.$location); + } } return $statusCode; } @@ -280,10 +446,9 @@ class YellowCommand } // Clean static file - function cleanStaticFile($path, $location) + function cleanStaticFile($fileName) { $statusCode = 200; - $fileName = $this->getStaticFile($path, $location, $statusCode); if(is_file($fileName)) { if(!$this->yellow->toolbox->deleteFile($fileName)) @@ -365,6 +530,18 @@ class YellowCommand return $fileName; } + // Return static location + function getStaticLocation($path, $fileName) + { + $location = substru($fileName, strlenu($path)); + if(basename($location)==$this->yellow->config->get("staticDefaultFile")) + { + $defaultFileLength = strlenu($this->yellow->config->get("staticDefaultFile")); + $location = substru($location, 0, -$defaultFileLength); + } + return $location; + } + // Return static redirect function getStaticRedirect($location) { @@ -374,9 +551,15 @@ class YellowCommand $output .= "</head>\n</html>"; return $output; } + + // Return human readable status + function getStatusFormatted($statusCode) + { + return $this->yellow->toolbox->getHttpStatusFormatted($statusCode, true); + } // Return content locations - function getContentLocations() + function getContentLocations($includeAll = false) { $locations = array(); $staticUrl = $this->yellow->config->get("staticUrl"); @@ -384,7 +567,7 @@ class YellowCommand $this->yellow->page->setRequestInformation($scheme, $address, $base, "", ""); foreach($this->yellow->pages->index(true, true) as $page) { - if($page->get("status")!="ignore" && $page->get("status")!="draft") + if(($page->get("status")!="ignore" && $page->get("status")!="draft") || $includeAll) { array_push($locations, $page->location); } @@ -458,6 +641,27 @@ class YellowCommand } return array($statusCode, $data); } + + // Return link status + function getLinkStatus($url, $referer) + { + if(extension_loaded("curl")) + { + $curlHandle = curl_init(); + curl_setopt($curlHandle, CURLOPT_URL, $url); + curl_setopt($curlHandle, CURLOPT_REFERER, $referer); + curl_setopt($curlHandle, CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; DatenstromYellow/".YellowCore::VERSION."; LinkChecker)"); + curl_setopt($curlHandle, CURLOPT_NOBODY, 1); + curl_setopt($curlHandle, CURLOPT_CONNECTTIMEOUT, 30); + curl_exec($curlHandle); + $statusCode = curl_getinfo($curlHandle, CURLINFO_HTTP_CODE); + curl_close($curlHandle); + if(defined("DEBUG") && DEBUG>=2) echo "YellowCommand::getLinkStatus status:$statusCode url:$url<br/>\n"; + } else { + $statusCode = 500; + } + return $statusCode; + } } $yellow->plugins->register("command", "YellowCommand", YellowCommand::VERSION); diff --git a/system/plugins/update.php b/system/plugins/update.php @@ -5,7 +5,7 @@ class YellowUpdate { - const VERSION = "0.7.9"; + const VERSION = "0.7.10"; var $yellow; //access to API var $updates; //number of updates @@ -18,7 +18,6 @@ class YellowUpdate $this->yellow->config->setDefault("updateInformationFile", "update.ini"); $this->yellow->config->setDefault("updateVersionFile", "version.ini"); $this->yellow->config->setDefault("updateResourceFile", "resource.ini"); - $this->yellow->config->setDefault("newFile", "page-new-(.*).txt"); //TODO: remove with next release } // Handle startup @@ -826,7 +825,7 @@ class YellowUpdate } $curlHandle = curl_init(); curl_setopt($curlHandle, CURLOPT_URL, $urlRequest); - curl_setopt($curlHandle, CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; YellowCore/".YellowCore::VERSION).")"; + curl_setopt($curlHandle, CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; DatenstromYellow/".YellowCore::VERSION."; SoftwareUpdater)"); curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, 1); curl_setopt($curlHandle, CURLOPT_CONNECTTIMEOUT, 30); $rawData = curl_exec($curlHandle);