TimeGateController.php 2.3 KiB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  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. * @NoAdminRequired
  24. * @NoCSRFRequired
  25. */
  26. public function timeGate($url) {
  27. $matchingMementos = findMementos($this->userFolder, $url);
  28. // Choose one of the matched mementos, if any.
  29. if (count($matchingMementos) === 0) {
  30. // No matches. :(
  31. $message = "<h1>No snapshots found for requested URL. :(</h1>";
  32. return new DataDisplayResponse($message, 404);
  33. } else if (count($matchingMementos) === 1) {
  34. // One match; no need to choose.
  35. $chosenMemento = $matchingMementos[0];
  36. } else {
  37. // Multiple matches: choose based on requested date.
  38. $acceptDatetimeHeader = $this->request->getHeader('Accept-Datetime');
  39. if ($acceptDatetimeHeader) {
  40. try {
  41. $requestedDatetime = DateTime::createFromFormat(DateTime::RFC1123, $acceptDatetimeHeader)
  42. ->getTimestamp();
  43. } catch (Exception $e) {
  44. return new DataDisplayResponse("Invalid Accept-Datetime header.", 400);
  45. }
  46. } else {
  47. // Not sending the header means requesting the most recent version.
  48. $requestedDatetime = time();
  49. }
  50. // Pick the one closest to the requested date (either before or after it).
  51. $chosenMemento = minBy($matchingMementos,
  52. function ($matchingMemento) use ($requestedDatetime) {
  53. return abs($matchingMemento['datetime'] - $requestedDatetime);
  54. }
  55. );
  56. }
  57. // Send a 302 Found redirect pointing to the chosen memento.
  58. $response = new RedirectResponse($chosenMemento['mementoUrl']);
  59. $response->setStatus(302);
  60. $response->addHeader('Vary', 'accept-datetime');
  61. $response->addHeader('Link', "<{$chosenMemento['originalUrl']}>; rel=\"original\"");
  62. return $response;
  63. }
  64. }
  65. function minBy($array, $iteratee) {
  66. // is there any simpler way for this in php?
  67. $values = array_map($iteratee, $array);
  68. $argmin = array_search(min($values), $values);
  69. return $array[$argmin];
  70. }