From 51d19328be481deff569a72fae2805bd6766f723 Mon Sep 17 00:00:00 2001 From: Alex Ling Date: Fri, 27 Mar 2020 04:19:21 +0000 Subject: [PATCH 01/29] Set up API endpoint for setting display names --- src/library.cr | 90 +++++++++++++++++++++++++++++++++-------------- src/routes/api.cr | 23 ++++++++++++ 2 files changed, 87 insertions(+), 26 deletions(-) diff --git a/src/library.cr b/src/library.cr index 2ce3ce1..1eab5f4 100644 --- a/src/library.cr +++ b/src/library.cr @@ -15,12 +15,12 @@ struct Image end class Entry - property zip_path : String, book_title : String, title : String, + property zip_path : String, book : Title, title : String, size : String, pages : Int32, cover_url : String, id : String, title_id : String, encoded_path : String, encoded_title : String, mtime : Time - def initialize(path, @book_title, @title_id, storage) + def initialize(path, @book, @title_id, storage) @zip_path = path @encoded_path = URI.encode path @title = File.basename path, File.extname path @@ -41,11 +41,11 @@ class Entry def to_json(json : JSON::Builder) json.object do - {% for str in ["zip_path", "book_title", "title", "size", - "cover_url", "id", "title_id", "encoded_path", - "encoded_title"] %} + {% for str in ["zip_path", "title", "size", "cover_url", "id", + "title_id", "encoded_path", "encoded_title"] %} json.field {{str}}, @{{str.id}} {% end %} + json.field "display_name", @book.display_name @title json.field "pages" {json.number @pages} json.field "mtime" {json.number @mtime.to_unix} end @@ -80,14 +80,14 @@ class Title entries : Array(Entry), title : String, id : String, encoded_title : String, mtime : Time - def initialize(dir : String, @parent_id, storage, + def initialize(@dir : String, @parent_id, storage, @logger : MLogger, @library : Library) - @dir = dir @id = storage.get_id @dir, true @title = File.basename dir @encoded_title = URI.encode @title @title_ids = [] of String @entries = [] of Entry + @mtime = File.info(dir).modification_time Dir.entries(dir).each do |fn| next if fn.starts_with? "." @@ -101,11 +101,16 @@ class Title end if [".zip", ".cbz"].includes? File.extname path next if !valid_zip path - entry = Entry.new path, @title, @id, storage + entry = Entry.new path, self, @id, storage @entries << entry if entry.pages > 0 end end + mtimes = [@mtime] + mtimes += @title_ids.map{|e| @library.title_hash[e].mtime} + mtimes += @entries.map{|e| e.mtime} + @mtime = mtimes.max + @title_ids.sort! do |a, b| compare_alphanumerically @library.title_hash[a].title, @library.title_hash[b].title @@ -113,11 +118,6 @@ class Title @entries.sort! do |a, b| compare_alphanumerically a.title, b.title end - - mtimes = [File.info(dir).modification_time] - mtimes += @title_ids.map{|e| @library.title_hash[e].mtime} - mtimes += @entries.map{|e| e.mtime} - @mtime = mtimes.max end def to_json(json : JSON::Builder) @@ -125,6 +125,7 @@ class Title {% for str in ["dir", "title", "id", "encoded_title"] %} json.field {{str}}, @{{str.id}} {% end %} + json.field "display_name", display_name json.field "mtime" {json.number @mtime.to_unix} json.field "titles" do json.raw self.titles.to_json @@ -179,21 +180,51 @@ class Title return false end end + def get_entry(eid) @entries.find { |e| e.id == eid } end + + def display_name + info = TitleInfo.new @dir + dn = info.display_name + dn.empty? ? @title : dn + end + + def display_name(entry_name) + info = TitleInfo.new @dir + dn = info.entry_display_name[entry_name]? + unless dn.nil? || dn.empty? + return dn + end + entry_name + end + + def set_display_name(dn) + info = TitleInfo.new @dir + info.display_name = dn + info.save + end + + def set_display_name(entry_name : String, dn) + info = TitleInfo.new @dir + info.entry_display_name[entry_name] = dn + info.save + end + # For backward backward compatibility with v0.1.0, we save entry titles # instead of IDs in info.json def save_progress(username, entry, page) info = TitleInfo.new @dir if info.progress[username]?.nil? info.progress[username] = {entry => page} - info.save @dir + info.save return end info.progress[username][entry] = page - info.save @dir + info.save end + def load_progress(username, entry) info = TitleInfo.new @dir if info.progress[username]?.nil? @@ -204,6 +235,7 @@ class Title end info.progress[username][entry] end + def load_percetage(username, entry) info = TitleInfo.new @dir page = load_progress username, entry @@ -211,6 +243,7 @@ class Title return 0.0 if entry_obj.nil? page / entry_obj.pages end + def load_percetage(username) return 0.0 if @entries.empty? read_pages = total_pages = 0 @@ -220,6 +253,7 @@ class Title end read_pages / total_pages end + def next_entry(current_entry_obj) idx = @entries.index current_entry_obj return nil if idx.nil? || idx == @entries.size - 1 @@ -228,26 +262,30 @@ class Title end class TitleInfo - # { user1: { entry1: 10, entry2: 0 } } include JSON::Serializable property comment = "Generated by Mango. DO NOT EDIT!" - property progress : Hash(String, Hash(String, Int32)) + # { user1: { entry1: 10, entry2: 0 } } + property progress = {} of String => Hash(String, Int32) + property display_name = "" + # { entry1 : "display name" } + property entry_display_name = {} of String => String - def initialize(title_dir) - info = nil + @[JSON::Field(ignore: true)] + property dir : String = "" - json_path = File.join title_dir, "info.json" + def initialize(@dir) + json_path = File.join @dir, "info.json" if File.exists? json_path info = TitleInfo.from_json File.read json_path - else - info = TitleInfo.from_json "{\"progress\": {}}" + @progress = info.progress.clone + @display_name = info.display_name + @entry_display_name = info.entry_display_name.clone end - - @progress = info.progress.clone end - def save(title_dir) - json_path = File.join title_dir, "info.json" + + def save + json_path = File.join @dir, "info.json" File.write json_path, self.to_pretty_json end end diff --git a/src/routes/api.cr b/src/routes/api.cr index cefb1b6..387320d 100644 --- a/src/routes/api.cr +++ b/src/routes/api.cr @@ -90,6 +90,29 @@ class APIRouter < Router end end + post "/api/admin/display_name/:title/:name" do |env| + begin + title = (@context.library.get_title env.params.url["title"]) + .not_nil! + entry = title.get_entry env.params.query["entry"]? + name = env.params.url["name"] + + if entry.nil? + title.set_display_name name + else + title.set_display_name entry.title, name + end + rescue e + @context.error e + send_json env, { + "success" => false, + "error" => e.message + }.to_json + else + send_json env, {"success" => true}.to_json + end + end + get "/api/admin/mangadex/manga/:id" do |env| begin id = env.params.url["id"] From 75edfcdb5b000348d794a5e205deb9acb4eb3867 Mon Sep 17 00:00:00 2001 From: Alex Ling Date: Fri, 3 Apr 2020 12:07:09 +0000 Subject: [PATCH 02/29] Set and load display names in frontend --- public/css/mango.css | 3 +++ public/js/title.js | 62 +++++++++++++++++++++++++++++++++++++++++++- src/library.cr | 12 +++++++++ src/routes/api.cr | 6 ++--- src/views/index.ecr | 2 +- src/views/title.ecr | 30 ++++++++++++++++----- 6 files changed, 103 insertions(+), 12 deletions(-) diff --git a/public/css/mango.css b/public/css/mango.css index 8b9df2a..08a9723 100644 --- a/public/css/mango.css +++ b/public/css/mango.css @@ -56,3 +56,6 @@ td > .uk-dropdown { white-space: pre-line; } +.title-rename > .uk-inline { + width: 100%; +} diff --git a/public/js/title.js b/public/js/title.js index a719fb7..4c9f748 100644 --- a/public/js/title.js +++ b/public/js/title.js @@ -15,7 +15,10 @@ function showModal(encodedPath, pages, percentage, encodedeTitle, encodedEntryTi if (percentage === 100) { $('#read-btn').attr('hidden', ''); } - $('#modal-title').text(entry); + $('#modal-title').find('span').text(entry); + $('#modal-title').next().attr('data-id', titleID); + $('#modal-title').next().attr('data-entry-id', entryID); + $('#modal-title').next().find('.title-rename-field').val(entry); $('#path-text').text(zipPath); $('#pages-text').text(pages + ' pages'); @@ -43,3 +46,60 @@ function updateProgress(titleID, entryID, page) { } }); } + +const rename = ele => { + const h2 = $(ele).parent(); + + $(h2).attr('hidden', true); + $(h2).next().removeAttr('hidden'); +}; + +const renameSubmit = ele => { + const group = $(ele).closest('.title-rename'); + const id = $(group).attr('data-id'); + const eid = $(group).attr('data-entry-id'); + const name = $(ele).next().val(); + + console.log(name); + + $(group).attr('hidden', true); + $(group).prev().removeAttr('hidden'); + + if (name.length === 0) { + alert('danger', 'The display name should not be empty'); + return; + } + + $(group).prev().find('span').text(name); + + const query = $.param({ entry: eid }); + let url = `/api/admin/display_name/${id}/${name}`; + if (eid) + url += `?${query}`; + + $.ajax({ + type: 'POST', + url: url, + contentType: "application/json", + dataType: 'json' + }) + .done(data => { + console.log(data); + if (data.error) { + alert('danger', `Failed to update display name. Error: ${data.error}`); + return; + } + location.reload(); + }) + .fail((jqXHR, status) => { + alert('danger', `Failed to update display name. Error: [${jqXHR.status}] ${jqXHR.statusText}`); + }); +}; + +$(() => { + $('.uk-input.title-rename-field').keyup(event => { + if (event.keyCode === 13) { + renameSubmit($(event.currentTarget).prev()); + } + }); +}); diff --git a/src/library.cr b/src/library.cr index 1eab5f4..6b3e068 100644 --- a/src/library.cr +++ b/src/library.cr @@ -51,6 +51,14 @@ class Entry end end + def display_name + @book.display_name @title + end + + def encoded_display_name + URI.encode display_name + end + def read_page(page_num) Zip::File.open @zip_path do |file| page = file.entries @@ -191,6 +199,10 @@ class Title dn.empty? ? @title : dn end + def encoded_display_name + URI.encode display_name + end + def display_name(entry_name) info = TitleInfo.new @dir dn = info.entry_display_name[entry_name]? diff --git a/src/routes/api.cr b/src/routes/api.cr index 387320d..c0423cb 100644 --- a/src/routes/api.cr +++ b/src/routes/api.cr @@ -94,13 +94,13 @@ class APIRouter < Router begin title = (@context.library.get_title env.params.url["title"]) .not_nil! - entry = title.get_entry env.params.query["entry"]? name = env.params.url["name"] - + entry = env.params.query["entry"]? if entry.nil? title.set_display_name name else - title.set_display_name entry.title, name + eobj = title.get_entry entry + title.set_display_name eobj.not_nil!.title, name end rescue e @context.error e diff --git a/src/views/index.ecr b/src/views/index.ecr index b7a9727..543394f 100644 --- a/src/views/index.ecr +++ b/src/views/index.ecr @@ -36,7 +36,7 @@ <%- if t.entries.size > 0 -%>
<%= (percentage[i] * 100).round(1) %>%
<%- end -%> -

"><%= t.title %>

+

"><%= t.display_name %>

<%= t.size %> entries

diff --git a/src/views/title.ecr b/src/views/title.ecr index ef502df..a2c25e9 100644 --- a/src/views/title.ecr +++ b/src/views/title.ecr @@ -1,10 +1,18 @@ -

<%= title.title %>

+
+

<%= title.display_name %>

+ +

<%= title.size %> entries found

@@ -42,7 +50,7 @@ <%- end -%>
-

"><%= t.title %>

+

"><%= t.display_name %>

<%= t.size %> entries

@@ -52,13 +60,13 @@ <%- title.entries.each_with_index do |e, i| -%>
-
+
<%= (percentage[i] * 100).round(1) %>%
-

"><%= e.title %>

+

"><%= e.display_name %>

<%= e.pages %> pages

@@ -71,7 +79,15 @@
- +
+ + +

From b7c0515af730af842ac1de86ccddac1043bc77a5 Mon Sep 17 00:00:00 2001 From: Alex Ling Date: Fri, 3 Apr 2020 23:30:41 +0000 Subject: [PATCH 03/29] Fix dark mode on login page --- src/views/login.ecr | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/views/login.ecr b/src/views/login.ecr index ff56165..80afeb6 100644 --- a/src/views/login.ecr +++ b/src/views/login.ecr @@ -8,9 +8,10 @@ + + -
@@ -33,6 +34,9 @@
+ From 1d4237d687ae1b8aca3886fb80f08f73a6cac5c3 Mon Sep 17 00:00:00 2001 From: Alex Ling Date: Fri, 3 Apr 2020 23:31:24 +0000 Subject: [PATCH 04/29] Pass in admin information when rendering all pages --- src/util.cr | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/util.cr b/src/util.cr index ca7b08d..8fb67c9 100644 --- a/src/util.cr +++ b/src/util.cr @@ -3,7 +3,17 @@ require "big" IMGS_PER_PAGE = 5 macro layout(name) - render "src/views/#{{{name}}}.ecr", "src/views/layout.ecr" + begin + cookie = env.request.cookies.find { |c| c.name == "token" } + is_admin = false + unless cookie.nil? + is_admin = @context.storage.verify_admin cookie.value + end + render "src/views/#{{{name}}}.ecr", "src/views/layout.ecr" + rescue e + message = e.to_s + render "message" + end end macro send_img(env, img) From 8ce51a616318e24172545ef38bfe9872e3e0fbe5 Mon Sep 17 00:00:00 2001 From: Alex Ling Date: Fri, 3 Apr 2020 23:31:52 +0000 Subject: [PATCH 05/29] Hide the "Admin" and "Download" buttons when user is not admin --- src/views/layout.ecr | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/views/layout.ecr b/src/views/layout.ecr index 68d0498..cbd6ead 100644 --- a/src/views/layout.ecr +++ b/src/views/layout.ecr @@ -21,8 +21,10 @@
From 14e3470b1224dbd2d27b23aba50469dd21a0490c Mon Sep 17 00:00:00 2001 From: Alex Ling Date: Fri, 3 Apr 2020 23:42:37 +0000 Subject: [PATCH 06/29] Hide rename buttons when the login user is not admin --- src/views/title.ecr | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/views/title.ecr b/src/views/title.ecr index a2c25e9..349a38e 100644 --- a/src/views/title.ecr +++ b/src/views/title.ecr @@ -1,5 +1,10 @@
-

<%= title.display_name %>

+

<%= title.display_name %> +   + <% if is_admin %> + + <% end %> +