From e1ae5556a9aa6369a05d48b26a7ecbd5db6d0f7e Mon Sep 17 00:00:00 2001 From: Matt Jenkins Date: Thu, 4 Jun 2026 16:30:29 +0100 Subject: [PATCH] Added archive.org upload --- app/controllers/ArchiveController.php | 17 ++++++ app/jobs/ArchiveJob.php | 66 ++++++++++++++++++++ lib/Archive.php | 87 +++++++++++++++++++++++++++ lib/HTTPRequest.php | 52 ++++++++++++++++ lib/Model.php | 6 ++ routes/web.php | 11 ++-- views/document.blade.php | 31 +++++++--- 7 files changed, 254 insertions(+), 16 deletions(-) create mode 100644 app/controllers/ArchiveController.php create mode 100644 app/jobs/ArchiveJob.php create mode 100644 lib/Archive.php diff --git a/app/controllers/ArchiveController.php b/app/controllers/ArchiveController.php new file mode 100644 index 0000000..092f8fa --- /dev/null +++ b/app/controllers/ArchiveController.php @@ -0,0 +1,17 @@ +id); + $job->document = $doc->id; + $job->queue(); + } + return redirect("/document/" . $doc->id); + } +} diff --git a/app/jobs/ArchiveJob.php b/app/jobs/ArchiveJob.php new file mode 100644 index 0000000..e4ba33b --- /dev/null +++ b/app/jobs/ArchiveJob.php @@ -0,0 +1,66 @@ +document); + + $meta = []; + $meta['title'] = sprintf("uri(\"%s\")", rawurlencode(trim($doc->title . " " . $doc->subtitle . " " . $doc->subsubtitle))); + $meta['creator'] = "Digital Equipment Corporation"; + $meta['description'] = sprintf("uri(\"%s\")", rawurlencode($doc->overview_md())); + + $doc->load("revisions"); + + $doc->archive = "P"; + + $worked = 0; + + $id = $doc->internal_id; + if (Config::get("SANDBOX")) { + $id = sprintf("decpdf_test_%s_%08d", $id, time()); + } + + $this->status($id); + $doc->load("revisions"); + + foreach ($doc->revisions as $r) { + if ($r->archived == "N") { + try { + $this->status("Uploading " . $r->filename()); + $r->archived = "P"; + $r->save(); + Archive::upload($r->getPDF(), $id, $r->filename(), $meta); + $r->refresh(); + $r->archived = "Y"; + $r->save(); + $worked++; + } catch (Exception $e) { + $r->refresh(); + $r->archived = "F"; + $r->save(); + $this->status($e->getMessage()); + } + } + } + + if ($worked == 0) { + $doc->refresh(); + $doc->archived = "F"; + $doc->save(); + $this->fail("No files uploaded"); + return; + } else { + $doc->refresh(); + $doc->archived = "Y"; + $doc->archive_url = $id; + $doc->save(); + } + + $this->finish(); + } + +} diff --git a/lib/Archive.php b/lib/Archive.php new file mode 100644 index 0000000..dc32807 --- /dev/null +++ b/lib/Archive.php @@ -0,0 +1,87 @@ +get("https://archive.org/services/tasks.php?summary=1&catalog=1&identifier=" . $identifier, [ + "Authorization: LOW " . Config::get("ARCHIVE_KEY") . ":" . Config::get("ARCHIVE_SECRET") + ]); + } else { + $resp = $req->get("https://archive.org/services/tasks.php?summary=1&catalog=1", [ + "Authorization: LOW " . Config::get("ARCHIVE_KEY") . ":" . Config::get("ARCHIVE_SECRET") + ]); + } + + if ($resp == 200) { + $j = json_decode($req->body); + if ($j) { + return $j; + } + } + + throw new Exception("Error $resp processing tasks"); + } + + static function upload(File $file, $identifier, $filename, $metadata = []) { + + $headers = []; + $headers[] = "Authorization: LOW " . Config::get("ARCHIVE_KEY") . ":" . Config::get("ARCHIVE_SECRET"); + $headers[] = "x-archive-auto-make-bucket:1"; + $headers[] = "x-archive-queue-derive:0"; + $headers[] = "x-archive-interactive-priority:1"; + + $metadata["mediatype"] = "texts"; + if (Config::get("SANDBOX")) { + $metadata["collection"] = "test_collection"; + } else { + $metadata["collection"] = "opensource"; + } + + foreach ($metadata as $k=>$v) { + if (is_array($v)) { + $i = 1; + foreach ($v as $q) { + $headers[] = sprintf("x-archive-meta-%02d-%s:%s", $i, str_replace("_", "--", $k), $q); + $i++; + } + } else { + $headers[] = sprintf("x-archive-meta-%s:%s", str_replace("_", "--", $k), $v); + } + } + + + $req = new HTTPRequest(); + $resp = $req->put("https://s3.us.archive.org/" . $identifier . "/" . $filename, $file->path(), $headers); + + if ($resp == 200) { + return true; + } + throw new Exception($resp->body); + } + + static function metadata($identifier) { + + $req = new HTTPRequest(); + + $resp = $req->get("https://archive.org/metadata/" . $identifier, [ + "Authorization: LOW " . Config::get("ARCHIVE_KEY") . ":" . Config::get("ARCHIVE_SECRET") + ]); + + if ($resp == 200) { + $j = json_decode($req->body); + if ($j) { + return $j; + } + } + + throw new Exception("Error " . $resp . " during metadata get"); + } + +} diff --git a/lib/HTTPRequest.php b/lib/HTTPRequest.php index d367930..b1164ca 100644 --- a/lib/HTTPRequest.php +++ b/lib/HTTPRequest.php @@ -37,6 +37,8 @@ class HTTPRequest { $this->body = substr($response, $header_size); $this->headers = []; + $hl = explode("\n", $header); + foreach ($hl as $h) { $h = trim($h); if (preg_match('/^HTTP\/([^\s]+)\s+(\d+)\s*/', $h, $m)) { @@ -109,4 +111,54 @@ class HTTPRequest { } + public function put($url, $file, $headers = [], $headersonly = false) { + + $fh = fopen($file, "r"); + + curl_reset($this->ch); + curl_setopt($this->ch, CURLOPT_URL, $url); + curl_setopt($this->ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:138.0) Gecko/20100101 Firefox/138.0"); + curl_setopt($this->ch, CURLOPT_PUT, true); + curl_setopt($this->ch, CURLOPT_FILETIME, true); + curl_setopt($this->ch, CURLOPT_CONNECTTIMEOUT, $this->connect_timeout); + curl_setopt($this->ch, CURLOPT_TIMEOUT, $this->transfer_timeout); + curl_setopt($this->ch, CURLOPT_HTTPHEADER, $headers); + curl_setopt($this->ch, CURLOPT_NOBODY, $headersonly); + curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($this->ch, CURLOPT_HEADER, true); + curl_setopt($this->ch, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($this->ch, CURLOPT_INFILE, $fh); + curl_setopt($this->ch, CURLOPT_INFILESIZE, filesize($file)); + + $this->status = 0; + + $response = curl_exec($this->ch); + + $header_size = curl_getinfo($this->ch, CURLINFO_HEADER_SIZE); + $header = substr($response, 0, $header_size); + $this->body = substr($response, $header_size); + $this->headers = []; + + + $hl = explode("\n", $header); + + foreach ($hl as $h) { + $h = trim($h); + if (preg_match('/^HTTP\/([^\s]+)\s+(\d+)\s*/', $h, $m)) { + $this->version = $m[1]; + $this->status = $m[2]; + continue; + } + if (str_starts_with($h, " ")) { + $this->headers[$curr] .= " " . trim($h); + continue; + } + if (preg_match('/^([^:]+):\s+(.*)$/', $h, $m)) { + $curr = strtolower($m[1]); + $this->headers[$curr] = $m[2]; + } + } + + return $this->status; + } } diff --git a/lib/Model.php b/lib/Model.php index 517a0bd..b43de4a 100755 --- a/lib/Model.php +++ b/lib/Model.php @@ -82,6 +82,12 @@ class Model implements JsonSerializable { Model::$_cache->addServer($host, $port, $weight); } + public function refresh() { + if ($this->id) { + $this->_load_record($this->id); + } + } + private function _load_record($id) { $class = get_called_class(); diff --git a/routes/web.php b/routes/web.php index 08363a1..c0a4a5e 100644 --- a/routes/web.php +++ b/routes/web.php @@ -41,12 +41,6 @@ Routes::add_web("GET", "/merge/{id}", ["DocumentController", "merge"], ["Auth", Routes::add_web("GET", "/explode/{id}", ["DocumentController", "separate"], ["Auth", "can_moderate"]); -Routes::add_web("GET", "/test", function($_request) { - print("
");
-	print_r($_request);
-	exit(0);
-});
-
 
 Routes::add_web("GET", "/privacy", function() { return blade("privacy"); });
 
@@ -108,3 +102,8 @@ Routes::add_web("GET", "/system/{id}/add/{doc}", ["SystemController", "system_ad
 Routes::add_web("GET", "/system/{id}/del/{doc}", ["SystemController", "system_del_doc"], ["Auth", "logged_in"]);
 
 Routes::add_web("GET", "/extract/revision/{id}", ["RevisionController", "split_revision"], ["Auth", "can_moderate"]);
+
+
+
+ArchiveController::routes();
+
diff --git a/views/document.blade.php b/views/document.blade.php
index 2ddf95c..cee9a8d 100644
--- a/views/document.blade.php
+++ b/views/document.blade.php
@@ -34,20 +34,31 @@
 

{{ $doc->subtitle }}

{{ $doc->subsubtitle }}

+
- @if (Auth::can_moderate()) -
- - - - - - -
- @endif + @if (Auth::can_moderate()) +
+ + + + + + + +
+ @endif
Order Number: {{ $doc->internal_id }}
+ @if ($doc->archived == "Y") + + @endif + @if (Auth::can_moderate())