From 6997c66d9a2469ec344ddb4d8e3769730dc40aae Mon Sep 17 00:00:00 2001 From: Phill MV Date: Sun, 18 Feb 2024 14:12:26 -0500 Subject: [PATCH 01/25] poking at a 'mawl server' mode for live reloading changes. --- bin/mawl-server | 8 ++++++++ bin/start-arquivo | 6 ++++++ 2 files changed, 14 insertions(+) create mode 100755 bin/mawl-server diff --git a/bin/mawl-server b/bin/mawl-server new file mode 100755 index 00000000..cdb2804f --- /dev/null +++ b/bin/mawl-server @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -euo pipefail + +PROJECT_FOLDER=`realpath $(dirname "$0")/..` && cd "$PROJECT_FOLDER" + +PID=$(cat tmp/pids/server.pid 2>/dev/null) && ps -p $PID > /dev/null && kill $PID +./bin/rails s -e development diff --git a/bin/start-arquivo b/bin/start-arquivo index 3630104c..cdd87583 100755 --- a/bin/start-arquivo +++ b/bin/start-arquivo @@ -50,6 +50,12 @@ else export STATIC_PLS=1 DOCKER_COMMAND=/arquivo/bin/mawl-import-and-generate # DOCKER_COMMAND="./bin/rails server -d && ./bin/rails static:import static:generate" + elif [ "$1" = "mawl-server" ]; then + MAWL_INPUT_PATH=`realpath "$2"` + MAWL_OUTPUT_PATH="$PROJECT_FOLDER"/tmp/mawl-output + + export STATIC_PLS=1 + DOCKER_COMMAND=/arquivo/bin/mawl-server fi fi From b5dc0d8a5b61ee62898d132633564c8e7115151a Mon Sep 17 00:00:00 2001 From: Phill MV Date: Sun, 18 Feb 2024 14:41:03 -0500 Subject: [PATCH 02/25] WIP: isolated some AdhocMarkdown import logic into EntryImporter to enable live-reloading of markdown entries. --- .../static_site/entries_controller.rb | 17 +++ app/models/ad_hoc_markdown_importer.rb | 87 +------------ app/models/entry_importer.rb | 118 ++++++++++++++++++ 3 files changed, 138 insertions(+), 84 deletions(-) create mode 100644 app/models/entry_importer.rb diff --git a/app/controllers/static_site/entries_controller.rb b/app/controllers/static_site/entries_controller.rb index 5570d0d6..111ac5ff 100644 --- a/app/controllers/static_site/entries_controller.rb +++ b/app/controllers/static_site/entries_controller.rb @@ -38,6 +38,23 @@ def self.controller_path private def set_entry + + # TODO: live-reloading + # see if the file exists and if it does, import it + + # step 1: does it exist as a file? + # step 2: is it markdown or yaml? + # step 2.1: actually this is harder to untangle + # will have to think about how i want to support the "normal" dump o yaml + # vs the "adhoc" markdown + # step 3: parse it & add it. + + @entry = EntryImporter.new(current_notebook).resolve_and_import!(params[:id]) + + if @entry + return + end + # quick terrible hack for routing document type entries if params[:format] identifier = "#{params[:id]}.#{params[:format]}" diff --git a/app/models/ad_hoc_markdown_importer.rb b/app/models/ad_hoc_markdown_importer.rb index 58cdcc1a..91bacaaa 100644 --- a/app/models/ad_hoc_markdown_importer.rb +++ b/app/models/ad_hoc_markdown_importer.rb @@ -54,20 +54,19 @@ def process_import_path(notebook) identifier = path_to_relative_identifier(file_path, notebook.import_path) + entry_importer = EntryImporter.new(current_notebook) if skip_file_path?(identifier, file_path) # Do nothing. elsif looks_like_text?(file_path) # Files that become normal entries, the content in our site. puts "processing #{file_path}" if Rails.env.development? - entry_attributes = entry_attributes_from_markdown(identifier, file_path) - - entry = add_entry!(notebook, entry_attributes) + entry_importer.import!(identifier, file_path) else # everything that is not markdown is treated a bit differently. puts "processing #{file_path}" if Rails.env.development? entry_attributes = entry_attributes_from_document(identifier, file_path) - entry = add_entry!(notebook, entry_attributes) + entry = entry_importer.add_entry!(entry_attributes) filename = File.basename(identifier) if !entry.files.blobs.find_by(filename: filename) @@ -94,71 +93,6 @@ def looks_like_text?(file_path) file_path =~ /\.(md|markdown|html)$/ end - def entry_attributes_from_markdown(identifier, md_path) - loader = FrontMatterParser::Loader::Yaml.new(allowlist_classes: [Time, Date, DateTime]) - md_parser = FrontMatterParser::SyntaxParser::Md.new - parsed_file = FrontMatterParser::Parser.new(md_parser, loader: loader).call(File.read(md_path)) - - occurred_at = parsed_file["occurred_at"] - if occurred_at.blank? - # let's try to guess it from the file - basename = File.basename(md_path) - date = basename.match(/([0-9]{4}-*[0-9]{2}-*[0-9]{2}-*)/).to_a[0] - - # most of the time this does the right thing but it does have the habit - # of sometimes throwing an exception - begin - occurred_at = DateTime.parse(date.to_s) - rescue ArgumentError - end - - # if still nil, let's look at the file itself - if occurred_at.nil? - occurred_at = File.ctime(md_path) - end - end - - created_at = parsed_file["created_at"] || File.ctime(md_path) - updated_at = parsed_file["updated_at"] || File.mtime(md_path) - - # if we're parsing front-mattered markdown, you don't get to define an - # identifier separate from the file's relative path, don't want to deal - # with collisions etc, too confusing, the ad hoc markdown is for ad hoc - # files, loosely slapped together! - # - # then, we lop off `.md`, `.markdown` and `html` from the suffix - entry_source = identifier # store the unmodified identifier as the "source" - identifier = identifier.gsub(/\.(md|markdown|html)/, "") - - entry_attributes = parsed_file.front_matter.merge({ - "identifier" => identifier, - "source" => entry_source, - "occurred_at" => occurred_at, - "body" => parsed_file.content, - "created_at" => created_at, - "updated_at" => updated_at, - skip_local_sync: true - }).slice(*Entry.accepted_attributes) - - # handle metadata! - metadata_keys = (parsed_file.front_matter.keys - Entry.accepted_attributes) - if metadata_keys.any? - # if the user has specified a non Hash value, ie "metadata: foo", then - # throw a slightly easier to understand error here, instead of later on - # when we try to instantiate the Entry object & the error gets thrown there. - if entry_attributes["metadata"] && !entry_attributes["metadata"].is_a?(Hash) - raise "I expected the 'metadata' key on #{identifier} to be Hash, but something else is going on." - end - - entry_attributes["metadata"] ||= {} - metadata_keys.each do |mkey| - entry_attributes["metadata"][mkey] ||= parsed_file.front_matter[mkey] - end - end - - entry_attributes - end - def entry_attributes_from_document(identifier, file_path) entry_kind = nil entry_source = identifier @@ -236,19 +170,4 @@ def process_templates(notebook) def path_to_relative_identifier(file_path, import_path) Pathname.new(file_path).relative_path_from(import_path).to_s end - - def add_entry!(notebook, entry_attributes) - identifier = entry_attributes["identifier"] - - # find or update the entry - entry = notebook.entries.find_by(identifier: identifier) - - if entry - entry.update!(entry_attributes) - else - entry = notebook.entries.create(entry_attributes) - end - - entry - end end diff --git a/app/models/entry_importer.rb b/app/models/entry_importer.rb new file mode 100644 index 00000000..76bae56e --- /dev/null +++ b/app/models/entry_importer.rb @@ -0,0 +1,118 @@ +class EntryImporter + attr_reader :current_notebook, :root_path + def initialize(current_notebook) + @current_notebook = current_notebook + @root_path = current_notebook.import_path + end + + def resolve_and_import!(identifier) + # TODO: replace hacky & brittle with more well-defined search + # i.e. exact lookups vs just fuzzy searching + # ALSO: need to think about how this would interact with scss templates. + search_path = build_file_path(identifier) + "*" + file_path = Dir[search_path].first + if file_path && looks_like_text?(file_path) + # TODO: hrm, should the identifier come from the request? this will blow up somehow + import!(identifier, file_path) + else + puts "couldn't find nothin' to import" + return nil + end + end + + def import!(identifier, file_path) + entry_attributes = entry_attributes_from_markdown(identifier, file_path) + + entry = add_entry!(entry_attributes) + end + + def looks_like_text?(file_path) + file_path =~ /\.(md|markdown|html)$/ + end + + def build_file_path(path_info) + clean_path_info = Rack::Utils.clean_path_info(path_info) + ::File.join(@root_path, clean_path_info) + end + + def entry_attributes_from_markdown(identifier, md_path) + loader = FrontMatterParser::Loader::Yaml.new(allowlist_classes: [Time, Date, DateTime]) + md_parser = FrontMatterParser::SyntaxParser::Md.new + parsed_file = FrontMatterParser::Parser.new(md_parser, loader: loader).call(File.read(md_path)) + + occurred_at = parsed_file["occurred_at"] + if occurred_at.blank? + # let's try to guess it from the file + basename = File.basename(md_path) + date = basename.match(/([0-9]{4}-*[0-9]{2}-*[0-9]{2}-*)/).to_a[0] + + # most of the time this does the right thing but it does have the habit + # of sometimes throwing an exception + begin + occurred_at = DateTime.parse(date.to_s) + rescue ArgumentError + end + + # if still nil, let's look at the file itself + if occurred_at.nil? + occurred_at = File.ctime(md_path) + end + end + + created_at = parsed_file["created_at"] || File.ctime(md_path) + updated_at = parsed_file["updated_at"] || File.mtime(md_path) + + # if we're parsing front-mattered markdown, you don't get to define an + # identifier separate from the file's relative path, don't want to deal + # with collisions etc, too confusing, the ad hoc markdown is for ad hoc + # files, loosely slapped together! + # + # then, we lop off `.md`, `.markdown` and `html` from the suffix + entry_source = identifier # store the unmodified identifier as the "source" + identifier = identifier.gsub(/\.(md|markdown|html)/, "") + + entry_attributes = parsed_file.front_matter.merge({ + "identifier" => identifier, + "source" => entry_source, + "occurred_at" => occurred_at, + "body" => parsed_file.content, + "created_at" => created_at, + "updated_at" => updated_at, + skip_local_sync: true + }).slice(*Entry.accepted_attributes) + + # handle metadata! + metadata_keys = (parsed_file.front_matter.keys - Entry.accepted_attributes) + if metadata_keys.any? + # if the user has specified a non Hash value, ie "metadata: foo", then + # throw a slightly easier to understand error here, instead of later on + # when we try to instantiate the Entry object & the error gets thrown there. + if entry_attributes["metadata"] && !entry_attributes["metadata"].is_a?(Hash) + raise "I expected the 'metadata' key on #{identifier} to be Hash, but something else is going on." + end + + entry_attributes["metadata"] ||= {} + metadata_keys.each do |mkey| + entry_attributes["metadata"][mkey] ||= parsed_file.front_matter[mkey] + end + end + + entry_attributes + end + + def add_entry!(entry_attributes) + identifier = entry_attributes["identifier"] + + # find or update the entry + entry = current_notebook.entries.find_by(identifier: identifier) + + if entry + entry.update!(entry_attributes) + else + entry = current_notebook.entries.create(entry_attributes) + end + + entry + end + +end From aff262d05bcb8ca36e8644673ed43e58c94230ac Mon Sep 17 00:00:00 2001 From: Phill MV Date: Fri, 22 Mar 2024 21:02:47 -0400 Subject: [PATCH 03/25] wip uh experimenting with loading stylesheet dynamically --- .../static_site/entries_controller.rb | 2 +- app/models/entry_importer.rb | 41 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/app/controllers/static_site/entries_controller.rb b/app/controllers/static_site/entries_controller.rb index 111ac5ff..8152e852 100644 --- a/app/controllers/static_site/entries_controller.rb +++ b/app/controllers/static_site/entries_controller.rb @@ -87,7 +87,7 @@ def serve_blob(blob) end response.headers["Content-Type"] = content_type || ActiveStorage::BaseController::DEFAULT_SEND_FILE_TYPE - response.headers["Content-Disposition"] = disposition || ActiveStorage::BaseController::DEFAULT_SEND_FILE_DISPOSITION + # response.headers["Content-Disposition"] = disposition || ActiveStorage::BaseController::DEFAULT_SEND_FILE_DISPOSITION end end end diff --git a/app/models/entry_importer.rb b/app/models/entry_importer.rb index 76bae56e..dbdfb8e1 100644 --- a/app/models/entry_importer.rb +++ b/app/models/entry_importer.rb @@ -6,6 +6,11 @@ def initialize(current_notebook) end def resolve_and_import!(identifier) + + case identifier + when "stylesheets/application" + return render_stylesheet + end # TODO: replace hacky & brittle with more well-defined search # i.e. exact lookups vs just fuzzy searching # ALSO: need to think about how this would interact with scss templates. @@ -115,4 +120,40 @@ def add_entry!(entry_attributes) entry end + def render_stylesheet + # okay lets do the dumbest thing possible + # reload the file everytime!!!! + + scss_path = build_file_path("stylesheets/application.css.scss") + + rendered_stylesheet = nil + if File.exist?(scss_path) + load_path = File.join(current_notebook.import_path, "stylesheets") + + rendered_css = SassC::Engine.new(File.read(scss_path), { + filename: "application.css.scss", + syntax: :scss, + load_paths: [load_path], + }).render + + # there can only be ONE application.css + if to_delete = current_notebook.entries.find_by(identifier: "stylesheets/application.css") + puts "Destroying extraneous stylesheets/application.css, so it can be replaced." + to_delete.destroy + end + + rendered_stylesheet = current_notebook.entries.new + rendered_stylesheet.identifier = "stylesheets/application.css" + rendered_stylesheet.kind = :document + rendered_stylesheet.save! + + blob = ActiveStorage::Blob.create_and_upload!(io: StringIO.new(rendered_css), + metadata: { analyzed: true }, + filename: "application.css") + # blob.analyze + rendered_stylesheet.files.create(blob_id: blob.id, created_at: blob.created_at) + end + + rendered_stylesheet + end end From c452cfac2beec14578234a2bfe40512de1f70973 Mon Sep 17 00:00:00 2001 From: Phill MV Date: Sat, 30 Mar 2024 14:23:50 -0400 Subject: [PATCH 04/25] silence sqlite warning, i get it --- config/environments/production.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/environments/production.rb b/config/environments/production.rb index 74b0b23b..35f74080 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -7,6 +7,8 @@ config.hosts << "localhost" end + config.active_record.sqlite3_production_warning=false + # until we figure out how to multitenant this, config.active_storage.routes_prefix = ENV["ARQUIVO_USER"] || "/phillmv/_" # config.active_storage.routes_prefix = "/#{User.current}/_" From 15cdac0e3d9c8ad3cd80d7832dad01edc0417c16 Mon Sep 17 00:00:00 2001 From: Phill MV Date: Sat, 30 Mar 2024 14:24:33 -0400 Subject: [PATCH 05/25] don't rely on any secrets not in the codebase --- bin/start-arquivo | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/start-arquivo b/bin/start-arquivo index cdd87583..6f64ea7c 100755 --- a/bin/start-arquivo +++ b/bin/start-arquivo @@ -62,7 +62,7 @@ fi export ARQUIVO_USER=phillmv export ARQUIVO_GIT_EMAIL=phillmv@okayfail.com export ARQUIVO_GIT_NAME="Phill MV" -export RAILS_MASTER_KEY=$(cat "$PROJECT_FOLDER"/config/master.key) +export SECRET_KEY_BASE=`openssl rand -hex 64` export RAILS_BIND=tcp://0.0.0.0:3001 echo "Running arquivo while mounting local filesystem..." @@ -74,6 +74,7 @@ docker run -it -p "$ARQUIVO_PORT":3001 \ -e RAILS_ENV \ -e RAILS_BIND \ -e STATIC_PLS \ + -e SECRET_KEY_BASE \ -v "$MAWL_INPUT_PATH":/mawl-input \ -v "$DATA_FOLDER":/data \ -v "$MAWL_OUTPUT_PATH":/output \ From b495acf98cd3fab686ff161a604845ed30ee0610 Mon Sep 17 00:00:00 2001 From: Phill MV Date: Sat, 30 Mar 2024 14:29:02 -0400 Subject: [PATCH 06/25] always run db:prepare? --- bin/docker-entrypoint | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/bin/docker-entrypoint b/bin/docker-entrypoint index 2d1c0970..bae6a44e 100755 --- a/bin/docker-entrypoint +++ b/bin/docker-entrypoint @@ -1,13 +1,6 @@ #!/bin/bash -e -# If running the rails server then create or migrate existing database -if [ "${*}" == "./bin/rails server" ]; then - ./bin/rails db:prepare - - # if [ "${RAILS_ENV}" == "development" ]; then - # ./bin/rails db:setup - # fi -fi +./bin/rails db:prepare if [[ -n ${ARQUIVO_GIT_NAME} ]]; then git config --global user.name "${ARQUIVO_GIT_NAME}" From 38de6b2f7146a80fe1e0192e307afc35e6ffa263 Mon Sep 17 00:00:00 2001 From: Phill MV Date: Sat, 30 Mar 2024 14:29:37 -0400 Subject: [PATCH 07/25] since we provide the SECRET_KEY we don't need this anymore --- config/credentials.yml.enc | 1 - 1 file changed, 1 deletion(-) delete mode 100644 config/credentials.yml.enc diff --git a/config/credentials.yml.enc b/config/credentials.yml.enc deleted file mode 100644 index 9c2eb8ef..00000000 --- a/config/credentials.yml.enc +++ /dev/null @@ -1 +0,0 @@ -3A2/7wcCmN9/H8EvKiP0dHihZKZ2ei8+vCM7iYBy/ON5IDEuzDqseKGWiCJa62edYQmO7uDdVaDq3KJQ6MYf27oqCZpePjFZClxE9OxSC8vi+2QOSAb8wWtNJKEGhkkNfzPTq35FLG9UnhIJMdHpHEb4SHaHkI2wxgGydH+iMyjuu70/UKjWMxwtep6Vq3ZyoA0K4JKtP1IQqEcKByRaBTX0IpvZVPIb6kz/u7ZyRkEyhpNKTUErG2OnuX/ul+qjhPKCs1Ib4/33pXwetn3BPwF1tsMOBgxBKC3wYGQLKz2cDjD1wZ1rKytmPx26daR4tWl52VBAOw4I1UJAIkSEhBa83G0GDwAuu5JvpVkjcKPRvMDgUjz9lWo80zsB7QCZCfrbsSiZxnSvNdl4YSqhg6eIxfJnS9931ZsK--9DAk4Pa9ZJ/ry83M--0LHmUU9Z320nVXl4IEMDjw== \ No newline at end of file From f3d3dd6a2a5e1f921a184a328efc87568fe9a90e Mon Sep 17 00:00:00 2001 From: Phill MV Date: Sat, 30 Mar 2024 14:33:25 -0400 Subject: [PATCH 08/25] wip: let's avoid dynamic stylesheets for just now, when dynamically loading files skip over directories, and a fix for the AdHocMarkdownImporter. --- app/controllers/static_site/entries_controller.rb | 2 ++ app/models/ad_hoc_markdown_importer.rb | 2 +- app/models/entry_importer.rb | 10 +++++----- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/controllers/static_site/entries_controller.rb b/app/controllers/static_site/entries_controller.rb index 8152e852..4cb748f9 100644 --- a/app/controllers/static_site/entries_controller.rb +++ b/app/controllers/static_site/entries_controller.rb @@ -49,11 +49,13 @@ def set_entry # vs the "adhoc" markdown # step 3: parse it & add it. + if Rails.env.development? @entry = EntryImporter.new(current_notebook).resolve_and_import!(params[:id]) if @entry return end + end # quick terrible hack for routing document type entries if params[:format] diff --git a/app/models/ad_hoc_markdown_importer.rb b/app/models/ad_hoc_markdown_importer.rb index 91bacaaa..21e84f30 100644 --- a/app/models/ad_hoc_markdown_importer.rb +++ b/app/models/ad_hoc_markdown_importer.rb @@ -54,7 +54,7 @@ def process_import_path(notebook) identifier = path_to_relative_identifier(file_path, notebook.import_path) - entry_importer = EntryImporter.new(current_notebook) + entry_importer = EntryImporter.new(notebook) if skip_file_path?(identifier, file_path) # Do nothing. elsif looks_like_text?(file_path) diff --git a/app/models/entry_importer.rb b/app/models/entry_importer.rb index dbdfb8e1..c8d7ba0b 100644 --- a/app/models/entry_importer.rb +++ b/app/models/entry_importer.rb @@ -7,15 +7,15 @@ def initialize(current_notebook) def resolve_and_import!(identifier) - case identifier - when "stylesheets/application" - return render_stylesheet - end + # case identifier + # when "stylesheets/application" + # return render_stylesheet + # end # TODO: replace hacky & brittle with more well-defined search # i.e. exact lookups vs just fuzzy searching # ALSO: need to think about how this would interact with scss templates. search_path = build_file_path(identifier) + "*" - file_path = Dir[search_path].first + file_path = Dir[search_path].reject {|f| File.directory?(f) }.first if file_path && looks_like_text?(file_path) # TODO: hrm, should the identifier come from the request? this will blow up somehow import!(identifier, file_path) From feae41cfd88d54f4b4b61261a17005aedb40bb9e Mon Sep 17 00:00:00 2001 From: Phill MV Date: Sun, 14 Apr 2024 19:29:54 -0400 Subject: [PATCH 09/25] reintroduced concept of a template entry whose body is rendered as erb --- .../static_site/entries_controller.rb | 4 +-- app/models/entry_importer.rb | 20 +++++++++++---- app/models/entry_renderer.rb | 25 +++++++++++++++++++ 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/app/controllers/static_site/entries_controller.rb b/app/controllers/static_site/entries_controller.rb index 4cb748f9..4a986e1b 100644 --- a/app/controllers/static_site/entries_controller.rb +++ b/app/controllers/static_site/entries_controller.rb @@ -15,9 +15,9 @@ def show # TODO: make up my mind on how to handle templates. # elsif @entry.template? # don't love it but fix later, lol do not deploy this to untrusted user contexts??? - # render inline: File.read(File.join(current_notebook.import_path, @entry.source)), layout: "application" + # render inline: @entry.body, layout: "application" - elsif @entry.note? || @entry.bookmark? + elsif @entry.note? || @entry.bookmark? || @entry.template? @show_thread = params[:thread].present? @renderer = EntryRenderer.new(@entry, remove_subject: true) @current_date = @entry.occurred_at.strftime("%Y-%m-%d") diff --git a/app/models/entry_importer.rb b/app/models/entry_importer.rb index c8d7ba0b..e305415a 100644 --- a/app/models/entry_importer.rb +++ b/app/models/entry_importer.rb @@ -14,8 +14,9 @@ def resolve_and_import!(identifier) # TODO: replace hacky & brittle with more well-defined search # i.e. exact lookups vs just fuzzy searching # ALSO: need to think about how this would interact with scss templates. - search_path = build_file_path(identifier) + "*" - file_path = Dir[search_path].reject {|f| File.directory?(f) }.first + file_path = build_file_path(identifier) + glob_path = file_path + "*" + file_path = Dir[glob_path].select { |f| f =~ /#{file_path}(\.html|\.md|\.markdown)+/ }.reject {|f| File.directory?(f) }.first if file_path && looks_like_text?(file_path) # TODO: hrm, should the identifier come from the request? this will blow up somehow import!(identifier, file_path) @@ -32,7 +33,7 @@ def import!(identifier, file_path) end def looks_like_text?(file_path) - file_path =~ /\.(md|markdown|html)$/ + file_path =~ /\.(md|markdown|html|erb)$/ end def build_file_path(path_info) @@ -64,6 +65,7 @@ def entry_attributes_from_markdown(identifier, md_path) end end + # TODO: DON'T DO THIS GIT DOESN"T SET CTIME!!!!!! created_at = parsed_file["created_at"] || File.ctime(md_path) updated_at = parsed_file["updated_at"] || File.mtime(md_path) @@ -73,14 +75,22 @@ def entry_attributes_from_markdown(identifier, md_path) # files, loosely slapped together! # # then, we lop off `.md`, `.markdown` and `html` from the suffix - entry_source = identifier # store the unmodified identifier as the "source" - identifier = identifier.gsub(/\.(md|markdown|html)/, "") + entry_source = md_path # store the unmodified identifier as the "source" + identifier = identifier.gsub(/\.(md|markdown|html|erb)/, "") + + # TODO: test this behaviour + if entry_source =~ /\.erb$/ + entry_kind = "template" + else + entry_kind = nil + end entry_attributes = parsed_file.front_matter.merge({ "identifier" => identifier, "source" => entry_source, "occurred_at" => occurred_at, "body" => parsed_file.content, + "kind" => entry_kind, "created_at" => created_at, "updated_at" => updated_at, skip_local_sync: true diff --git a/app/models/entry_renderer.rb b/app/models/entry_renderer.rb index bc99ecef..c490f077 100644 --- a/app/models/entry_renderer.rb +++ b/app/models/entry_renderer.rb @@ -33,6 +33,27 @@ def pipeline(opt = {}) end end + class EntryContext + include ActionView::Helpers + include ActionView::RoutingUrlFor + include Rails.application.routes.url_helpers + # include ActionDispatch::Routing::UrlFor + include UrlHelper + + attr_reader :entry + def initialize(entry) + @entry = entry + end + + def default_url_options + {} + end + + def binding + super + end + end + # do we take an attribute? we're rendering an entry when was the last fucking time i rendered something other than a body? def render(opt = {}) @@ -58,6 +79,10 @@ def to_html(attribute_name = "body", opt = {}) if !attribute "" else + if entry.template? + attribute = ERB.new(attribute).result(EntryContext.new(entry).binding).html_safe + end + render_html(attribute, opt) end end From 88b11ee4a5faf35e2b253d229efd57ea99c866d1 Mon Sep 17 00:00:00 2001 From: Phill MV Date: Wed, 24 Apr 2024 22:06:42 -0400 Subject: [PATCH 10/25] prepare db on mawl import and generate --- bin/mawl-import-and-generate | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/mawl-import-and-generate b/bin/mawl-import-and-generate index 774549d6..9e948c7a 100755 --- a/bin/mawl-import-and-generate +++ b/bin/mawl-import-and-generate @@ -2,5 +2,6 @@ set -euo pipefail +./bin/rails db:prepare PROJECT_FOLDER=`realpath $(dirname "$0")/..` && cd "$PROJECT_FOLDER" ./bin/mawl-import && ./bin/mawl-generate From a05b4c1f6cac998f0bafdc82a8c85c3f42f0a4d6 Mon Sep 17 00:00:00 2001 From: Phill MV Date: Wed, 24 Apr 2024 22:06:59 -0400 Subject: [PATCH 11/25] entry renderer now supplies default_url_options --- app/models/entry_renderer.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/models/entry_renderer.rb b/app/models/entry_renderer.rb index c490f077..0671f02e 100644 --- a/app/models/entry_renderer.rb +++ b/app/models/entry_renderer.rb @@ -34,6 +34,7 @@ def pipeline(opt = {}) end class EntryContext + include ActionView::Context include ActionView::Helpers include ActionView::RoutingUrlFor include Rails.application.routes.url_helpers @@ -46,7 +47,7 @@ def initialize(entry) end def default_url_options - {} + {format: "html"} end def binding From 636a56dfdc923a9888ea053ceb5653e18874abb0 Mon Sep 17 00:00:00 2001 From: Phill MV Date: Wed, 24 Apr 2024 22:07:32 -0400 Subject: [PATCH 12/25] AdHoc importer now also identifies erb files. --- app/models/ad_hoc_markdown_importer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/ad_hoc_markdown_importer.rb b/app/models/ad_hoc_markdown_importer.rb index 21e84f30..c8d9e7e5 100644 --- a/app/models/ad_hoc_markdown_importer.rb +++ b/app/models/ad_hoc_markdown_importer.rb @@ -90,7 +90,7 @@ def skip_file_path?(identifier, file_path) end def looks_like_text?(file_path) - file_path =~ /\.(md|markdown|html)$/ + file_path =~ /\.(md|markdown|html|erb)$/ end def entry_attributes_from_document(identifier, file_path) From 2831c152a8d482f9fc729c291cc28e4ccc480160 Mon Sep 17 00:00:00 2001 From: Phill MV Date: Wed, 24 Apr 2024 22:08:15 -0400 Subject: [PATCH 13/25] always link to archive with .html extension --- app/views/static_site/timeline/hidden_entries.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/static_site/timeline/hidden_entries.html.erb b/app/views/static_site/timeline/hidden_entries.html.erb index 9d2e9bee..7599f2c9 100644 --- a/app/views/static_site/timeline/hidden_entries.html.erb +++ b/app/views/static_site/timeline/hidden_entries.html.erb @@ -3,7 +3,7 @@ <% @entries.each do |entry| %> <%= link_to entry.identifier, entry_path(entry) %> <% end %> - <%= link_to "archive", archive_path %> + <%= link_to "archive", archive_path(format: "html") %>
<%= will_paginate @entries, class: "pagination mt-4 mb-4"%> From 01f4a341f3c5443919c3c4a0712113ec4a65236b Mon Sep 17 00:00:00 2001 From: Phill MV Date: Tue, 14 May 2024 12:51:13 -0400 Subject: [PATCH 14/25] Swapped the active_storage routes prefix to be '_' --- config/environments/development.rb | 2 +- config/environments/production.rb | 3 +-- config/environments/static.rb | 2 +- config/environments/test.rb | 2 +- config/routes.rb | 8 +++----- 5 files changed, 7 insertions(+), 10 deletions(-) diff --git a/config/environments/development.rb b/config/environments/development.rb index 9f101c1b..9a5067a5 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -10,7 +10,7 @@ config.hosts << "arquivo.io" # until we figure out how to multitenant this, - config.active_storage.routes_prefix = ENV["ARQUIVO_USER"] || "/phillmv/_" + config.active_storage.routes_prefix = "_" # Settings specified here will take precedence over those in config/application.rb. diff --git a/config/environments/production.rb b/config/environments/production.rb index 35f74080..15c7f51b 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -10,8 +10,7 @@ config.active_record.sqlite3_production_warning=false # until we figure out how to multitenant this, - config.active_storage.routes_prefix = ENV["ARQUIVO_USER"] || "/phillmv/_" - # config.active_storage.routes_prefix = "/#{User.current}/_" + config.active_storage.routes_prefix = "_" # config.assets.prefix = "/#{User.current}/_" # Settings specified here will take precedence over those in config/application.rb. diff --git a/config/environments/static.rb b/config/environments/static.rb index 076218a4..2a6758d7 100644 --- a/config/environments/static.rb +++ b/config/environments/static.rb @@ -2,7 +2,7 @@ config.hosts << "arquivo.io" # until we figure out how to multitenant this, - config.active_storage.routes_prefix = "/#{User.current}/_" + config.active_storage.routes_prefix = "_" # Settings specified here will take precedence over those in config/application.rb. diff --git a/config/environments/test.rb b/config/environments/test.rb index e0130968..210499b5 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -6,7 +6,7 @@ # and recreated between test runs. Don't rely on the data there! Rails.application.configure do - config.active_storage.routes_prefix = "/phillmv/_" + config.active_storage.routes_prefix = "_" # Settings specified here will take precedence over those in config/application.rb. diff --git a/config/routes.rb b/config/routes.rb index 827ab8ab..3db27596 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -56,11 +56,9 @@ else # END STATIC MODE resources :notebooks - scope ':owner', defaults: { owner: "owner" } do - # lambda used exclusively to handle ActiveStorage urls while mounting the whole app - # on a /user subdirectory, cos we've defined the ActiveStorage route prefix to be - # /user/_/ in config/environment/* - scope ':notebook', defaults: { notebook: "journal" }, constraints: lambda { |req| req.path.split("/")[2] != "_" } do + # lambda used exclusively to handle ActiveStorage urls + scope ':owner', defaults: { owner: "owner" }, constraints: lambda { |req| req.path.index("/_/") != 0 } do + scope ':notebook', defaults: { notebook: "journal" } do get '/', to: "timeline#index", as: :timeline get '/page/:page', to: "timeline#index" get '/agenda', to: "timeline#agenda", as: :agenda From 7817e4636473f7ec6f6a5d8c7f41f4805a625c97 Mon Sep 17 00:00:00 2001 From: Phill MV Date: Tue, 14 May 2024 12:52:16 -0400 Subject: [PATCH 15/25] Added LinkRelativizer to EntryRenderer pipeline. --- app/controllers/entries_controller.rb | 19 ++++++++++-- app/models/entry_renderer.rb | 2 ++ .../pipeline_filter/link_relativizer.rb | 29 +++++++++++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 app/models/pipeline_filter/link_relativizer.rb diff --git a/app/controllers/entries_controller.rb b/app/controllers/entries_controller.rb index 07cc76e0..0d979919 100644 --- a/app/controllers/entries_controller.rb +++ b/app/controllers/entries_controller.rb @@ -1,6 +1,6 @@ class EntriesController < ApplicationController before_action :find_or_build_entry, only: [:show, :edit] - before_action :find_entry, only: [:update, :destroy, :files, :copy] + before_action :find_entry, only: [:update, :destroy, :copy] # GET /entries # GET /entries.json @@ -159,7 +159,22 @@ def destroy end def files - blob = @entry.files.blobs.find_by!(filename: params[:filename]) + # TODO: test this obviously insane behaviour + # are we dealing with a document entry with `files` in the identifier? + doc_identifier = File.join(params["id"], "files", params["filename"]) + @entry = current_notebook.entries.find_by(identifier: doc_identifier) + + if @entry.nil? + @entry = current_notebook.entries.find_by!(identifier: params[:id]) + end + + # TODO: undo this when we move documents to just reading the file off disk + if @entry.document? + blob = @entry.files.blobs.first + else + blob = @entry.files.blobs.find_by(filename: params[:filename]) + end + expires_in ActiveStorage.service_urls_expire_in redirect_to rails_blob_path(blob, disposition: params[:disposition]) end diff --git a/app/models/entry_renderer.rb b/app/models/entry_renderer.rb index 0671f02e..93e07ca6 100644 --- a/app/models/entry_renderer.rb +++ b/app/models/entry_renderer.rb @@ -174,6 +174,7 @@ def render_body(opt = {}) PipelineFilter::MarkdownFilter, # convert to HTML PipelineFilter::WikiLinkFilter, HTML::Pipeline::SanitizationFilter, # strip scary tags + PipelineFilter::LinkRelativizer, PipelineFilter::MyTaskListFilter, # convert task markdown to html PipelineFilter::HashtagFilter, # link hashtags PipelineFilter::MentionFilter, # link mentions @@ -191,6 +192,7 @@ def render_body(opt = {}) PipelineFilter::MarkdownFilter, # convert to HTML PipelineFilter::SubjectExtractorFilter, PipelineFilter::WikiLinkFilter, + PipelineFilter::LinkRelativizer, # Here we commented out: HTML::Pipeline::SanitizationFilte PipelineFilter::MyTaskListFilter, # convert task markdown to html PipelineFilter::HashtagFilter, # link hashtags diff --git a/app/models/pipeline_filter/link_relativizer.rb b/app/models/pipeline_filter/link_relativizer.rb new file mode 100644 index 00000000..1721a384 --- /dev/null +++ b/app/models/pipeline_filter/link_relativizer.rb @@ -0,0 +1,29 @@ +require 'html/pipeline' + +class PipelineFilter::LinkRelativizer < HTML::Pipeline::Filter + + # TODO: test his somewhat insane behaviour; needs to ignore links to other sites + # TODO: probably needs some other optional switch? + # TODO: needs to support stylesheets, video, audio, etc + # i.e. https://www.w3schools.com/tags/att_src.asp / https://www.w3schools.com/tags/att_href.asp + def call + if !Arquivo.static? && context[:entry] + prefix = "/#{context[:entry].parent_notebook.name_with_owner}" + doc.css("a[href^='/']:not([href^='#{prefix}']), + img[src^='/']:not([src^='#{prefix}'])").each do |tag| + case tag.name + when "a" + attr = "href" + when "img" + attr = "src" + end + + old_path = tag.attributes[attr].value + new_path = Pathname.new(File.join(prefix, old_path)).cleanpath.to_s + tag.attributes[attr].value = new_path + end + end + + doc + end +end From 46dd8d3e4bae4c86356c363e74cbd31c47259b57 Mon Sep 17 00:00:00 2001 From: Phill MV Date: Sat, 9 Nov 2024 16:11:25 -0500 Subject: [PATCH 16/25] wip, separated mawl out from start-arquivo, fixed some settings --- bin/start-arquivo | 30 +++------------------ bin/start-mawl | 67 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 27 deletions(-) create mode 100755 bin/start-mawl diff --git a/bin/start-arquivo b/bin/start-arquivo index 3630104c..8f19c101 100755 --- a/bin/start-arquivo +++ b/bin/start-arquivo @@ -11,7 +11,7 @@ fi mkdir -p "$DATA_FOLDER" if [ -z ${ARQUIVO_PORT+x} ]; then - ARQUIVO_PORT=12346 + ARQUIVO_PORT=12347 fi if [ "$#" -eq 0 ]; then @@ -30,33 +30,13 @@ else # do the default DOCKER_COMMAND= export RAILS_ENV=development - elif [ "$1" = "mawl" ]; then - # TODO: rationalize into its own command, plus a server option - shift - - if [ -z "${1+x}" ]; then - echo "Usage: $0 mawl [input-folder] [output-folder]" - exit 1 - else - MAWL_INPUT_PATH=`realpath "$1"` - fi - - if [ -z "${2+x}" ]; then - MAWL_OUTPUT_PATH="$PROJECT_FOLDER"/tmp/mawl-output - else - MAWL_OUTPUT_PATH="$2" - fi - - export STATIC_PLS=1 - DOCKER_COMMAND=/arquivo/bin/mawl-import-and-generate - # DOCKER_COMMAND="./bin/rails server -d && ./bin/rails static:import static:generate" fi fi export ARQUIVO_USER=phillmv export ARQUIVO_GIT_EMAIL=phillmv@okayfail.com export ARQUIVO_GIT_NAME="Phill MV" -export RAILS_MASTER_KEY=$(cat "$PROJECT_FOLDER"/config/master.key) +# export RAILS_MASTER_KEY=$(cat "$PROJECT_FOLDER"/config/master.key) export RAILS_BIND=tcp://0.0.0.0:3001 echo "Running arquivo while mounting local filesystem..." @@ -67,9 +47,5 @@ docker run -it -p "$ARQUIVO_PORT":3001 \ -e ARQUIVO_GIT_NAME \ -e RAILS_ENV \ -e RAILS_BIND \ - -e STATIC_PLS \ - -v "$MAWL_INPUT_PATH":/mawl-input \ -v "$DATA_FOLDER":/data \ - -v "$MAWL_OUTPUT_PATH":/output \ - -v "$PROJECT_FOLDER":/arquivo \ - arquivo-development:latest ${DOCKER_COMMAND} + ghcr.io/phillmv/arquivo-development:latest ${DOCKER_COMMAND} diff --git a/bin/start-mawl b/bin/start-mawl new file mode 100755 index 00000000..a49d39e1 --- /dev/null +++ b/bin/start-mawl @@ -0,0 +1,67 @@ +#!/usr/bin/env bash + +set -euo pipefail + +PROJECT_FOLDER=`realpath $(dirname "$0")/..` + +if [ -z ${DATA_FOLDER+x} ]; then + DATA_FOLDER="$PROJECT_FOLDER"/data +fi + +mkdir -p "$DATA_FOLDER" + +if [ -z ${ARQUIVO_PORT+x} ]; then + ARQUIVO_PORT=12347 +fi + +if [ "$#" -eq 0 ]; then + echo "Usage: $0 mawl [input-folder] [output-folder]" + exit 1 +else + if [ "$1" = "build" ]; then + shift + MAWL_INPUT_PATH=`realpath "$1"` + + if [ -z "${2+x}" ]; then + MAWL_OUTPUT_PATH="$PROJECT_FOLDER"/tmp/mawl-output + else + MAWL_OUTPUT_PATH="$2" + fi + + export STATIC_PLS=1 + DOCKER_COMMAND=/arquivo/bin/mawl-import-and-generate + elif [ "$1" = "server" ]; then + shift + MAWL_INPUT_PATH=`realpath "$1"` + + if [ -z "${2+x}" ]; then + MAWL_OUTPUT_PATH="$PROJECT_FOLDER"/tmp/mawl-output + else + MAWL_OUTPUT_PATH="$2" + fi + + export STATIC_PLS=1 + DOCKER_COMMAND= + fi +fi + +export ARQUIVO_USER=phillmv +export ARQUIVO_GIT_EMAIL=phillmv@okayfail.com +export ARQUIVO_GIT_NAME="Phill MV" +export RAILS_BIND=tcp://0.0.0.0:3001 +export RAILS_ENV=development + +echo "Running mawl while mounting local filesystem..." + +docker run -it -p "$ARQUIVO_PORT":3001 \ + -e ARQUIVO_USER \ + -e ARQUIVO_GIT_EMAIL \ + -e ARQUIVO_GIT_NAME \ + -e RAILS_ENV \ + -e RAILS_BIND \ + -e STATIC_PLS \ + -v "$DATA_FOLDER":/data \ + -v "$MAWL_INPUT_PATH":/mawl-input \ + -v "$MAWL_OUTPUT_PATH":/output \ + -v "$PROJECT_FOLDER":/arquivo \ + ghcr.io/phillmv/arquivo-development:latest ${DOCKER_COMMAND} From 04ea2dd630d52549420d0b62e2ddfb38630fe201 Mon Sep 17 00:00:00 2001 From: Phill MV Date: Sun, 17 Nov 2024 13:39:29 -0500 Subject: [PATCH 17/25] start-arquivo should load the current folder --- bin/start-arquivo | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/start-arquivo b/bin/start-arquivo index 8f19c101..9d241aeb 100755 --- a/bin/start-arquivo +++ b/bin/start-arquivo @@ -48,4 +48,5 @@ docker run -it -p "$ARQUIVO_PORT":3001 \ -e RAILS_ENV \ -e RAILS_BIND \ -v "$DATA_FOLDER":/data \ + -v "$PROJECT_FOLDER":/arquivo \ ghcr.io/phillmv/arquivo-development:latest ${DOCKER_COMMAND} From 3d306005ca38ae5f6ea004c3d4678614a12465f5 Mon Sep 17 00:00:00 2001 From: Phill MV Date: Fri, 29 Nov 2024 16:35:24 -0500 Subject: [PATCH 18/25] added mawl console --- bin/start-mawl | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/bin/start-mawl b/bin/start-mawl index a49d39e1..f423717d 100755 --- a/bin/start-mawl +++ b/bin/start-mawl @@ -42,7 +42,20 @@ else export STATIC_PLS=1 DOCKER_COMMAND= + elif [ "$1" = "console" ]; then + shift + MAWL_INPUT_PATH=`realpath "$1"` + + if [ -z "${2+x}" ]; then + MAWL_OUTPUT_PATH="$PROJECT_FOLDER"/tmp/mawl-output + else + MAWL_OUTPUT_PATH="$2" + fi + + export STATIC_PLS=1 + DOCKER_COMMAND=bash fi + fi export ARQUIVO_USER=phillmv From 74d1ec391422360a1d598c22aa8520fac941d5c9 Mon Sep 17 00:00:00 2001 From: Phill MV Date: Fri, 29 Nov 2024 16:37:29 -0500 Subject: [PATCH 19/25] wip: Hackily added the ability to live-reload stylesheets in static mode. --- .../static_site/entries_controller.rb | 2 +- app/models/ad_hoc_markdown_importer.rb | 21 +++------------- app/models/entry.rb | 24 +++++++++++++++++++ 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/app/controllers/static_site/entries_controller.rb b/app/controllers/static_site/entries_controller.rb index 4a986e1b..4e2b874d 100644 --- a/app/controllers/static_site/entries_controller.rb +++ b/app/controllers/static_site/entries_controller.rb @@ -10,7 +10,7 @@ def show serve_blob(blob) elsif @entry.manifest? - render plain: @entry.body + render plain: @entry.render_stylesheet!, content_type: 'text/css' # TODO: make up my mind on how to handle templates. # elsif @entry.template? diff --git a/app/models/ad_hoc_markdown_importer.rb b/app/models/ad_hoc_markdown_importer.rb index c8d9e7e5..d68a1928 100644 --- a/app/models/ad_hoc_markdown_importer.rb +++ b/app/models/ad_hoc_markdown_importer.rb @@ -103,7 +103,6 @@ def entry_attributes_from_document(identifier, file_path) if identifier == "stylesheets/application.css.scss" # TODO: should this also be a "template"? doesn't super matter. entry_kind = :manifest - entry_body = File.read(file_path) elsif identifier =~ /\.erb$/ # idea is that templates are rendered from within context of a # controller, which is too painful to setup here @@ -140,29 +139,15 @@ def process_templates(notebook) # if there is a stylesheets/application.css.scss we want to render the # Sass and convert it to a stylesheets/application.css if stylesheet = notebook.entries.manifests.find_by(identifier: "stylesheets/application.css.scss") - load_path = File.join(notebook.import_path, "stylesheets") - - rendered_css = SassC::Engine.new(stylesheet.body, { - filename: "application.css.scss", - syntax: :scss, - load_paths: [load_path], - }).render - # there can only be ONE application.css if to_delete = notebook.entries.find_by(identifier: "stylesheets/application.css") puts "Destroying extraneous stylesheets/application.css, so it can be replaced." to_delete.destroy end - rendered_stylesheet = notebook.entries.new(stylesheet.export_attributes) - rendered_stylesheet.identifier = "stylesheets/application.css" - rendered_stylesheet.kind = :document - rendered_stylesheet.save! - - blob = ActiveStorage::Blob.create_and_upload!(io: StringIO.new(rendered_css), - filename: "application.css") - blob.analyze - rendered_stylesheet.files.create(blob_id: blob.id, created_at: blob.created_at) + stylesheet.identifier = "stylesheets/application.css" + stylesheet.render_stylesheet! + stylesheet.save end end diff --git a/app/models/entry.rb b/app/models/entry.rb index 64cf0179..875cb7d6 100644 --- a/app/models/entry.rb +++ b/app/models/entry.rb @@ -442,4 +442,28 @@ def self.accepted_attributes "state", "hide"] end + + # ---- hack + SCSS_MANIFEST = "application.css.scss" + def render_stylesheet! + if self.manifest? + load_path = File.join(parent_notebook.import_path, "stylesheets") + manifest_path = File.join(load_path, SCSS_MANIFEST) + + if File.exist?(manifest_path) + if body.nil? || File.mtime(manifest_path) > updated_at + rendered_css = SassC::Engine.new(File.read(manifest_path), { + filename: SCSS_MANIFEST, + syntax: :scss, + load_paths: [load_path], + }).render + + self.body = rendered_css + self.save! + end + end + end + + self.body + end end From 0d9f5fe54662137c188516afcbe389f68be14313 Mon Sep 17 00:00:00 2001 From: Filipa MV Date: Thu, 4 Dec 2025 20:01:55 +0000 Subject: [PATCH 20/25] don't try to divine timestamps from c/mtime, which git does not set. --- app/models/entry_importer.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/models/entry_importer.rb b/app/models/entry_importer.rb index e305415a..5560edff 100644 --- a/app/models/entry_importer.rb +++ b/app/models/entry_importer.rb @@ -61,13 +61,13 @@ def entry_attributes_from_markdown(identifier, md_path) # if still nil, let's look at the file itself if occurred_at.nil? + # TODO: should throw an error maybe? Git does not set ctime. occurred_at = File.ctime(md_path) end end - # TODO: DON'T DO THIS GIT DOESN"T SET CTIME!!!!!! - created_at = parsed_file["created_at"] || File.ctime(md_path) - updated_at = parsed_file["updated_at"] || File.mtime(md_path) + created_at = parsed_file["created_at"] || occurred_at + updated_at = parsed_file["updated_at"] || occurred_at # if we're parsing front-mattered markdown, you don't get to define an # identifier separate from the file's relative path, don't want to deal From c08bcb986833b8e7a4d66826c8a46248f5f80833 Mon Sep 17 00:00:00 2001 From: Filipa MV Date: Thu, 4 Dec 2025 20:19:17 +0000 Subject: [PATCH 21/25] Going to avoid this for now, think of a better solution down the road. --- app/models/entry_renderer.rb | 2 -- .../pipeline_filter/link_relativizer.rb | 29 ------------------- 2 files changed, 31 deletions(-) delete mode 100644 app/models/pipeline_filter/link_relativizer.rb diff --git a/app/models/entry_renderer.rb b/app/models/entry_renderer.rb index 93e07ca6..0671f02e 100644 --- a/app/models/entry_renderer.rb +++ b/app/models/entry_renderer.rb @@ -174,7 +174,6 @@ def render_body(opt = {}) PipelineFilter::MarkdownFilter, # convert to HTML PipelineFilter::WikiLinkFilter, HTML::Pipeline::SanitizationFilter, # strip scary tags - PipelineFilter::LinkRelativizer, PipelineFilter::MyTaskListFilter, # convert task markdown to html PipelineFilter::HashtagFilter, # link hashtags PipelineFilter::MentionFilter, # link mentions @@ -192,7 +191,6 @@ def render_body(opt = {}) PipelineFilter::MarkdownFilter, # convert to HTML PipelineFilter::SubjectExtractorFilter, PipelineFilter::WikiLinkFilter, - PipelineFilter::LinkRelativizer, # Here we commented out: HTML::Pipeline::SanitizationFilte PipelineFilter::MyTaskListFilter, # convert task markdown to html PipelineFilter::HashtagFilter, # link hashtags diff --git a/app/models/pipeline_filter/link_relativizer.rb b/app/models/pipeline_filter/link_relativizer.rb deleted file mode 100644 index 1721a384..00000000 --- a/app/models/pipeline_filter/link_relativizer.rb +++ /dev/null @@ -1,29 +0,0 @@ -require 'html/pipeline' - -class PipelineFilter::LinkRelativizer < HTML::Pipeline::Filter - - # TODO: test his somewhat insane behaviour; needs to ignore links to other sites - # TODO: probably needs some other optional switch? - # TODO: needs to support stylesheets, video, audio, etc - # i.e. https://www.w3schools.com/tags/att_src.asp / https://www.w3schools.com/tags/att_href.asp - def call - if !Arquivo.static? && context[:entry] - prefix = "/#{context[:entry].parent_notebook.name_with_owner}" - doc.css("a[href^='/']:not([href^='#{prefix}']), - img[src^='/']:not([src^='#{prefix}'])").each do |tag| - case tag.name - when "a" - attr = "href" - when "img" - attr = "src" - end - - old_path = tag.attributes[attr].value - new_path = Pathname.new(File.join(prefix, old_path)).cleanpath.to_s - tag.attributes[attr].value = new_path - end - end - - doc - end -end From 8d5d20b89ce49ee5087e94c98b8e7cee2212a3a6 Mon Sep 17 00:00:00 2001 From: Filipa MV Date: Thu, 18 Dec 2025 20:28:34 +0000 Subject: [PATCH 22/25] backed out file attachment experiment in non static controller --- app/controllers/entries_controller.rb | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/app/controllers/entries_controller.rb b/app/controllers/entries_controller.rb index 0d979919..07cc76e0 100644 --- a/app/controllers/entries_controller.rb +++ b/app/controllers/entries_controller.rb @@ -1,6 +1,6 @@ class EntriesController < ApplicationController before_action :find_or_build_entry, only: [:show, :edit] - before_action :find_entry, only: [:update, :destroy, :copy] + before_action :find_entry, only: [:update, :destroy, :files, :copy] # GET /entries # GET /entries.json @@ -159,22 +159,7 @@ def destroy end def files - # TODO: test this obviously insane behaviour - # are we dealing with a document entry with `files` in the identifier? - doc_identifier = File.join(params["id"], "files", params["filename"]) - @entry = current_notebook.entries.find_by(identifier: doc_identifier) - - if @entry.nil? - @entry = current_notebook.entries.find_by!(identifier: params[:id]) - end - - # TODO: undo this when we move documents to just reading the file off disk - if @entry.document? - blob = @entry.files.blobs.first - else - blob = @entry.files.blobs.find_by(filename: params[:filename]) - end - + blob = @entry.files.blobs.find_by!(filename: params[:filename]) expires_in ActiveStorage.service_urls_expire_in redirect_to rails_blob_path(blob, disposition: params[:disposition]) end From 23c2257430b1725382a17af94dc845f1b89ac9a5 Mon Sep 17 00:00:00 2001 From: Filipa MV Date: Thu, 18 Dec 2025 20:37:58 +0000 Subject: [PATCH 23/25] what if we fflag this for now? --- app/controllers/static_site/entries_controller.rb | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/app/controllers/static_site/entries_controller.rb b/app/controllers/static_site/entries_controller.rb index 4e2b874d..577d4e27 100644 --- a/app/controllers/static_site/entries_controller.rb +++ b/app/controllers/static_site/entries_controller.rb @@ -38,18 +38,7 @@ def self.controller_path private def set_entry - - # TODO: live-reloading - # see if the file exists and if it does, import it - - # step 1: does it exist as a file? - # step 2: is it markdown or yaml? - # step 2.1: actually this is harder to untangle - # will have to think about how i want to support the "normal" dump o yaml - # vs the "adhoc" markdown - # step 3: parse it & add it. - - if Rails.env.development? + if ENV["FFLAG_RELOAD"] @entry = EntryImporter.new(current_notebook).resolve_and_import!(params[:id]) if @entry @@ -89,7 +78,7 @@ def serve_blob(blob) end response.headers["Content-Type"] = content_type || ActiveStorage::BaseController::DEFAULT_SEND_FILE_TYPE - # response.headers["Content-Disposition"] = disposition || ActiveStorage::BaseController::DEFAULT_SEND_FILE_DISPOSITION + response.headers["Content-Disposition"] = disposition || ActiveStorage::BaseController::DEFAULT_SEND_FILE_DISPOSITION end end end From 6911fe6c9b20d76392cb571035a226633b1adfe5 Mon Sep 17 00:00:00 2001 From: Filipa MV Date: Thu, 18 Dec 2025 20:41:14 +0000 Subject: [PATCH 24/25] i did figure out a better way to handle templates: in the renderer! --- app/controllers/static_site/entries_controller.rb | 6 +----- app/models/entry_renderer.rb | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/app/controllers/static_site/entries_controller.rb b/app/controllers/static_site/entries_controller.rb index 577d4e27..68074509 100644 --- a/app/controllers/static_site/entries_controller.rb +++ b/app/controllers/static_site/entries_controller.rb @@ -12,15 +12,11 @@ def show elsif @entry.manifest? render plain: @entry.render_stylesheet!, content_type: 'text/css' - # TODO: make up my mind on how to handle templates. - # elsif @entry.template? - # don't love it but fix later, lol do not deploy this to untrusted user contexts??? - # render inline: @entry.body, layout: "application" - elsif @entry.note? || @entry.bookmark? || @entry.template? @show_thread = params[:thread].present? @renderer = EntryRenderer.new(@entry, remove_subject: true) @current_date = @entry.occurred_at.strftime("%Y-%m-%d") + else render plain: "", status: 404 end diff --git a/app/models/entry_renderer.rb b/app/models/entry_renderer.rb index 0671f02e..0d7f1836 100644 --- a/app/models/entry_renderer.rb +++ b/app/models/entry_renderer.rb @@ -1,7 +1,7 @@ require 'task_list/filter' class EntryRenderer attr_accessor :entry, :output, :html - + # avail options: # todo_only: true # smart_punctuation: true @@ -33,12 +33,12 @@ def pipeline(opt = {}) end end + # used for rendering ERB but with access to helpers class EntryContext include ActionView::Context include ActionView::Helpers include ActionView::RoutingUrlFor include Rails.application.routes.url_helpers - # include ActionDispatch::Routing::UrlFor include UrlHelper attr_reader :entry From d16750bbfba6f7714cebf690870880808068bac0 Mon Sep 17 00:00:00 2001 From: Filipa MV Date: Thu, 18 Dec 2025 20:42:41 +0000 Subject: [PATCH 25/25] minor cleanup --- config/environments/development.rb | 2 -- config/environments/production.rb | 2 -- config/environments/static.rb | 1 - 3 files changed, 5 deletions(-) diff --git a/config/environments/development.rb b/config/environments/development.rb index 63906716..fd1d5b23 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -9,9 +9,7 @@ end config.hosts << "arquivo.io" - # until we figure out how to multitenant this, config.active_storage.routes_prefix = "_" - # config.active_storage.routes_prefix = "#{ENV['ARQUIVO_USER']}/_" || "/phillmv/_" # Settings specified here will take precedence over those in config/application.rb. diff --git a/config/environments/production.rb b/config/environments/production.rb index c0ebfc2f..1dee0ea0 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -9,9 +9,7 @@ config.active_record.sqlite3_production_warning=false - # until we figure out how to multitenant this, config.active_storage.routes_prefix = "_" - # config.active_storage.routes_prefix = "#{ENV['ARQUIVO_USER']}/_" || "/phillmv/_" # Settings specified here will take precedence over those in config/application.rb. diff --git a/config/environments/static.rb b/config/environments/static.rb index 2a6758d7..10318245 100644 --- a/config/environments/static.rb +++ b/config/environments/static.rb @@ -1,7 +1,6 @@ Rails.application.configure do config.hosts << "arquivo.io" - # until we figure out how to multitenant this, config.active_storage.routes_prefix = "_" # Settings specified here will take precedence over those in config/application.rb.