Skip to content

Commit

Permalink
Add overview (#20)
Browse files Browse the repository at this point in the history
Fixes #8
  • Loading branch information
AustinWise authored Jan 29, 2022
1 parent d241624 commit 6f44e34
Show file tree
Hide file tree
Showing 13 changed files with 236 additions and 17 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
WIP
===

* Add overview, which alows browsing files and folders [#8](https://github.com/AustinWise/smeagol/issues/8)
* Don't allow access to files and directories whose name starts with a dot (`.`)

0.2.1 (2021-01-19)
Expand Down
2 changes: 2 additions & 0 deletions frontend/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ cp node_modules/@primer/css/dist/primer.css.map ../static/

cp node_modules/@primer/css/dist/primer.css ../site/css/
cp node_modules/@primer/css/dist/primer.css.map ../site/css/

node ./index.js
5 changes: 5 additions & 0 deletions frontend/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const fs = require('fs');
let octicons = require("@primer/octicons");

fs.writeFileSync(__dirname + '/../static/file.svg', octicons.file.toSVG());
fs.writeFileSync(__dirname + '/../static/file_directory.svg', octicons["file-directory"].toSVG());
32 changes: 31 additions & 1 deletion frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"author": "Austin Wise",
"license": "MIT",
"dependencies": {
"@primer/css": "^19.1.1"
"@primer/css": "^19.1.1",
"@primer/octicons": "^16.3.0"
}
}
6 changes: 3 additions & 3 deletions src/repository.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ use std::{
use crate::error::MyError;

//TODO: it is possible to use a borrowed string? Would that reduce copies?
#[derive(Debug)]
#[derive(Debug, PartialEq, PartialOrd, Eq, Ord)]
pub enum RepositoryItem {
File(String),
Directory(String),
File(String),
}

pub trait Repository: std::fmt::Debug {
Expand All @@ -20,7 +20,7 @@ pub trait Repository: std::fmt::Debug {
}

fn path_element_ok(element: &str) -> bool {
!element.starts_with(".")
!element.starts_with('.')
}

#[derive(Debug)]
Expand Down
95 changes: 83 additions & 12 deletions src/requests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use rocket::http::impl_from_uri_param_identity;
use rocket::http::uri::fmt::Formatter;
use rocket::http::uri::fmt::Path;
use rocket::http::uri::fmt::UriDisplay;
use rocket::http::uri::Origin;
use rocket::http::uri::Segments;
use rocket::http::ContentType;
use rocket::request::FromSegments;
Expand All @@ -12,8 +13,12 @@ use rocket::response::Responder;
use rocket::{Build, Rocket};

use crate::error::MyError;
use crate::repository;
use crate::settings::Settings;
use crate::templates::{render_edit_page, render_page, render_page_placeholder, Breadcrumb};
use crate::templates;
use crate::templates::{
render_edit_page, render_overview, render_page, render_page_placeholder, Breadcrumb,
};
use crate::wiki::Wiki;

struct MarkdownPage<'a> {
Expand Down Expand Up @@ -88,7 +93,7 @@ fn markdown_response(
&title,
edit_url,
&rendered_markdown,
path.create_breadcrumbs(),
path.page_breadcrumbs(),
)?)
}

Expand Down Expand Up @@ -120,6 +125,12 @@ impl<'r> WikiPagePath<'r> {
}
}

fn append_segment(&self, new_seg: &'r str) -> Self {
let mut segments = self.segments.clone();
segments.push(new_seg);
WikiPagePath { segments }
}

fn directories(&self) -> &[&'r str] {
match self.segments.split_last() {
Some((_, dirs)) => dirs,
Expand All @@ -140,18 +151,33 @@ impl<'r> WikiPagePath<'r> {
Some(self.file_name_and_extension()?.1)
}

fn create_breadcrumbs(&self) -> Vec<Breadcrumb<'r>> {
let mut dirs = self.directories();
fn breadcrumbs_helper<F: Fn(&'r [&'r str]) -> Origin>(
&'r self,
mut dirs: &'r [&'r str],
uri_func: F,
) -> Vec<Breadcrumb<'r>> {
let mut ret = Vec::with_capacity(dirs.len());
while let Some((name, next_dirs)) = dirs.split_last() {
let url = uri!(page(WikiPagePath::from_slice(dirs))).to_string();
let url = uri_func(dirs).to_string();
ret.push(Breadcrumb::new(name, url));
dirs = next_dirs;
}
//TODO: put the elements in the list in the correct order
ret.reverse();
ret
}

fn page_breadcrumbs(&'r self) -> Vec<Breadcrumb<'r>> {
self.breadcrumbs_helper(self.directories(), |dirs| {
uri!(page(WikiPagePath::from_slice(dirs)))
})
}

fn overview_breadcrumbs(&'r self) -> Vec<Breadcrumb<'r>> {
self.breadcrumbs_helper(&self.segments, |dirs| {
uri!(overview(WikiPagePath::from_slice(dirs)))
})
}
}

impl<'r> FromSegments<'r> for WikiPagePath<'r> {
Expand Down Expand Up @@ -226,7 +252,7 @@ fn edit_view(path: WikiPagePath, w: Wiki) -> Result<response::content::Html<Stri
&post_url.to_string(),
&view_url.to_string(),
content,
path.create_breadcrumbs(),
path.page_breadcrumbs(),
)?;
Ok(response::content::Html(html))
}
Expand All @@ -249,10 +275,8 @@ fn page(path: WikiPagePath, w: Wiki) -> WikiPageResponder {
}
Err(_) => {
if w.directory_exists(&path.segments).unwrap() {
let mut segments = path.segments;
let file_name = &format!("{}.md", w.settings().index_page());
segments.push(file_name);
let path = WikiPagePath::new(segments);
let file_name = format!("{}.md", w.settings().index_page());
let path = path.append_segment(&file_name);
WikiPageResponder::Redirect(response::Redirect::to(uri!(page(path))))
} else {
match path.file_name_and_extension() {
Expand All @@ -264,7 +288,7 @@ fn page(path: WikiPagePath, w: Wiki) -> WikiPageResponder {
file_stem,
&path.to_string(),
&create_url.to_string(),
path.create_breadcrumbs(),
path.page_breadcrumbs(),
)
.unwrap(),
),
Expand All @@ -280,6 +304,42 @@ fn page(path: WikiPagePath, w: Wiki) -> WikiPageResponder {
}
}

fn overview_inner(path: WikiPagePath, w: Wiki) -> Result<response::content::Html<String>, MyError> {
let mut entries = w.enumerate_files(&path.segments)?;
entries.sort();
let entries = entries;

let directories = entries
.iter()
.filter_map(|e| match e {
repository::RepositoryItem::Directory(name) => {
let url = uri!(overview(path.append_segment(name))).to_string();
Some(templates::DirectoryEntry::new(name, url))
}
_ => None,
})
.collect();

let files = entries
.iter()
.filter_map(|e| match e {
repository::RepositoryItem::File(name) => {
let url = uri!(page(path.append_segment(name))).to_string();
Some(templates::DirectoryEntry::new(name, url))
}
_ => None,
})
.collect();

let html = render_overview("Overview", path.overview_breadcrumbs(), directories, files)?;
Ok(response::content::Html(html))
}

#[get("/overview/<path..>")]
fn overview(path: WikiPagePath, w: Wiki) -> Result<response::content::Html<String>, MyError> {
overview_inner(path, w)
}

#[get("/")]
fn index(w: Wiki) -> response::Redirect {
let file_name = format!("{}.md", w.settings().index_page());
Expand All @@ -288,7 +348,7 @@ fn index(w: Wiki) -> response::Redirect {
}

pub fn mount_routes(rocket: Rocket<Build>) -> Rocket<Build> {
rocket.mount("/", routes![page, edit_save, edit_view, index])
rocket.mount("/", routes![page, edit_save, edit_view, overview, index])
}

#[cfg(test)]
Expand Down Expand Up @@ -374,4 +434,15 @@ mod tests {
assert!(extensionless_file.directories().is_empty());
assert!(extensionless_file.file_name_and_extension().is_none());
}

#[test]
fn test_wikipath_append() {
let empty = WikiPagePath::new(vec![]);

let folder = empty.append_segment("folder");
assert_eq!(folder.segments, vec!["folder"]);

let file = folder.append_segment("file");
assert_eq!(file.segments, vec!["folder", "file"]);
}
}
50 changes: 50 additions & 0 deletions src/templates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,53 @@ pub fn render_edit_page(
};
template.render()
}

pub struct DirectoryEntry<'a> {
name: &'a str,
href: String,
}

impl<'a> DirectoryEntry<'a> {
pub fn new(name: &'a str, href: String) -> Self {
DirectoryEntry {
name,
href,
}
}
}

#[derive(Template)]
#[template(path = "overview.html", escape = "none")]
struct OverviewTemplate<'a> {
primer_css_uri: &'a str,
favicon_png_uri: &'a str,
file_svg: &'a str,
file_directory_svg: &'a str,
title: &'a str,
breadcrumbs: Vec<Breadcrumb<'a>>,
directories: Vec<DirectoryEntry<'a>>,
files: Vec<DirectoryEntry<'a>>,
}

pub fn render_overview(
title: &str,
breadcrumbs: Vec<Breadcrumb<'_>>,
directories: Vec<DirectoryEntry<'_>>,
files: Vec<DirectoryEntry<'_>>,
) -> askama::Result<String> {
let primer_css_uri = &primer_css_uri();
let favicon_png_uri = &favicon_png_uri();
let file_svg = include_str!("../static/file.svg");
let file_directory_svg = include_str!("../static/file_directory.svg");
let template = OverviewTemplate {
primer_css_uri,
favicon_png_uri,
file_svg,
file_directory_svg,
title,
breadcrumbs,
directories,
files,
};
template.render()
}
5 changes: 5 additions & 0 deletions src/wiki.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::sync::Arc;

use crate::error::MyError;
use crate::repository::Repository;
use crate::repository::RepositoryItem;
use crate::settings::Settings;

/// Wiki god object.
Expand Down Expand Up @@ -39,4 +40,8 @@ impl Wiki {
pub fn directory_exists(&self, path: &[&str]) -> Result<bool, MyError> {
self.0.repository.directory_exists(path)
}

pub fn enumerate_files(&self, directory: &[&str]) -> Result<Vec<RepositoryItem>, MyError> {
self.0.repository.enumerate_files(directory)
}
}
1 change: 1 addition & 0 deletions static/file.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions static/file_directory.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions templates/layout.html
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,22 @@
</form>
-->
</div>
<div class="TableObject-item px-2">
<div class="BtnGroup d-flex">
<a class="btn BtnGroup-item btn-sm hide-sm hide-md
minibutton-rename-page" href="/overview">
Overview
</a>
</div>
</div>
{% block page_view_controls %}{% endblock %}
</div>
</header>
<main>
<h1 class="pt-4">{{title}}</h1>
<nav aria-label="Breadcrumb">
<ol>
{% block extra_breadcrumbs %}{% endblock %}
{% for crumb in breadcrumbs %}
<li class="breadcrumb-item">
<a href="{{crumb.href}}">{{crumb.name}}</a>
Expand Down
Loading

0 comments on commit 6f44e34

Please sign in to comment.