mikuli.cz

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

generate.php (22101B)


      1 <?php
      2 // Generate extension, https://github.com/annaesvensson/yellow-generate
      3 
      4 class YellowGenerate {
      5     const VERSION = "0.9.8";
      6     public $yellow;                  // access to API
      7     public $files;                   // number of files
      8     public $errors;                  // number of errors
      9     public $locationsWithArguments;  // locations with arguments detected
     10     public $locationsWithPagination; // locations with pagination detected
     11     
     12     // Handle initialisation
     13     public function onLoad($yellow) {
     14         $this->yellow = $yellow;
     15         $this->yellow->system->setDefault("generateStaticUrl", "auto");
     16         $this->yellow->system->setDefault("generateStaticDirectory", "public/");
     17         $this->yellow->system->setDefault("generateStaticDefaultFile", "index.html");
     18         $this->yellow->system->setDefault("generateStaticErrorFile", "404.html");
     19     }
     20     
     21     // Handle request
     22     public function onRequest($scheme, $address, $base, $location, $fileName) {
     23        return $this->processRequestCache($scheme, $address, $base, $location, $fileName);
     24     }
     25     
     26     // Handle command
     27     public function onCommand($command, $text) {
     28         switch ($command) {
     29             case "generate": $statusCode = $this->processCommandGenerate($command, $text); break;
     30             case "clean":    $statusCode = $this->processCommandClean($command, $text); break;
     31             default:         $statusCode = 0;
     32         }
     33         return $statusCode;
     34     }
     35     
     36     // Handle command help
     37     public function onCommandHelp() {
     38         return array("generate [directory location]", "clean [directory location]");
     39     }
     40 
     41     // Process command to generate static website
     42     public function processCommandGenerate($command, $text) {
     43         $statusCode = 0;
     44         list($path, $location) = $this->yellow->toolbox->getTextArguments($text);
     45         if (is_string_empty($location) || substru($location, 0, 1)=="/") {
     46             if ($this->checkStaticSettings()) {
     47                 $statusCode = $this->generateStatic($path, $location);
     48             } else {
     49                 $statusCode = 500;
     50                 $this->files = 0;
     51                 $this->errors = 1;
     52                 $fileName = $this->yellow->system->get("coreExtensionDirectory").$this->yellow->system->get("coreSystemFile");
     53                 echo "ERROR generating files: Please configure GenerateStaticUrl in file '$fileName'!\n";
     54             }
     55             echo "Yellow $command: $this->files file".($this->files!=1 ? "s" : "");
     56             echo ", $this->errors error".($this->errors!=1 ? "s" : "")."\n";
     57         } else {
     58             $statusCode = 400;
     59             echo "Yellow $command: Invalid arguments\n";
     60         }
     61         return $statusCode;
     62     }
     63     
     64     // Generate static website
     65     public function generateStatic($path, $location) {
     66         $statusCode = 200;
     67         $this->files = $this->errors = 0;
     68         $path = rtrim(is_string_empty($path) ? $this->yellow->system->get("generateStaticDirectory") : $path, "/");
     69         if (is_string_empty($location)) {
     70             $statusCode = $this->cleanStatic($path, $location);
     71             foreach ($this->yellow->extension->data as $key=>$value) {
     72                 if (method_exists($value["object"], "onUpdate")) $value["object"]->onUpdate("clean");
     73             }
     74         }
     75         $statusCode = max($statusCode, $this->generateStaticContent($path, $location, "\rGenerating static website", 5, 95));
     76         $statusCode = max($statusCode, $this->generateStaticMedia($path, $location));
     77         $statusCode = max($statusCode, $this->generateStaticSystem($path, $location));
     78         echo "\rGenerating static website 100%... done\n";
     79         return $statusCode;
     80     }
     81     
     82     // Generate static content
     83     public function generateStaticContent($path, $locationFilter, $progressText, $increments, $max) {
     84         $statusCode = 200;
     85         $this->locationsWithArguments = $this->locationsWithPagination = array();
     86         $staticUrl = $this->yellow->system->get("generateStaticUrl");
     87         list($scheme, $address, $base) = $this->yellow->lookup->getUrlInformation($staticUrl);
     88         $locations = $this->getContentLocations();
     89         $filesEstimated = count($locations);
     90         foreach ($locations as $location) {
     91             echo "$progressText ".$this->getProgressPercent($this->files, $filesEstimated, $increments, $max/1.5)."%... ";
     92             if (!preg_match("#^$base$locationFilter#", "$base$location")) continue;
     93             $statusCode = max($statusCode, $this->generateStaticFile($path, $location, true));
     94         }
     95         foreach ($this->locationsWithArguments as $location) {
     96             echo "$progressText ".$this->getProgressPercent($this->files, $filesEstimated, $increments, $max/1.5)."%... ";
     97             if (!preg_match("#^$base$locationFilter#", "$base$location")) continue;
     98             $statusCode = max($statusCode, $this->generateStaticFile($path, $location, true));
     99         }
    100         $filesEstimated = $this->files + count($this->locationsWithArguments) + count($this->locationsWithPagination);
    101         foreach ($this->locationsWithPagination as $location) {
    102             echo "$progressText ".$this->getProgressPercent($this->files, $filesEstimated, $increments, $max)."%... ";
    103             if (!preg_match("#^$base$locationFilter#", "$base$location")) continue;
    104             for ($pageNumber=2; $pageNumber<=999; ++$pageNumber) {
    105                 $statusCodeLocation = $this->generateStaticFile($path, $location.$pageNumber, false, true);
    106                 $statusCode = max($statusCode, $statusCodeLocation);
    107                 if ($statusCodeLocation==100) break;
    108             }
    109         }
    110         echo "$progressText ".$this->getProgressPercent(100, 100, $increments, $max)."%... ";
    111         return $statusCode;
    112     }
    113     
    114     // Generate static media
    115     public function generateStaticMedia($path, $locationFilter) {
    116         $statusCode = 200;
    117         if (is_string_empty($locationFilter)) {
    118             foreach ($this->getMediaLocations() as $location) {
    119                 $statusCode = max($statusCode, $this->generateStaticFile($path, $location));
    120             }
    121         }
    122         return $statusCode;
    123     }
    124 
    125     // Generate static system
    126     public function generateStaticSystem($path, $locationFilter) {
    127         $statusCode = 200;
    128         if (is_string_empty($locationFilter)) {
    129             foreach ($this->getSystemLocations($path) as $location) {
    130                 $statusCode = max($statusCode, $this->generateStaticFile($path, $location));
    131             }
    132             $statusCode = max($statusCode, $this->generateStaticFile($path, "/error/", false, false, true));
    133         }
    134         return $statusCode;
    135     }
    136 
    137     // Generate static file
    138     public function generateStaticFile($path, $location, $analyse = false, $probe = false, $error = false) {
    139         $this->yellow->page = new YellowPage($this->yellow);
    140         $this->yellow->page->fileName = substru($location, 1);
    141         if (!is_readable($this->yellow->page->fileName)) {
    142             ob_start();
    143             $staticUrl = $this->yellow->system->get("generateStaticUrl");
    144             list($scheme, $address, $base) = $this->yellow->lookup->getUrlInformation($staticUrl);
    145             $statusCode = $this->requestStaticFile($scheme, $address, $base, $location);
    146             if ($statusCode<400 || $error) {
    147                 $fileData = ob_get_contents();
    148                 $statusCode = $this->saveStaticFile($path, $location, $fileData, $statusCode);
    149             }
    150             ob_end_clean();
    151         } else {
    152             $statusCode = $this->copyStaticFile($path, $location);
    153         }
    154         if ($statusCode==200 && $analyse) $this->analyseStaticLocations($scheme, $address, $base, $fileData);
    155         if ($statusCode==404 && $probe) $statusCode = 100;
    156         if ($statusCode==404 && $error) $statusCode = 200;
    157         if ($statusCode>=200) ++$this->files;
    158         if ($statusCode>=400) {
    159             ++$this->errors;
    160             echo "\rERROR generating location '$location', ".$this->yellow->page->getStatusCode(true)."\n";
    161         }
    162         if ($this->yellow->system->get("coreDebugMode")>=2) {
    163             echo "YellowGenerate::generateStaticFile status:$statusCode location:$location<br />\n";
    164         }
    165         return $statusCode;
    166     }
    167     
    168     // Request static file
    169     public function requestStaticFile($scheme, $address, $base, $location) {
    170         list($serverName, $serverPort) = $this->yellow->toolbox->getTextList($address, ":", 2);
    171         if (is_string_empty($serverPort)) $serverPort = $scheme=="https" ? 443 : 80;
    172         $_SERVER["SERVER_PROTOCOL"] = "HTTP/1.1";
    173         $_SERVER["SERVER_NAME"] = $serverName;
    174         $_SERVER["SERVER_PORT"] = $serverPort;
    175         $_SERVER["REQUEST_METHOD"] = "GET";
    176         $_SERVER["REQUEST_SCHEME"] = $scheme;
    177         $_SERVER["REQUEST_URI"] = $base.$location;
    178         $_SERVER["SCRIPT_NAME"] = $base."/yellow.php";
    179         $_SERVER["REMOTE_ADDR"] = "127.0.0.1";
    180         $_REQUEST = array();
    181         return $this->yellow->request();
    182     }
    183     
    184     // Save static file
    185     public function saveStaticFile($path, $location, $fileData, $statusCode) {
    186         $modified = strtotime($this->yellow->page->getHeader("Last-Modified"));
    187         if ($modified==0) $modified = $this->yellow->toolbox->getFileModified($this->yellow->page->fileName);
    188         if ($statusCode>=301 && $statusCode<=303) {
    189             $fileData = $this->getStaticRedirect($this->yellow->page->getHeader("Location"));
    190             $modified = time();
    191         }
    192         $fileName = $this->getStaticFile($path, $location, $statusCode);
    193         if (is_file($fileName)) $this->yellow->toolbox->deleteFile($fileName);
    194         if (!$this->yellow->toolbox->writeFile($fileName, $fileData, true) ||
    195             !$this->yellow->toolbox->modifyFile($fileName, $modified)) {
    196             $statusCode = 500;
    197             $this->yellow->page->statusCode = $statusCode;
    198             $this->yellow->page->errorMessage = "Can't write file '$fileName'!";
    199         }
    200         return $statusCode;
    201     }
    202     
    203     // Copy static file
    204     public function copyStaticFile($path, $location) {
    205         $statusCode = 200;
    206         $modified = $this->yellow->toolbox->getFileModified($this->yellow->page->fileName);
    207         $fileName = $this->getStaticFile($path, $location, $statusCode);
    208         if (is_file($fileName)) $this->yellow->toolbox->deleteFile($fileName);
    209         if (!$this->yellow->toolbox->copyFile($this->yellow->page->fileName, $fileName, true) ||
    210             !$this->yellow->toolbox->modifyFile($fileName, $modified)) {
    211             $statusCode = 500;
    212             $this->yellow->page->statusCode = $statusCode;
    213             $this->yellow->page->errorMessage = "Can't write file '$fileName'!";
    214         }
    215         return $statusCode;
    216     }
    217     
    218     // Analyse static locations with arguments
    219     public function analyseStaticLocations($scheme, $address, $base, $rawData) {
    220         preg_match_all("/<(.*?)href=\"([^\"]+)\"(.*?)>/i", $rawData, $matches);
    221         foreach ($matches[2] as $match) {
    222             $location = rawurldecode($match);
    223             if (preg_match("/^(.*?)#(.*)$/", $location, $tokens)) $location = $tokens[1];
    224             if (preg_match("/^(\w+):\/\/([^\/]+)(.*)$/", $location, $tokens)) {
    225                 if ($tokens[1]!=$scheme) continue;
    226                 if ($tokens[2]!=$address) continue;
    227                 $location = $tokens[3];
    228             }
    229             if (substru($location, 0, strlenu($base))!=$base) continue;
    230             if (substru($location, strlenu($base), 1)!="/") continue;
    231             $location = substru($location, strlenu($base));
    232             if (!$this->yellow->toolbox->isLocationArguments($location)) continue;
    233             if (!$this->yellow->toolbox->isLocationArgumentsPagination($location)) {
    234                 if (!isset($this->locationsWithArguments[$location])) {
    235                     $this->locationsWithArguments[$location] = $location;
    236                     if ($this->yellow->system->get("coreDebugMode")>=2) {
    237                         echo "YellowGenerate::analyseStaticLocations detected location:$location<br />\n";
    238                     }
    239                 }
    240             } else {
    241                 $location = rtrim($location, "0..9");
    242                 if (!isset($this->locationsWithPagination[$location])) {
    243                     $this->locationsWithPagination[$location] = $location;
    244                     if ($this->yellow->system->get("coreDebugMode")>=2) {
    245                         echo "YellowGenerate::analyseStaticLocations detected location:$location<br />\n";
    246                     }
    247                 }
    248             }
    249         }
    250     }
    251     
    252     // Process command to clean static website
    253     public function processCommandClean($command, $text) {
    254         $statusCode = 0;
    255         list($path, $location) = $this->yellow->toolbox->getTextArguments($text);
    256         if (is_string_empty($location) || substru($location, 0, 1)=="/") {
    257             $statusCode = $this->cleanStatic($path, $location);
    258             echo "Yellow $command: Static website";
    259             echo " ".($statusCode!=200 ? "not " : "")."cleaned\n";
    260         } else {
    261             $statusCode = 400;
    262             echo "Yellow $command: Invalid arguments\n";
    263         }
    264         return $statusCode;
    265     }
    266     
    267     // Clean static website
    268     public function cleanStatic($path, $location) {
    269         $statusCode = 200;
    270         $path = rtrim(is_string_empty($path) ? $this->yellow->system->get("generateStaticDirectory") : $path, "/");
    271         if (is_string_empty($location)) {
    272             $statusCode = max($statusCode, $this->cleanStaticDirectory($path));
    273         } else {
    274             if ($this->yellow->lookup->isFileLocation($location)) {
    275                 $fileName = $this->getStaticFile($path, $location, $statusCode);
    276                 $statusCode = $this->cleanStaticFile($fileName);
    277             } else {
    278                 $statusCode = $this->cleanStaticDirectory($path.$location);
    279             }
    280         }
    281         return $statusCode;
    282     }
    283     
    284     // Clean static directory
    285     public function cleanStaticDirectory($path) {
    286         $statusCode = 200;
    287         if (is_dir($path) && $this->checkStaticDirectory($path)) {
    288             if (!$this->yellow->toolbox->deleteDirectory($path)) {
    289                 $statusCode = 500;
    290                 echo "ERROR cleaning files: Can't delete directory '$path'!\n";
    291             }
    292         }
    293         return $statusCode;
    294     }
    295     
    296     // Clean static file
    297     public function cleanStaticFile($fileName) {
    298         $statusCode = 200;
    299         if (is_file($fileName)) {
    300             if (!$this->yellow->toolbox->deleteFile($fileName)) {
    301                 $statusCode = 500;
    302                 echo "ERROR cleaning files: Can't delete file '$fileName'!\n";
    303             }
    304         }
    305         return $statusCode;
    306     }
    307     
    308     // Process request for cached files
    309     public function processRequestCache($scheme, $address, $base, $location, $fileName) {
    310         $statusCode = 0;
    311         if (is_dir($this->yellow->system->get("coreCacheDirectory"))) {
    312             $location .= $this->yellow->toolbox->getLocationArguments();
    313             $fileName = rtrim($this->yellow->system->get("coreCacheDirectory"), "/").$location;
    314             if (!$this->yellow->lookup->isFileLocation($location)) $fileName .= $this->yellow->system->get("generateStaticDefaultFile");
    315             if (is_file($fileName) && is_readable($fileName) && !$this->yellow->lookup->isCommandLine()) {
    316                 $statusCode = $this->yellow->sendFile(200, $fileName, true);
    317             }
    318         }
    319         return $statusCode;
    320     }
    321     
    322     // Check static settings
    323     public function checkStaticSettings() {
    324         return preg_match("/^(http|https):/", $this->yellow->system->get("generateStaticUrl"));
    325     }
    326     
    327     // Check static directory
    328     public function checkStaticDirectory($path) {
    329         $ok = false;
    330         if (!is_string_empty($path)) {
    331             if ($path==rtrim($this->yellow->system->get("generateStaticDirectory"), "/")) $ok = true;
    332             if ($path==rtrim($this->yellow->system->get("coreCacheDirectory"), "/")) $ok = true;
    333             if ($path==rtrim($this->yellow->system->get("coreTrashDirectory"), "/")) $ok = true;
    334             if (is_file("$path/".$this->yellow->system->get("generateStaticDefaultFile"))) $ok = true;
    335             if (is_file("$path/yellow.php")) $ok = false;
    336         }
    337         return $ok;
    338     }
    339     
    340     // Return progress in percent
    341     public function getProgressPercent($now, $total, $increments, $max) {
    342         $max = intval($max/$increments) * $increments;
    343         $percent = intval(($max/$total) * $now);
    344         if ($increments>1) $percent = intval($percent/$increments) * $increments;
    345         return min($max, $percent);
    346     }
    347     
    348     // Return static file
    349     public function getStaticFile($path, $location, $statusCode) {
    350         if ($statusCode<400) {
    351             $fileName = $path.$location;
    352             if (!$this->yellow->lookup->isFileLocation($location)) $fileName .= $this->yellow->system->get("generateStaticDefaultFile");
    353         } elseif ($statusCode==404) {
    354             $fileName = $path."/".$this->yellow->system->get("generateStaticErrorFile");
    355         } else {
    356             $fileName = $path."/error.html";
    357         }
    358         return $fileName;
    359     }
    360     
    361     // Return static redirect
    362     public function getStaticRedirect($location) {
    363         $output = "<!DOCTYPE html><html>\n<head>\n";
    364         $output .= "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\" />\n";
    365         $output .= "<meta http-equiv=\"refresh\" content=\"0;url=".htmlspecialchars($location)."\" />\n";
    366         $output .= "</head>\n</html>";
    367         return $output;
    368     }
    369 
    370     // Return content locations
    371     public function getContentLocations() {
    372         $locations = array();
    373         $staticUrl = $this->yellow->system->get("generateStaticUrl");
    374         list($scheme, $address, $base) = $this->yellow->lookup->getUrlInformation($staticUrl);
    375         $this->yellow->page->setRequestInformation($scheme, $address, $base, "", "", false);
    376         foreach ($this->yellow->content->getChildrenRecursive("", true) as $page) {
    377             if (preg_match("/exclude/i", $page->get("generate"))) continue;
    378             if ($page->get("status")=="private" || $page->get("status")=="draft") continue;
    379             array_push($locations, $page->location);
    380         }
    381         if (!$this->yellow->content->find("/") && $this->yellow->system->get("coreMultiLanguageMode")) array_unshift($locations, "/");
    382         return $locations;
    383     }
    384     
    385     // Return media locations
    386     public function getMediaLocations() {
    387         $locations = array();
    388         $path = $this->yellow->system->get("coreMediaDirectory");
    389         $fileNames = $this->yellow->toolbox->getDirectoryEntriesRecursive($path, "/.*/", false, false);
    390         foreach ($fileNames as $fileName) {
    391             $location = $this->yellow->lookup->findMediaLocationFromFile($fileName);
    392             if (!is_string_empty($location)) array_push($locations, $location);
    393         }
    394         return $locations;
    395     }
    396 
    397     // Return system locations
    398     public function getSystemLocations($pathIgnore) {
    399         $locations = array();
    400         $path = $this->yellow->system->get("coreWorkerDirectory");
    401         $fileNames = $this->yellow->toolbox->getDirectoryEntriesRecursive($path, "/.*/", false, false);
    402         foreach ($fileNames as $fileName) {
    403             $location = $this->yellow->lookup->findSystemLocationFromFile($fileName);
    404             if (!is_string_empty($location)) array_push($locations, $location);
    405         }
    406         $path = $this->yellow->system->get("coreThemeDirectory");
    407         $fileNames = $this->yellow->toolbox->getDirectoryEntriesRecursive($path, "/.*/", false, false);
    408         foreach ($fileNames as $fileName) {
    409             $location = $this->yellow->lookup->findSystemLocationFromFile($fileName);
    410             if (!is_string_empty($location)) array_push($locations, $location);
    411         }
    412         $path = $this->yellow->system->get("coreLayoutDirectory");
    413         $fileNames = $this->yellow->toolbox->getDirectoryEntriesRecursive($path, "/.*/", false, false);
    414         foreach ($fileNames as $fileName) {
    415             $location = $this->yellow->lookup->findSystemLocationFromFile($fileName);
    416             if (!is_string_empty($location)) array_push($locations, $location);
    417         }
    418         $regexIgnore = "#^(/".
    419             $this->yellow->system->get("coreContentDirectory")."|/".
    420             $this->yellow->system->get("coreMediaDirectory")."|/".
    421             $this->yellow->system->get("coreSystemDirectory")."|/".
    422             $this->yellow->system->get("generateStaticDirectory")."|/$pathIgnore/|/yellow.php)#";
    423         $fileNames = $this->yellow->toolbox->getDirectoryEntriesRecursive(".", "/.*/", false, false);
    424         foreach ($fileNames as $fileName) {
    425             $location = substru($fileName, 1);
    426             if (!preg_match($regexIgnore, $location)) array_push($locations, $location);
    427         }
    428         return array_diff($locations, $this->getSystemLocationsIgnore());
    429     }
    430     
    431     // Return system locations to ignore
    432     public function getSystemLocationsIgnore() {
    433         $locations = array();
    434         $path = $this->yellow->system->get("coreWorkerDirectory");
    435         $workerDirectoryLength = strlenu($this->yellow->system->get("coreWorkerDirectory"));
    436         if ($this->yellow->extension->isExisting("bundle")) {
    437             foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/^bundle-(.*)/", false, false) as $entry) {
    438                 list($locationsBundle) = $this->yellow->extension->get("bundle")->getBundleInformation($entry);
    439                 $locations = array_merge($locations, $locationsBundle);
    440             }
    441         }
    442         if ($this->yellow->extension->isExisting("edit")) {
    443             foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/^edit(\-|\.)(.*)/", false, false) as $entry) {
    444                 $location = $this->yellow->system->get("coreAssetLocation").substru($entry, $workerDirectoryLength);
    445                 array_push($locations, $location);
    446             }
    447         }
    448         return array_unique($locations);
    449     }
    450 }