nextcloud-memento/lib/Controller/ TimeGateController.php
82 lines
2.4 KiB

  1. <?php
  2. namespace OCA\Memento\Controller;
  3. require_once __DIR__ . '/findMementos.php';
  4. use DateTime;
  5. use OCP\IRequest;
  6. use OCP\IServerContainer;
  7. use OCP\AppFramework\Controller;
  8. use OCP\AppFramework\Http\RedirectResponse;
  9. use OCP\AppFramework\Http\DataDisplayResponse;
  10. use findMementos;
  11. class TimeGateController extends Controller {
  12. private $userFolder;
  13. public function __construct(
  14. $AppName,
  15. IRequest $request,
  16. $UserId,
  17. IServerContainer $serverContainer
  18. ) {
  19. parent::__construct($AppName, $request);
  20. $this->userFolder = $serverContainer->getUserFolder($UserId);
  21. }
  22. /**
  23. * @PublicPage
  24. * @NoAdminRequired
  25. * @NoCSRFRequired
  26. */
  27. public function timeGate($url) {
  28. $matchingMementos = findMementos($this->userFolder, $url);
  29. // Choose one of the matched mementos, if any.
  30. if (count($matchingMementos) === 0) {
  31. // No matches. :(
  32. $message = "<h1>No snapshots found for requested URL. :(</h1>";
  33. return new DataDisplayResponse($message, 404);
  34. } else if (count($matchingMementos) === 1) {
  35. // One match; no need to choose.
  36. $chosenMemento = $matchingMementos[0];
  37. } else {
  38. // Multiple matches: choose based on requested date.
  39. $acceptDatetimeHeader = $this->request->getHeader('Accept-Datetime');
  40. if ($acceptDatetimeHeader) {
  41. try {
  42. $requestedDatetime = DateTime::createFromFormat(DateTime::RFC1123, $acceptDatetimeHeader)
  43. ->getTimestamp();
  44. } catch (Exception $e) {
  45. return new DataDisplayResponse("Invalid Accept-Datetime header.", 400);
  46. }
  47. } else {
  48. // Not sending the header means requesting the most recent version.
  49. $requestedDatetime = time();
  50. }
  51. // Pick the one closest to the requested date (either before or after it).
  52. $chosenMemento = minBy($matchingMementos,
  53. function ($matchingMemento) use ($requestedDatetime) {
  54. return abs($matchingMemento['datetime'] - $requestedDatetime);
  55. }
  56. );
  57. }
  58. // Send a 302 Found redirect pointing to the chosen memento.
  59. $response = new RedirectResponse($chosenMemento['mementoUrl']);
  60. $response->setStatus(302);
  61. $response->addHeader('Vary', 'accept-datetime');
  62. $response->addHeader('Link', "<{$chosenMemento['originalUrl']}>; rel=\"original\"");
  63. return $response;
  64. }
  65. }
  66. function minBy($array, $iteratee) {
  67. // is there any simpler way for this in php?
  68. $values = array_map($iteratee, $array);
  69. $argmin = array_search(min($values), $values);
  70. return $array[$argmin];
  71. }