From b459e462a5c3447d0693674253c40bc7385a4f66 Mon Sep 17 00:00:00 2001 From: Niki Roo Date: Wed, 13 May 2020 14:26:22 +0200 Subject: [PATCH] Update from master --- Instance.java | 5 + library/BasicLibrary.java | 78 +++++++- library/WebLibraryServer.java | 347 ++++++++++++++++++++++++---------- library/web/clear-32x32.png | Bin 0 -> 1848 bytes library/web/clear.png | Bin 0 -> 1698 bytes library/web/style.css | 260 +++++++++++++++++++------ 6 files changed, 523 insertions(+), 167 deletions(-) create mode 100644 library/web/clear-32x32.png create mode 100644 library/web/clear.png diff --git a/Instance.java b/Instance.java index a2cb90a..e0a0727 100644 --- a/Instance.java +++ b/Instance.java @@ -363,6 +363,11 @@ public class Instance { remoteDir.mkdirs(); if (host != null) { + host = host.replace("fanfix://", ""); + host = host.replace("http://", ""); + host = host.replace("https://", ""); + host = host.replaceAll("[^a-zA-Z0-9=+.-]", "_"); + return new File(remoteDir, host); } diff --git a/library/BasicLibrary.java b/library/BasicLibrary.java index d435f8d..78f0f62 100644 --- a/library/BasicLibrary.java +++ b/library/BasicLibrary.java @@ -4,11 +4,9 @@ import java.io.File; import java.io.IOException; import java.net.URL; import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.TreeMap; import be.nikiroo.fanfix.Instance; import be.nikiroo.fanfix.data.MetaData; @@ -122,12 +120,24 @@ abstract public class BasicLibrary { */ public abstract Image getCover(String luid) throws IOException; - // TODO: ensure it is the main used interface + /** + * Retrieve the list of {@link MetaData} known by this {@link BasicLibrary} + * in a easy-to-filter version. + * + * @param pg + * the optional {@link Progress} + * @return the list of {@link MetaData} as a {@link MetaResultList} you can + * query + * @throws IOException + * in case of I/O eror + */ public MetaResultList getList(Progress pg) throws IOException { + // TODO: ensure it is the main used interface + return new MetaResultList(getMetas(pg)); } - // TODO: make something for (normal and custom) not-story covers + // TODO: make something for (normal and custom) non-story covers /** * Return the cover image associated to this source. @@ -378,6 +388,9 @@ abstract public class BasicLibrary { } /** + * @return the same as getList() + * @throws IOException + * in case of I/O error * @deprecated please use {@link BasicLibrary#getList()} and * {@link MetaResultList#getSources()} instead. */ @@ -387,6 +400,9 @@ abstract public class BasicLibrary { } /** + * @return the same as getList() + * @throws IOException + * in case of I/O error * @deprecated please use {@link BasicLibrary#getList()} and * {@link MetaResultList#getSourcesGrouped()} instead. */ @@ -396,6 +412,9 @@ abstract public class BasicLibrary { } /** + * @return the same as getList() + * @throws IOException + * in case of I/O error * @deprecated please use {@link BasicLibrary#getList()} and * {@link MetaResultList#getAuthors()} instead. */ @@ -405,9 +424,13 @@ abstract public class BasicLibrary { } /** + * @return the same as getList() + * @throws IOException + * in case of I/O error * @deprecated please use {@link BasicLibrary#getList()} and * {@link MetaResultList#getAuthorsGrouped()} instead. */ + @Deprecated public Map> getAuthorsGrouped() throws IOException { return getList().getAuthorsGrouped(); } @@ -901,4 +924,49 @@ abstract public class BasicLibrary { pg.done(); } + + /** + * Describe a {@link Story} from its {@link MetaData} and return a list of + * title/value that represent this {@link Story}. + * + * @param meta + * the {@link MetaData} to represent + * + * @return the information, translated and sorted + */ + static public Map getMetaDesc(MetaData meta) { + Map metaDesc = new LinkedHashMap(); + + // TODO: i18n + + StringBuilder tags = new StringBuilder(); + for (String tag : meta.getTags()) { + if (tags.length() > 0) { + tags.append(", "); + } + tags.append(tag); + } + + // TODO: i18n + metaDesc.put("Author", meta.getAuthor()); + metaDesc.put("Published on", meta.getPublisher()); + metaDesc.put("Publication date", meta.getDate()); + metaDesc.put("Creation date", meta.getCreationDate()); + String count = ""; + if (meta.getWords() > 0) { + count = StringUtils.formatNumber(meta.getWords()); + } + if (meta.isImageDocument()) { + metaDesc.put("Number of images", count); + } else { + metaDesc.put("Number of words", count); + } + metaDesc.put("Source", meta.getSource()); + metaDesc.put("Subject", meta.getSubject()); + metaDesc.put("Language", meta.getLang()); + metaDesc.put("Tags", tags.toString()); + metaDesc.put("URL", meta.getUrl()); + + return metaDesc; + } } diff --git a/library/WebLibraryServer.java b/library/WebLibraryServer.java index 3e2c906..b4a6e4b 100644 --- a/library/WebLibraryServer.java +++ b/library/WebLibraryServer.java @@ -242,8 +242,10 @@ public class WebLibraryServer implements Runnable { // set options String optionName = params.get("optionName"); if (optionName != null && !optionName.isEmpty()) { + String optionNo = params.get("optionNo"); String optionValue = params.get("optionValue"); - if (optionValue == null || optionValue.isEmpty()) { + if (optionNo != null || optionValue == null + || optionValue.isEmpty()) { session.getCookies().delete(optionName); cookies.remove(optionName); } else { @@ -491,12 +493,15 @@ public class WebLibraryServer implements Runnable { appendPreHtml(builder, true); + Map params = session.getParms(); + String filter = cookies.get("filter"); + if (params.get("optionNo") != null) + filter = null; if (filter == null) { filter = ""; } - Map params = session.getParms(); String browser = params.get("browser") == null ? "" : params.get("browser"); String browser2 = params.get("browser2") == null ? "" @@ -585,11 +590,12 @@ public class WebLibraryServer implements Runnable { // TODO: javascript in realtime, using visible=false + hide [submit] builder.append("
\n"); - builder.append("\tFilter: \n"); + builder.append("\tFilter: \n"); builder.append( "\t\n"); builder.append("\t\n"); + builder.append("\t"); builder.append( "\t\n"); builder.append("
\n"); @@ -672,7 +678,8 @@ public class WebLibraryServer implements Runnable { // 1-based (0 = desc) int chapter = 0; if (chapterStr != null && !"cover".equals(chapterStr) - && !"metadata".equals(chapterStr) && !"json".equals(chapterStr)) { + && !"metadata".equals(chapterStr) + && !"json".equals(chapterStr)) { try { chapter = Integer.parseInt(chapterStr); if (chapter < 0) { @@ -706,12 +713,14 @@ public class WebLibraryServer implements Runnable { if (img != null) { in = img.newInputStream(); } + // TODO: get correct image type + mimeType = "image/png"; } else if ("metadata".equals(chapterStr)) { MetaData meta = meta(luid, whitelist); JSONObject json = JsonIO.toJson(meta); mimeType = "application/json"; in = new ByteArrayInputStream(json.toString().getBytes()); - } else if ("json".equals(chapterStr)) { + } else if ("json".equals(chapterStr)) { Story story = story(luid, whitelist); JSONObject json = JsonIO.toJson(story); mimeType = "application/json"; @@ -812,123 +821,213 @@ public class WebLibraryServer implements Runnable { StringBuilder builder = new StringBuilder(); appendPreHtml(builder, false); - if (chapter < 0) { - builder.append(story); - } else { - if (chapter == 0) { - // TODO: description - chapter = 1; - } + // For images documents, always go to the images if not chap 0 desc + if (story.getMeta().isImageDocument()) { + if (chapter > 0 && paragraph <= 0) + paragraph = 1; + } - Chapter chap = null; + Chapter chap = null; + if (chapter <= 0) { + chap = story.getMeta().getResume(); + } else { try { chap = story.getChapters().get(chapter - 1); } catch (IndexOutOfBoundsException e) { return NanoHTTPD.newFixedLengthResponse(Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, "Chapter not found"); } + } - if (story.getMeta().isImageDocument() && paragraph <= 0) { - paragraph = 1; + String first, previous, next, last; + + StringBuilder content = new StringBuilder(); + + String disabledLeft = ""; + String disabledRight = ""; + String disabledZoomReal = ""; + String disabledZoomWidth = ""; + String disabledZoomHeight = ""; + + if (paragraph <= 0) { + first = getViewUrl(luid, 0, null); + previous = getViewUrl(luid, (Math.max(chapter - 1, 0)), null); + next = getViewUrl(luid, + (Math.min(chapter + 1, story.getChapters().size())), + null); + last = getViewUrl(luid, story.getChapters().size(), null); + + StringBuilder desc = new StringBuilder(); + + if (chapter <= 0) { + desc.append("

"); + desc.append(story.getMeta().getTitle()); + desc.append("

\n"); + desc.append("
\n"); + desc.append("\t
\n"); + desc.append("\t\t\n"); + desc.append("\t
\n"); + desc.append("\t\n"); + Map details = BasicLibrary + .getMetaDesc(story.getMeta()); + for (String key : details.keySet()) { + appendTableRow(desc, 2, key, details.get(key)); + } + desc.append("\t
\n"); + desc.append("
\n"); + desc.append("

Description

\n"); } - String first, previous, next, last; - String content; + content.append("
\n"); + content.append(desc); + String description = new TextOutput(false).convert(chap, + chapter > 0); + content.append(chap.getParagraphs().size() <= 0 + ? "No content provided." + : description); + content.append("
\n"); + + if (chapter <= 0) + disabledLeft = " disabled='disbaled'"; + if (chapter >= story.getChapters().size()) + disabledRight = " disabled='disbaled'"; + } else { + first = getViewUrl(luid, chapter, 1); + previous = getViewUrl(luid, chapter, + (Math.max(paragraph - 1, 1))); + next = getViewUrl(luid, chapter, + (Math.min(paragraph + 1, chap.getParagraphs().size()))); + last = getViewUrl(luid, chapter, chap.getParagraphs().size()); + + if (paragraph <= 1) + disabledLeft = " disabled='disbaled'"; + if (paragraph >= chap.getParagraphs().size()) + disabledRight = " disabled='disbaled'"; + + // First -> previous *chapter* + if (chapter > 0) + disabledLeft = ""; + first = getViewUrl(luid, (Math.max(chapter - 1, 0)), null); + if (paragraph <= 1) { + previous = first; + } - if (paragraph <= 0) { - first = getViewUrl(luid, 1, null); - previous = getViewUrl(luid, (Math.max(chapter - 1, 1)), - null); - next = getViewUrl(luid, - (Math.min(chapter + 1, story.getChapters().size())), - null); - last = getViewUrl(luid, story.getChapters().size(), null); - - content = "
\n" - + new TextOutput(false).convert(chap, true) - + "
\n"; - } else { - first = getViewUrl(luid, chapter, 1); - previous = getViewUrl(luid, chapter, - (Math.max(paragraph - 1, 1))); - next = getViewUrl(luid, chapter, (Math.min(paragraph + 1, - chap.getParagraphs().size()))); - last = getViewUrl(luid, chapter, - chap.getParagraphs().size()); - - Paragraph para = null; - try { - para = chap.getParagraphs().get(paragraph - 1); - } catch (IndexOutOfBoundsException e) { - return NanoHTTPD.newFixedLengthResponse( - Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, - "Paragraph not found"); - } + Paragraph para = null; + try { + para = chap.getParagraphs().get(paragraph - 1); + } catch (IndexOutOfBoundsException e) { + return NanoHTTPD.newFixedLengthResponse(Status.NOT_FOUND, + NanoHTTPD.MIME_PLAINTEXT, + "Paragraph " + paragraph + " not found"); + } - if (para.getType() == ParagraphType.IMAGE) { - String zoomStyle = "max-width: 100%;"; - String zoomOption = cookies.get("zoom"); - if (zoomOption != null && !zoomOption.isEmpty()) { - if (zoomOption.equals("real")) { - zoomStyle = ""; - } else if (zoomOption.equals("width")) { - zoomStyle = "max-width: 100%;"; - } else if (zoomOption.equals("height")) { - // see height of navbar + optionbar - zoomStyle = "max-height: calc(100% - 128px);"; - } + if (para.getType() == ParagraphType.IMAGE) { + String zoomStyle = "max-width: 100%;"; + disabledZoomWidth = " disabled='disabled'"; + String zoomOption = cookies.get("zoom"); + if (zoomOption != null && !zoomOption.isEmpty()) { + if (zoomOption.equals("real")) { + zoomStyle = ""; + disabledZoomWidth = ""; + disabledZoomReal = " disabled='disabled'"; + } else if (zoomOption.equals("width")) { + zoomStyle = "max-width: 100%;"; + } else if (zoomOption.equals("height")) { + // see height of navbar + optionbar + zoomStyle = "max-height: calc(100% - 128px);"; + disabledZoomWidth = ""; + disabledZoomHeight = " disabled='disabled'"; } - content = String.format("" // - + "" // - + "" - + "", // - next, // - zoomStyle, // - getStoryUrl(luid, chapter, paragraph)); - } else { - content = para.getContent(); } + content.append(String.format("" // + + "" + + "" + + "", // + next, // + zoomStyle, // + getStoryUrl(luid, chapter, paragraph))); + } else { + content.append(String.format("" // + + "
%s
", // + para.getContent())); } + } - builder.append(String.format("" // - + "\n" // - + "%s", // - first, // - previous, // - next, // - last, // - content // - )); - - builder.append("
\n" // + + "\t<<\n"// + + "\t<\n" // + + "\t
\n" // + + "\t\t
%d
\n" // + + "\t\t
\n", // + disabledLeft, first, // + disabledLeft, previous, // + paragraph > 0 ? paragraph : chapter // + )); + + // List of chap/para links + + String blink = "/view/story/" + luid + "/"; + appendItemA(builder, 3, blink + "0", "Description", + paragraph == 0 && chapter == 0); + + if (paragraph > 0) { + blink = blink + chapter + "/"; + for (int i = 1; i <= chap.getParagraphs().size(); i++) { + appendItemA(builder, 3, blink + i, "Image " + i, + paragraph == i); } - builder.append("'>\n"); - builder.append( - " BACK\n"); - - if (paragraph > 0) { - builder.append(String.format("" // - + "\tREAL\n"// - + "\tWIDTH\n"// - + "\tHEIGHT\n"// - + "
\n", // - uri + "?optionName=zoom&optionValue=real", // - uri + "?optionName=zoom&optionValue=width", // - uri + "?optionName=zoom&optionValue=height" // - )); + } else { + int i = 1; + for (Chapter c : story.getChapters()) { + String chapName = "Chapter " + c.getNumber(); + if (c.getName() != null && !c.getName().isEmpty()) { + chapName += ": " + c.getName(); + } + + appendItemA(builder, 3, blink + i, chapName, chapter == i); + + i++; } } + builder.append(String.format("" // + + "\t\t
\n" // + + "\t
\n" // + + "\t>\n" // + + "\t>>\n"// + + "\n", // + disabledRight, next, // + disabledRight, last // + )); + + builder.append(content); + + builder.append("
\n"); + builder.append(" BACK\n"); + + if (paragraph > 0) { + builder.append(String.format("" // + + "\tREAL\n"// + + "\tWIDTH\n"// + + "\tHEIGHT\n"// + + "
\n", // + disabledZoomReal, + uri + "?optionName=zoom&optionValue=real", // + disabledZoomWidth, + uri + "?optionName=zoom&optionValue=width", // + disabledZoomHeight, + uri + "?optionName=zoom&optionValue=height" // + )); + } + appendPostHtml(builder); return NanoHTTPD.newFixedLengthResponse(Status.OK, NanoHTTPD.MIME_HTML, builder.toString()); @@ -998,7 +1097,8 @@ public class WebLibraryServer implements Runnable { throws IOException { MetaData meta = meta(luid, whitelist); if (meta != null) { - return meta.getCover(); + BasicLibrary lib = Instance.getInstance().getLibrary(); + return lib.getCover(meta.getLuid()); } return null; @@ -1073,7 +1173,7 @@ public class WebLibraryServer implements Runnable { if (banner) { builder.append("