148 lines
4.1 KiB
Crystal
148 lines
4.1 KiB
Crystal
require "uri"
|
|
|
|
require "./errors"
|
|
require "./page/*"
|
|
|
|
# A `Page` is the representation in the wiki of something that can be accessed
|
|
# from an url /pages/*path.
|
|
#
|
|
# It is used to associate path, url and data.
|
|
# Is is can also jails the path into the *OPTIONS.basedir* to be sure that
|
|
# there is no attack by writing files outside of the directory where the pages
|
|
# must be stored.
|
|
class Wikicr::Page
|
|
include Wikicr::Page::TableOfContentReader
|
|
include Wikicr::Page::TagsReader
|
|
|
|
# Directory where the pages are stored
|
|
PAGES_SUB_DIRECTORY = "pages/"
|
|
|
|
# Beginning of the url of a page
|
|
URL_PREFIX = "/pages"
|
|
|
|
# Path of the file that contains the page
|
|
getter path : String
|
|
|
|
# Url of the page (without any prefix)
|
|
getter url : String
|
|
|
|
# Complete Url of the page
|
|
getter real_url : String
|
|
|
|
# Title of the page
|
|
getter title : String
|
|
|
|
# A way to hold the tags without having to compute it again and again
|
|
# you need to fill it yourself (with the wikimd patch)
|
|
property tags : Array(String)
|
|
|
|
def initialize(url : String, real_url : Bool = false, parse_title : Bool = false)
|
|
@tags = [] of String
|
|
url = Page.sanitize(url)
|
|
if real_url
|
|
@real_url = url
|
|
@url = @real_url[URL_PREFIX.size..-1].strip "/"
|
|
else
|
|
@url = url.strip "/"
|
|
@real_url = File.expand_path @url, URL_PREFIX
|
|
end
|
|
@path = Page.url_to_file @url
|
|
@title = File.basename @url
|
|
@title = Page.parse_title(@path) || @title if parse_title && File.exists? @path
|
|
end
|
|
|
|
def self.parse_title(path : String) : String?
|
|
title = File.read(path).split("\n").find { |l| l.starts_with? "# " }
|
|
title && title.strip("# ").strip
|
|
end
|
|
|
|
# basic treatment of the url
|
|
# it is NOT a security measure
|
|
def self.sanitize(url : String)
|
|
Index::Entry.title_to_slug url
|
|
end
|
|
|
|
def parse_title!
|
|
@title = Page.parse_title(@path) || @title if File.exists?(@path)
|
|
end
|
|
|
|
# translate a name ("/test/title" for example)
|
|
# into a file path ("/srv/data/test/ttle.md)
|
|
def self.url_to_file(url : String)
|
|
page_dir = File.expand_path Wikicr::OPTIONS.basedir, PAGES_SUB_DIRECTORY
|
|
page_file = File.expand_path Page.sanitize(url), page_dir
|
|
page_file + ".md"
|
|
end
|
|
|
|
# verify if the *file* is in the current dir (avoid ../ etc.)
|
|
# it will raise a `Error403` if the file is out of the basedir
|
|
def jail
|
|
# TODO: consider security of ".git/"
|
|
|
|
# the @file is already expanded (File.expand_path) in the constructor
|
|
if Wikicr::OPTIONS.basedir != @path[0..(Wikicr::OPTIONS.basedir.size - 1)]
|
|
raise Error403.new "Out of chroot (#{@path} on #{Wikicr::OPTIONS.basedir})"
|
|
end
|
|
self
|
|
end
|
|
|
|
# Get the directory of the *file* (~/data/test/home becomes ~/data/test)
|
|
def dirname
|
|
File.dirname @path
|
|
end
|
|
|
|
# Url without the page itself (/pages/test/home becomes /test)
|
|
def url_dirname
|
|
File.dirname @url
|
|
end
|
|
|
|
# Real url without the page itself (/pages/test/home becomes /pages/test)
|
|
def real_url_dirname
|
|
File.dirname @real_url
|
|
end
|
|
|
|
# Reads the *file* and returns the content.
|
|
def read
|
|
self.jail
|
|
File.read @path
|
|
end
|
|
|
|
# TODO: verify if the new_page already exists
|
|
# Move the current page into another place and commit
|
|
def rename(user : Wikicr::User, new_url) : Page
|
|
self.jail
|
|
new_page = Wikicr::Page.new new_url
|
|
new_page.jail
|
|
Dir.mkdir_p File.dirname new_page.path
|
|
File.rename self.path, new_page.path
|
|
Wikicr::Git.commit! user, message: "rename #{@url}", files: [@path, new_page.path]
|
|
new_page
|
|
end
|
|
|
|
# Writes into the *file*, and commit.
|
|
def write(user : Wikicr::User, body)
|
|
self.jail
|
|
Dir.mkdir_p self.dirname
|
|
is_new = File.exists? @path
|
|
File.write @path, body
|
|
Wikicr::Git.commit! user, message: (is_new ? "create #{@url}" : "update #{@url}"), files: [@path]
|
|
end
|
|
|
|
# Deletes the *file*, and commit
|
|
def delete(user : Wikicr::User)
|
|
self.jail
|
|
File.delete @path
|
|
Wikicr::Git.commit! user, message: "delete #{@url}", files: [@path]
|
|
end
|
|
|
|
# Checks if the *file* exists
|
|
def exists?
|
|
self.jail
|
|
File.exists? @path
|
|
end
|
|
end
|
|
|
|
# require "./users"
|
|
# require "./git"
|
|
# Wikicr::Page.new("testX").write("OK", Wikicr::USERS.load!.find("arthur.poulet@mailoo.org"))
|