AO3-DL/webui/work.php

515 lines
17 KiB
PHP

<?php
# This file is part of AOMO, "Archive Of My Own", a collection of Python and PHP
# scripts designed act as an improved local backup system
# for works published on https://archiveofourown.org
#
# Copyright (c) 2025 Cyberpro123, except where otherwise noted.
#
# This project is available under the GNU General Public License (GPL) v2.0,
# available at https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html
# or in the 'LICENSE.md' file that should be distributed alongside this one.
#
# Report issues to https://codeberg.org/Cyberpro123/AOMO
# Contact author at cyberpro123@posteo.com
################################################################
# Setup
################################################################
include ".aomoCommon.php";
define("CHAPTER", (int) get($_GET, "ch", get($_ENV, "AOMO_DEFAULT_CHAPTER", 1)));
define("IMAGE_PREFIX", (string) get($_ENV, "AOMO_IMG_PREFIX", "images/"));
define("IMAGE_PREFIX_MARKER", mb_chr(59907, "UTF-8"));
if (USER_ID) {
if (get($_POST, "progress_work_id", 0)) {
sqlFetch(array(
array("INSERT INTO progress_tracker (id, user_id) VALUES (?, ?) ON CONFLICT DO NOTHING", array($_POST['progress_work_id'], USER_ID)),
array("UPDATE progress_tracker SET chapter = ?, timestamp = ? WHERE id = ? AND user_id = ?", array($_POST['progress_chapter_no'], time(), $_POST['progress_work_id'], USER_ID))
));
}
}
$sqlArray = array(
"SELECT * FROM works WHERE id = ".ID,
"SELECT title, body FROM text WHERE id = ".ID." AND chapter = 0",
);
$dbReturn = sqlFetch($sqlArray);
$rowWorks = $dbReturn[0];
if ($rowWorks) {
$title = $dbReturn[1][0]['title'];
$workskin = $dbReturn[1][0]['body'];
$rowWorks = $rowWorks[0];
$chaptersCount = $rowWorks['chapters_count'];
} else {
$title = ID;
$workskin = "";
$chaptersCount = 0;
}
################################################################
# Functions
################################################################
function displayChapter($num, $oneshot=False) {
$rowTextLocal = sqlFetch(array("SELECT * FROM text WHERE id = ".ID." AND chapter = $num"))[0][0];
$outStr = "";
$outStr .= "<div id='chapter-$num' class='chapter'>";
$outStr .= "<div class='chapter preface group'>";
if (!$oneshot) {
$outStr .= "<h3 class='title'>Chapter $num: ".$rowTextLocal["title"]."</h3>";
}
if ($rowTextLocal['summary']) {
$outStr .= "<div id='summary' class='summary module'>
<h3 class='heading'>Summary:</h3>
<blockquote class='userstuff'>";
$outStr .= str_replace(IMAGE_PREFIX_MARKER, IMAGE_PREFIX, $rowTextLocal['summary']);
$outStr .= "</blockquote></div>"; # summary
}
if ($rowTextLocal['notes_start']) {
$outStr .= "<div id='notes' class='notes module'>
<h3 class='heading'>Notes:</h3>
<blockquote class='userstuff'>";
$outStr .= str_replace(IMAGE_PREFIX_MARKER, IMAGE_PREFIX, $rowTextLocal['notes_start']);
$outStr .= "</blockquote></div>"; # notes
}
$outStr .= "</div>"; # chapter preface group
$outStr .= "<div class='userstuff module' role='article'>".str_replace(IMAGE_PREFIX_MARKER, IMAGE_PREFIX, $rowTextLocal['body'])."</div>";
if ($rowTextLocal['notes_end']) {
$outStr .= "
<div class='chapter preface group'>
<div id='chapter_".$num."_endnotes' class='end notes module'>
<h3 class='heading'>Notes:</h3>
<blockquote class='userstuff'>
".str_replace(IMAGE_PREFIX_MARKER, IMAGE_PREFIX, $rowTextLocal['notes_end'])."
</blockquote>
</div>
</div>
";
}
$outStr .= "</div>"; # chapter-num
return $outStr;
}
function chapterSelector($chaptersCount) {
#<label for='ch'>Chapter Select:</label>
$outStr = "";
$outStr .= "<form method='get' class='chapterSelector'><p>
<input type='hidden' name='id' value='".ID."'>
<select name='ch'>
<option value='0'>View All Chapters</option>";
for ($i=1; $i <= $chaptersCount; $i++) {
$outStr .= "<option value='$i'>Chapter $i</option>";
}
$outStr .= "</select><input type='submit' value='Go'></p></form>";
return $outStr;
}
function getChapterNPButtons($chaptersCount) {
$outStr = "";
if (CHAPTER > 1) {
$outStr .= "<li class='chapter previous'><a href='work.php?id=".ID."&ch=".(CHAPTER - 1)."'>← Previous Chapter</a></li>";
}
if (CHAPTER != $chaptersCount and CHAPTER != 0) {
$outStr .= "<li class='chapter next'><a href='work.php?id=".ID."&ch=".(CHAPTER + 1)."'>Next Chapter →</a></li>";
}
return $outStr;
}
function getTagsBlurb($rowWorks) {
$sqlArray = array();
for ($i=1; $i < 9; $i++) {
$sqlArray[] = "SELECT tag FROM tags WHERE id=".ID." AND category = $i ORDER BY sequence";
}
if (USER_ID) {
$sqlArray[] = "SELECT chapter FROM progress_tracker WHERE id=".ID." AND user_id=".USER_ID;
}
$dbReturn = sqlFetch($sqlArray);
$rowsTags = array();
for ($i=1; $i < 9; $i++) {
$rowsTags[$i] = harvestTags($dbReturn[$i - 1], "tag");
}
if (USER_ID) {
if ($dbReturn[8]) {
$progressNo = $dbReturn[8][0]['chapter'];
} else {
$progressNo = 0;
}
}
unset($dbReturn);
$outStr = "";
$outStr .= "<div class='wrapper'><dl class='works meta group'>";
$outStr .= "<dt class='rating tags'>Rating:</dt><dd class='rating tags'><ul class='commas'><li>".$rowsTags[6][0]."</li></ul></dd>";
if ($rowsTags[7]) {
$outStr .= "<dt class='warning tags'>Archive Warning";
if (count($rowsTags[7]) > 1) {
$outStr .= "s";
}
$outStr .= ":</dt><dd class='warning tags'><ul class='commas'>";
foreach ($rowsTags[7] as $value) {
$outStr .= "<li>$value</li>";
}
$outStr .= "</ul></dd>";
}
if ($rowsTags[8]) {
$outStr .= "<dt class='category tags'>Categor";
if (count($rowsTags[8]) > 1) {
$outStr .= "ies";
} else {
$outStr .= "y";
}
$outStr .= "</dt><dd class='category tags'><ul class='commas'>";
foreach ($rowsTags[8] as $value) {
$outStr .= "<li>$value</li>";
}
$outStr .= "</ul></dd>";
}
foreach (array(
array(1, "Fandom", "fandom"),
array(2, "Relationship", "relationship"),
array(3, "Character", "character"),
array(4, "Additional Tag", "freeform")
) as $value) {
if ($rowsTags[$value[0]]) {
$outStr .= "<dt class='$value[2] tags'>$value[1]";
if (count($rowsTags[$value[0]]) > 1) {
$outStr .= "s";
}
$outStr .= ":</dt><dd class='$value[2] tags'><ul class='commas'>";
foreach ($rowsTags[$value[0]] as $value2) {
$outStr .= "<li><a class='tag'>$value2</a></li>";
}
$outStr .= "</ul></dd>";
}
}
$outStr .= "<dt class='language'>Language:</dt><dd class='language'>".$rowsTags[5][0]."</dd>";
$rowsSeries = sqlFetch(array("SELECT series_id, pos FROM series_works WHERE id = ".ID))[0];
if ($rowsSeries) {
$outStr .= "<dt class='series'>Series:</dt><dd class='series'><ul class='commas'>";
foreach ($rowsSeries as $value) {
$outStr .= "<li>".getSeriesBlurb($value['series_id'], $value['pos'])."</li>";
}
$outStr .= "</ul></dd>";
}
$outStr .= "<dt class='stats'>Stats:</dt><dd class='stats'><dl class='stats'>";
$outStr .= "<dt class='published'>Published:</dt><dd class='published'>".date("Y-m-d", $rowWorks["date_published"])."</dd>";
if ($rowWorks["chapters_expected"] != 1) {
$outStr .= "<dt class='status'>";
if ($rowWorks["chapters_expected"] == $rowWorks["chapters_count"]) {
$outStr .= "Completed";
} else {
$outStr .= "Updated";
}
$outStr .= ":</dt><dd class='status'>".date("Y-m-d", $rowWorks["date_last_updated"])."</dd>";
}
$outStr .= "<dt class='words'>Words:</dt><dd class='words'>".$rowWorks['word_count_ao3']."<span title='This word count generated by AO3, not AOMO'>*</span></dt>";
$outStr .= "<dt class='chapters'>Chapters:</dt><dd class='chapters'>".$rowWorks["chapters_count"]."/";
if ($rowWorks["chapters_expected"] == 0) {
$outStr .= "?";
} else {
$outStr .= $rowWorks["chapters_expected"];
}
$outStr .= "</dd>";
if (USER_ID) {
$outStr .= "<dt class='progress'>Progress</dt><dd class='progress'>$progressNo/";
$outStr .= $rowWorks['chapters_count'];
$outStr .= "</dd>";
}
$outStr .= "</dl></dd>";
$outStr .= "</dl></div>"; # end header
return $outStr;
}
function getTopNavButtons($title, $chaptersCount) {
$outStr = "";
$outStr .= "<ul class='work navigation actions'>";
$outStr .= "<li><a href='https://archiveofourown.org/works/".ID."'>Link to '$title' on AO3</a></li>";
if (USER_ID) {
$outStr .= getMarkProgressButton(0, "Mark as Unread");
}
if ($chaptersCount > 1) {
$outStr .= "<li class='chapter' id='chapter_index_button'><a href='#chapter_index_button'>Chapter Selector</a><ul id='chapter_index' class='expandable secondary'><li>";
$outStr .= chapterSelector($chaptersCount);
$outStr .= "</li><li><a href='#outer'>Close</a></li></ul></li>";
$outStr .= getChapterNPButtons($chaptersCount);
}
$outStr .= "</ul>";
return $outStr;
}
function getWorkByAuthor($id, $title, $authorRows) {
$outStr = "";
$outStr .= linkToWork($id, $title);
$outStr .= " by ";
$firstTag = true;
foreach ($authorRows as $value) {
if ($firstTag) {
$firstTag = false;
} else {
$outStr .= ", ";
}
$outStr .= $value['tag'];
}
return $outStr;
}
function getInspirationBlurb($id, $relation) {
$outStr = "<li>";
if ($id) {
$dbReturn = sqlFetch(array(
array("SELECT title FROM text WHERE id = ? AND chapter = 0", array($id)),
array("SELECT tag FROM tags WHERE id = ? AND category = 5 ORDER BY sequence", array($id)),
array("SELECT tag FROM tags WHERE id = ? AND category = 9 ORDER BY sequence", array($id)),
));
if ($dbReturn[0]) {
$title = $dbReturn[0][0]['title'];
switch ($relation) {
case 0:
$outStr .= "Inspired by ";
case 1:
$outStr .= getWorkByAuthor($id, $title, $dbReturn[2]);
break;
case 2:
$outStr .= "A translation of ";
$outStr .= getWorkByAuthor($id, $title, $dbReturn[2]);
break;
case 3:
$outStr .= "Translation into ".$dbReturn[1][0]['tag']." available: ";
$outStr .= getWorkByAuthor($id, $title, $dbReturn[2]);
break;
}
} else {
$title = "Undownloaded Work $id";
switch ($relation) {
case 0:
$outStr .= "Inspired by ";
case 1:
$outStr .= "<a href='https://archiveofourown.org/works/$id'>$title</a>";
break;
case 2:
$outStr .= "A translation of ";
$outStr .= "<a href='https://archiveofourown.org/works/$id'>$title</a>";
break;
case 3:
$outStr .= "Translation into unknown language available: ";
$outStr .= "<a href='https://archiveofourown.org/works/$id'>$title</a>";
break;
}
}
} else {
switch ($relation) {
case 0:
$outStr .= "Inspired by ";
case 1:
$outStr .= "[Unknown Work]";
break;
case 2:
$outStr .= "A translation of [Unknown Work]";
break;
case 3:
$outStr .= "Translation available: [Unknown Work]";
break;
}
}
$outStr .= "</li>";
return $outStr;
}
function getTitlePrefaceGroup($title) {
$dbReturn = sqlFetch(array(
"SELECT summary, notes_start FROM text WHERE id = ".ID." AND chapter = 0",
"SELECT tag FROM tags WHERE id = ".ID." AND category = 9 ORDER BY sequence",
array("SELECT id FROM inspirations WHERE translation = 1 AND parent_id = ?", array(ID)),
array("SELECT parent_id FROM inspirations WHERE translation = 1 AND id = ?", array(ID)),
array("SELECT parent_id FROM inspirations WHERE translation = 0 AND id = ?", array(ID)),
));
$rowText = $dbReturn[0][0];
$authorStr = "";
foreach ($dbReturn[1] as $value) {
if ($authorStr) {
$authorStr .= ", ";
}
$authorStr .= $value['tag'];
}
$translations = $dbReturn[2];
$translation_of = $dbReturn[3];
$inspired_by = $dbReturn[4];
$associations = ($translations or $translation_of or $inspired_by);
$outStr = "";
$outStr .= "<div class='preface group'>
<h2 class='title heading'>$title</h2>
<h3 class='byline heading'>$authorStr</h3>
";
if (CHAPTER < 2) {
if ($rowText['summary']) {
$outStr .= "<div class='summary module'>
<h3 class='heading'>Summary:</h3>
<blockquote class='userstuff'>
".str_replace(IMAGE_PREFIX_MARKER, IMAGE_PREFIX, $rowText['summary'])."</blockquote></div>";
}
if ($rowText['notes_start'] or $associations) {
$outStr .= "<div class='notes module'><h3 class='heading'>Notes:</h3>";
if ($associations) {
$outStr .= "<ul class='associations'>";
foreach ($translation_of as $value) {
$outStr .= getInspirationBlurb($value['parent_id'], 2);
}
foreach ($translations as $value) {
$outStr .= getInspirationBlurb($value['id'], 3);
}
foreach ($inspired_by as $value) {
$outStr .= getInspirationBlurb($value['parent_id'], 0);
}
$outStr .= "</ul>";
}
if ($rowText['notes_start']) {
$outStr .= "<blockquote class='userstuff'>
".str_replace(IMAGE_PREFIX_MARKER, IMAGE_PREFIX, $rowText['notes_start'])."</blockquote>";
}
$outStr .= "</div>";
}
}
$outStr .= "</div>"; # preface group
return $outStr;
}
function getEndnotesAfterwordGroup() {
$dbReturn = sqlFetch(array(
array("SELECT notes_end FROM text WHERE id = ? AND chapter = 0", array(ID)),
array("SELECT id FROM inspirations WHERE translation = 0 AND parent_id = ?", array(ID)),
));
if ($dbReturn[0]) {
$endnotes = $dbReturn[0][0]['notes_end'];
} else {
$endnotes = "";
}
$inspirations = $dbReturn[1];
$outStr = "";
if ($endnotes or $inspirations) {
$outStr .= "<div class='afterword preface group'>";
if ($endnotes) {
$outStr .= "<div id='work_endnotes' class='end notes module'>
<h3 class='heading'>Notes:</h3>
<blockquote class='userstuff'>".str_replace(IMAGE_PREFIX_MARKER, IMAGE_PREFIX, $endnotes)."</blockquote>
</div>";
}
if ($inspirations) {
$outStr .= "<div id='children' class='children module'><h3 class='heading'>Works inspired by this one:</h3><ul>";
foreach ($inspirations as $value) {
$outStr .= getInspirationBlurb($value['id'], 1);
}
$outStr .= "</ul></div>";
}
$outStr .= "</div>";
}
return $outStr;
}
function getMarkProgressButton($chapter, $prompt="Mark as Read") {
$outStr = "";
$outStr .= "<li><form method='post'>";
$outStr .= "<input type='hidden' name='progress_work_id' value='".ID."'>";
$outStr .= "<input type='hidden' name='progress_chapter_no' value='$chapter'>";
$outStr .= "<input type='submit' value='$prompt'>";
$outStr .= "</form></li>";
return $outStr;
}
################################################################
# Error Handling
################################################################
if ($chaptersCount == 0) {
# Requested work not in database
$titleStr = "Invalid Work ID - AOMO";
http_response_code(404);
$error = 1;
} elseif ($chaptersCount < CHAPTER or CHAPTER < 0) {
# Requested chapter we don't have
$titleStr = "Invalid Chapter ID - AOMO";
http_response_code(404);
$error = 2;
} else {
# Normal Operation
$titleStr = "$title - ".ID." - AOMO";
$error = 0;
}
################################################################
# Main
################################################################
echo getSiteMainLead($titleStr, $workskin);
################################################################
# HTML Body
################################################################
if ($error == 1) {
# Requested work not in database
echo "
<div id='errorPage'>
<h1>Error 404</h1>
<h2>Work ID ".ID." not in database</h2>
<p><a href='https://archiveofourown.org/works/".ID."'>View on AO3</a></p>
</div>
";
} elseif ($error == 2) {
# Requested chapter we don't have
echo "
<div id='errorPage'>
<h1>Error 404</h1>
<h2>Work <span class='workTitle'>'$title'</span> doesn't have a chapter ".CHAPTER."</h2>";
#if ($chaptersCount == 1) {
# # Oneshot
# echo "<li><a href='work.php?id=$id&ch=1'>Go to chapter 1</a></li>";
#} else {
echo chapterSelector($chaptersCount);
#}
echo "
<p><a href='https://archiveofourown.org/works/".ID."'>View on AO3</a></p>
</div>
";
} else {
# Normal Operation
echo getTopNavButtons($title, $chaptersCount);
echo "<h3 class='landmark heading'>Work Header</h3>";
echo getTagsBlurb($rowWorks);
echo "<div id='workskin'>";
echo getTitlePrefaceGroup($title);
echo "<div id='chapters' role='article'>";
$oneshot = $chaptersCount == 1;
if (CHAPTER == 0) {
for ($i = 1; $i <= $chaptersCount; $i++) {
echo displayChapter($i, $oneshot);
}
} else {
echo displayChapter(CHAPTER, $oneshot);
}
echo "</div>";
echo getEndnotesAfterwordGroup();
echo "<ul class='actions'>";
if (USER_ID) {
if (CHAPTER == 0) {
echo getMarkProgressButton($chapters_count);
} else {
echo getMarkProgressButton(CHAPTER);
}
}
echo getChapterNPButtons($chaptersCount);
echo "</ul>";
}
echo getSiteMainEnd();
?>