nextcloud-memento/lib/Controller/ findMementos.php
94 lines
2.9 KiB

  1. <?php
  2. namespace OCA\Memento\Controller;
  3. use DOMDocument;
  4. use DateTime;
  5. // Finds HTML files that claim to be a snapshot of the given URL;
  6. // Returns an array, each item being a file's url + original url + snapshot datetime.
  7. function findMementos($folder, $url) {
  8. // Get all HTML files the user owns.
  9. $files = $folder->searchByMime('text/html');
  10. // Filter them for pages that have a <link rel="original"> referring to the given URL.
  11. $matchingMementos = array();
  12. foreach ($files as $file) {
  13. $content = $file->getContent();
  14. try {
  15. $DOM = new DOMDocument;
  16. $DOM->loadHTML($content);
  17. $headElement = $DOM->documentElement->getElementsByTagName('head')[0];
  18. $originalUrls = getOriginalUrls($headElement);
  19. foreach ($originalUrls as $originalUrl) {
  20. if (normaliseUrl($originalUrl) === normaliseUrl($url)) {
  21. // Found a match!
  22. // Read its datetime
  23. $datetime = getDatetime($headElement);
  24. // Construct its URL.
  25. $absoluteFilePath = $file->getPath();
  26. $relativeFilePath = $folder->getRelativePath($absoluteFilePath);
  27. $mementoUrl = joinPaths("/apps/raw/files", $relativeFilePath); // XXX hardcoded dependency
  28. $matchingMementos[] = [
  29. 'mementoUrl' => $mementoUrl,
  30. 'originalUrl' => $originalUrl,
  31. 'datetime' => $datetime
  32. ];
  33. }
  34. }
  35. } catch (Exception $e) {
  36. continue;
  37. }
  38. }
  39. return $matchingMementos;
  40. }
  41. function joinPaths($piece1, $piece2) {
  42. $left = rtrim($piece1, '/');
  43. $right = ltrim($piece2, '/');
  44. return "$left/$right";
  45. }
  46. // Reads hrefs from any <link> with relation type "original".
  47. // (note the plural: we also accept pages that claim to correspond to multiple original URLs)
  48. function getOriginalUrls($headElement) {
  49. $originalUrls = [];
  50. $links = $headElement->getElementsByTagName('link');
  51. foreach ($links as $link) {
  52. $rels = explode(' ', $link->getAttribute('rel'));
  53. if (in_array('original', $rels)) {
  54. $href = $link->getAttribute('href');
  55. $href = filter_var($href, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED);
  56. if ($href) {
  57. $originalUrls[] = $href;
  58. }
  59. }
  60. }
  61. return $originalUrls;
  62. }
  63. // Read the content of the first <meta http-equiv="Memento-Datetime">, if any.
  64. function getDatetime($headElement) {
  65. $metas = $headElement->getElementsByTagName('meta');
  66. foreach($metas as $meta) {
  67. // Let's match case-insensitively, I guess?
  68. if (strtolower($meta->getAttribute('http-equiv')) === 'memento-datetime') {
  69. $datetime = $meta->getAttribute('content');
  70. $datetime = DateTime::createFromFormat(DateTime::RFC1123, $datetime)->getTimestamp();
  71. return $datetime; // Return directly at the first match
  72. }
  73. }
  74. return null;
  75. }
  76. function normaliseUrl($url) {
  77. // Ignore trailing slashes. Because everybody does.
  78. $url = rtrim($url, '/');
  79. // HACK. Replace multiple slashes with a single one. Because Nextcloud will have already done this
  80. // to the queried url (e.g. 'http://abc' arrives to us as 'http:/abc').
  81. $url = preg_replace('%/{2,}%', '/', $url);
  82. return $url;
  83. }