Compare commits

..

No commits in common. "master" and "9.0.0" have entirely different histories.

26 changed files with 53 additions and 379 deletions

View file

@ -1250,32 +1250,3 @@ details.accordion .inner {
.CodeMirror-focused .CodeMirror-placeholder {
opacity: 50%;
}
.CodeMirror-gutters {
border-color: var(--color-super-lowered) !important;
background-color: var(--color-lowered) !important;
}
.CodeMirror-hints {
background: var(--color-raised) !important;
box-shadow: var(--shadow-x-offset) var(--shadow-y-offset) var(--shadow-size)
var(--color-shadow);
border-radius: var(--radius) !important;
padding: var(--pad-1) !important;
border-color: var(--color-super-lowered) !important;
}
.CodeMirror-hints li {
color: var(--color-text-raised) !important;
border-radius: var(--radius) !important;
transition:
background 0.15s,
color 0.15s;
font-size: 10px;
padding: calc(var(--pad-1) / 2) var(--pad-2);
}
.CodeMirror-hints li.CodeMirror-hint-active {
background-color: var(--color-primary) !important;
color: var(--color-text-primary) !important;
}

View file

@ -1928,7 +1928,7 @@
(text "{%- endif %}")
; note listings
(text "{% for note in notes %} {% if not view_mode or note.title != \"journal.css\" -%}")
(text "{% for note in notes %}")
(div
("class" "flex flex-row gap-1")
(a
@ -1958,6 +1958,6 @@
(icon (text "trash"))
(str (text "general:action.delete")))))
(text "{%- endif %}"))
(text "{%- endif %} {% endfor %}"))
(text "{% endfor %}"))
(text "{%- endif %}")
(text "{%- endmacro %}")

View file

@ -2,27 +2,6 @@
(text "{% if journal -%}") (title (text "{{ journal.title }}")) (text "{% else %}") (title (text "Journals - {{ config.name }}")) (text "{%- endif %}")
(link ("rel" "stylesheet") ("data-turbo-temporary" "true") ("href" "/css/chats.css?v=tetratto-{{ random_cache_breaker }}"))
(style
(text "html, body {
overflow: hidden auto !important;
}
.sidebar {
position: sticky;
top: 42px;
}
@media screen and (max-width: 900px) {
.sidebar {
position: absolute;
top: unset;
}
body.sidebars_shown {
overflow: hidden !important;
}
}"))
(text "{% if view_mode and journal and is_editor -%} {% if note -%}")
; redirect to note
(meta ("http-equiv" "refresh") ("content" "0; url=/@{{ user.username }}/{{ journal.title }}/{{ note.title }}"))
@ -30,11 +9,6 @@
; redirect to journal homepage
(meta ("http-equiv" "refresh") ("content" "0; url=/@{{ user.username }}/{{ journal.title }}"))
(text "{%- endif %} {%- endif %}")
(text "{% if view_mode and journal -%}")
; add journal css
(link ("rel" "stylesheet") ("data-turbo-temporary" "true") ("href" "/api/v1/journals/{{ journal.id }}/journal.css?v=tetratto-{{ random_cache_breaker }}"))
(text "{%- endif %}")
(text "{% endblock %} {% block body %} {{ macros::nav(selected=\"journals\") }}")
(text "{% if not view_mode -%}")
(nav
@ -89,19 +63,17 @@
("href" "/api/v1/auth/user/find/{{ journal.owner }}")
(text "{{ components::avatar(username=journal.owner, selector_type=\"id\", size=\"18px\") }}"))
(text "{% if (view_mode and owner) or not view_mode -%}")
(a
("class" "flush")
("href" "{% if view_mode -%} /@{{ owner.username }}/{{ journal.title }} {%- else -%} /journals/{{ journal.id }}/0 {%- endif %}")
("href" "{% if view_mode -%} /@{{ owner.username }}/{{ journal.title }}/index {%- else -%} /journals/{{ journal.id }}/0 {%- endif %}")
(b (text "{{ journal.title }}")))
(text "{%- endif %}")
(text "{% if note -%}")
(span (text "/"))
(b (text "{{ note.title }}"))
(text "{%- endif %}"))
(text "{% if user and user.id == journal.owner and (not note or note.title != \"journal.css\") -%}")
(text "{% if user and user.id == journal.owner -%}")
(div
("class" "pillmenu")
(a
@ -111,7 +83,7 @@
(icon (text "pencil")))
(a
("class" "{% if view_mode -%}active{%- endif %}")
("href" "/@{{ user.username }}/{{ journal.title }}{% if note -%} /{{ note.title }} {%- endif %}")
("href" "/@{{ user.username }}/{{ journal.title }}/{% if note -%} {{ note.title }} {%- else -%} index {%- endif %}")
(icon (text "eye"))))
(text "{%- endif %}"))
(text "{%- endif %}")
@ -124,7 +96,6 @@
("class" "card w-full flex flex-col gap-2")
(h3 (str (text "journals:label.welcome")))
(span (str (text "journals:label.select_a_journal")))
(span ("class" "mobile") (str (text "journals:label.mobile_click_my_journals")))
(button
("onclick" "create_journal()")
(icon (text "plus"))
@ -209,36 +180,10 @@
; import codemirror
(script ("src" "https://unpkg.com/codemirror@5.39.2/lib/codemirror.js") ("data-turbo-temporary" "true"))
(script ("src" "https://unpkg.com/codemirror@5.39.2/addon/display/placeholder.js") ("data-turbo-temporary" "true"))
(script ("src" "https://unpkg.com/codemirror@5.39.2/mode/markdown/markdown.js") ("data-turbo-temporary" "true"))
(link ("rel" "stylesheet") ("href" "https://unpkg.com/codemirror@5.39.2/lib/codemirror.css") ("data-turbo-temporary" "true"))
(text "{% if note.title == \"journal.css\" -%}")
; css editor
(script ("src" "https://unpkg.com/codemirror@5.39.2/addon/edit/closebrackets.js") ("data-turbo-temporary" "true"))
(script ("src" "https://unpkg.com/codemirror@5.39.2/addon/hint/css-hint.js") ("data-turbo-temporary" "true"))
(script ("src" "https://unpkg.com/codemirror@5.39.2/addon/hint/show-hint.js") ("data-turbo-temporary" "true"))
(script ("src" "https://unpkg.com/codemirror@5.39.2/addon/lint/css-lint.js") ("data-turbo-temporary" "true"))
(script ("src" "https://unpkg.com/codemirror@5.39.2/mode/css/css.js") ("data-turbo-temporary" "true"))
(link ("rel" "stylesheet") ("href" "https://unpkg.com/codemirror@5.39.2/addon/hint/show-hint.css") ("data-turbo-temporary" "true"))
(link ("rel" "stylesheet") ("href" "https://unpkg.com/codemirror@5.39.2/addon/lint/lint.css") ("data-turbo-temporary" "true"))
(style
(text ".CodeMirror {
font-family: monospace !important;
font-size: 16px;
border: solid 1px var(--color-super-lowered);
border-radius: var(--radius);
}
.CodeMirror-line {
padding-left: 5px !important;
}"))
(text "{% else %}")
; markdown editor
(script ("src" "https://unpkg.com/codemirror@5.39.2/mode/markdown/markdown.js") ("data-turbo-temporary" "true"))
(text "{%- endif %}")
; tab bar
(text "{% if note.title != \"journal.css\" -%}")
(div
("class" "pillmenu")
(a
@ -253,7 +198,6 @@
("data-tab-button" "preview")
("data-turbo" "false")
(str (text "journals:label.preview_pane"))))
(text "{%- endif %}")
; tabs
(div
@ -277,15 +221,10 @@
(script ("id" "editor_content") ("type" "text/markdown") (text "{{ note.content|remove_script_tags|safe }}"))
(script
(text "setTimeout(() => {
if (!document.getElementById(\"preview_tab\").shadowRoot) {
document.getElementById(\"preview_tab\").attachShadow({ mode: \"open\" });
}
globalThis.editor = CodeMirror(document.getElementById(\"editor_tab\"), {
value: document.getElementById(\"editor_content\").innerHTML,
mode: \"{% if note.title == 'journal.css' -%} css {%- else -%} markdown {%- endif %}\",
mode: \"markdown\",
lineWrapping: true,
lineNumbers: \"{{ note.title }}\" === \"journal.css\",
autoCloseBrackets: true,
autofocus: true,
viewportMargin: Number.POSITIVE_INFINITY,
@ -293,8 +232,7 @@
highlightFormatting: false,
fencedCodeBlockHighlighting: false,
xml: false,
smartIndent: true,
indentUnit: 4,
smartIndent: false,
placeholder: `# {{ note.title }}`,
extraKeys: {
Home: \"goLineLeft\",
@ -305,15 +243,6 @@
},
});
editor.on(\"keydown\", (cm, e) => {
if (e.key.length > 1) {
// ignore all keys that aren't a letter
return;
}
CodeMirror.showHint(cm, CodeMirror.hint.css);
});
document.querySelector(\"[data-tab-button=editor]\").addEventListener(\"click\", async (e) => {
e.preventDefault();
trigger(\"atto::hooks::tabs:switch\", [\"editor\"]);
@ -333,10 +262,7 @@
})
).text();
const preview_token = window.crypto.randomUUID();
document.getElementById(\"preview_tab\").shadowRoot.innerHTML = `${res}<style>
@import url(\"/api/v1/journals/{{ journal.id }}/journal.css?v=preview-${preview_token}\");
</style>`;
document.getElementById(\"preview_tab\").innerHTML = res;
trigger(\"atto::hooks::tabs:switch\", [\"preview\"]);
});
}, 150);"))
@ -346,34 +272,7 @@
("class" "flex flex-col gap-2 card")
(text "{{ note.content|markdown|safe }}"))
(div
("class" "flex w-full justify-between gap-2")
(span (text "Last updated: ") (span ("class" "date") (text "{{ note.edited }}")))
(text "{% if user and user.id == owner.id -%}")
(button
("class" "small")
("onclick" "{% if journal.privacy == \"Public\" -%}
trigger('atto::copy_text', ['{{ config.host }}/@{{ owner.username }}/{{ journal.title }}/{{ note.title }}'])
{%- else -%}
prompt_make_public();
trigger('atto::copy_text', ['{{ config.host }}/@{{ owner.username }}/{{ journal.title }}/{{ note.title }}'])
{%- endif %}")
(icon (text "share"))
(str (text "general:label.share")))
(script
(text "globalThis.prompt_make_public = async () => {
if (
!(await trigger(\"atto::confirm\", [
\"Would you like to make this journal public? This is required for others to view this note.\",
]))
) {
return;
}
change_journal_privacy({ target: { selectedOptions: [{ value: \"Public\" }] }, preventDefault: () => {} });
}"))
(text "{%- endif %}"))
(span (text "Last updated: ") (span ("class" "date") (text "{{ note.edited }}")))
(text "{%- endif %}")
(text "{%- endif %}")))
(style
@ -433,7 +332,7 @@
},
body: JSON.stringify({
title,
content: title === \"journal.css\" ? `/* ${title} */\\n` : `# ${title}`,
content: `# ${title}`,
journal: \"{{ selected_journal }}\",
}),
})
@ -532,8 +431,8 @@
globalThis.change_journal_privacy = async (e) => {
e.preventDefault();
const selected = e.target.selectedOptions[0];
fetch(\"/api/v1/journals/{% if journal -%} {{ journal.id }} {%- else -%} {{ selected_journal }} {%- endif %}/privacy\", {
const selected = event.target.selectedOptions[0];
fetch(\"/api/v1/journals/{{ selected_journal }}/privacy\", {
method: \"POST\",
headers: {
\"Content-Type\": \"application/json\",

View file

@ -44,10 +44,9 @@
("ui_ident" "io_data_load")
(div ("ui_ident" "io_data_marker"))))
(text "{% set paged = user and user.settings.paged_timelines %}")
(script
(text "setTimeout(() => {
trigger(\"ui::io_data_load\", [\"/_swiss_army_timeline?user_id={{ profile.id }}&tag={{ tag }}&page=\", Number.parseInt(\"{{ page }}\") - 1, \"{{ paged }}\" === \"true\"]);
trigger(\"ui::io_data_load\", [\"/_swiss_army_timeline?user_id={{ profile.id }}&tag={{ tag }}&page=\", Number.parseInt(\"{{ page }}\") - 1]);
});"))
(text "{% endblock %}")

View file

@ -564,14 +564,14 @@
(li
(text "Use custom CSS on your profile"))
(li
(text "Use community emojis outside of
(text "Ability to use community emojis outside of
their community"))
(li
(text "Upload and use gif emojis"))
(text "Ability to upload and use gif emojis"))
(li
(text "Create infinite stack timelines"))
(li
(text "Upload images to posts"))
(text "Ability to upload images to posts"))
(li
(text "Save infinite post drafts"))
(li
@ -579,7 +579,7 @@
(li
(text "Ability to create forges"))
(li
(text "Create more than 1 app"))
(text "Ability to create more than 1 app"))
(li
(text "Create up to 10 stack blocks"))
(li
@ -587,9 +587,7 @@
(li
(text "Increased proxied image size"))
(li
(text "Create infinite journals"))
(li
(text "Create infinite notes in each journal")))
(text "Create infinite journals")))
(a
("href" "{{ config.stripe.payment_link }}?client_reference_id={{ user.id }}")
("class" "button")
@ -1403,11 +1401,6 @@
\"Hides dislikes on all posts. Users will also no longer be able to dislike your posts.\",
\"text\",
],
[
[\"paged_timelines\", \"Make timelines paged instead of infinitely scrolled\"],
\"{{ profile.settings.paged_timelines }}\",
\"checkbox\",
],
[[], \"Fun\", \"title\"],
[
[\"disable_gpa_fun\", \"Disable GPA\"],

View file

@ -83,10 +83,9 @@
("ui_ident" "io_data_load")
(div ("ui_ident" "io_data_marker")))
(text "{% set paged = user and user.settings.paged_timelines %}")
(script
(text "setTimeout(() => {
trigger(\"ui::io_data_load\", [\"/_swiss_army_timeline?stack_id={{ stack.id }}&page=\", Number.parseInt(\"{{ page }}\") - 1, \"{{ paged }}\" === \"true\"]);
trigger(\"ui::io_data_load\", [\"/_swiss_army_timeline?stack_id={{ stack.id }}&page=\", Number.parseInt(\"{{ page }}\") - 1]);
});"))
(text "{%- endif %}"))))

View file

@ -33,10 +33,9 @@
("ui_ident" "io_data_load")
(div ("ui_ident" "io_data_marker"))))
(text "{% set paged = user and user.settings.paged_timelines %}")
(script
(text "setTimeout(() => {
trigger(\"ui::io_data_load\", [\"/_swiss_army_timeline?tl=AllPosts&page=\", Number.parseInt(\"{{ page }}\") - 1, \"{{ paged }}\" === \"true\"]);
trigger(\"ui::io_data_load\", [\"/_swiss_army_timeline?tl=AllPosts&page=\", Number.parseInt(\"{{ page }}\") - 1]);
});"))
(text "{% endblock %}")

View file

@ -11,10 +11,9 @@
("ui_ident" "io_data_load")
(div ("ui_ident" "io_data_marker"))))
(text "{% set paged = user and user.settings.paged_timelines %}")
(script
(text "setTimeout(() => {
trigger(\"ui::io_data_load\", [\"/_swiss_army_timeline?tl=FollowingPosts&page=\", Number.parseInt(\"{{ page }}\") - 1, \"{{ paged }}\" === \"true\"]);
trigger(\"ui::io_data_load\", [\"/_swiss_army_timeline?tl=FollowingPosts&page=\", Number.parseInt(\"{{ page }}\") - 1]);
});"))
(text "{% endblock %}")

View file

@ -31,10 +31,9 @@
(div ("ui_ident" "io_data_marker")))
(text "{%- endif %}"))
(text "{% set paged = user and user.settings.paged_timelines %}")
(script
(text "setTimeout(() => {
trigger(\"ui::io_data_load\", [\"/_swiss_army_timeline?tl=MyCommunities&page=\", Number.parseInt(\"{{ page }}\") - 1, \"{{ paged }}\" === \"true\"]);
trigger(\"ui::io_data_load\", [\"/_swiss_army_timeline?tl=MyCommunities&page=\", Number.parseInt(\"{{ page }}\") - 1]);
});"))
(text "{% endblock %}")

View file

@ -11,10 +11,9 @@
("ui_ident" "io_data_load")
(div ("ui_ident" "io_data_marker"))))
(text "{% set paged = user and user.settings.paged_timelines %}")
(script
(text "setTimeout(() => {
trigger(\"ui::io_data_load\", [\"/_swiss_army_timeline?tl=PopularPosts&page=\", Number.parseInt(\"{{ page }}\") - 1, \"{{ paged }}\" === \"true\"]);
trigger(\"ui::io_data_load\", [\"/_swiss_army_timeline?tl=PopularPosts&page=\", Number.parseInt(\"{{ page }}\") - 1]);
});"))
(text "{% endblock %}")

View file

@ -30,7 +30,3 @@
(str (text "chats:label.go_back")))
(text "{%- endif %}"))
(text "{%- endif %}")
(text "{% if paginated -%}")
(text "{{ components::pagination(page=page, items=list|length) }}")
(text "{%- endif %}")

View file

@ -1141,7 +1141,7 @@ ${option.input_element_type === "textarea" ? `${option.value}</textarea>` : ""}
},
);
self.define("io_data_load", (_, tmpl, page, paginated_mode = false) => {
self.define("io_data_load", (_, tmpl, page) => {
self.IO_DATA_MARKER = document.querySelector(
"[ui_ident=io_data_marker]",
);
@ -1164,16 +1164,7 @@ ${option.input_element_type === "textarea" ? `${option.value}</textarea>` : ""}
self.IO_DATA_PAGE = page;
self.IO_DATA_SEEN_IDS = [];
if (!paginated_mode) {
self.IO_DATA_OBSERVER.observe(self.IO_DATA_MARKER);
} else {
// immediately load first page
self.IO_DATA_TMPL = self.IO_DATA_TMPL.replace("&page=", "");
self.IO_DATA_TMPL += `&paginated=true&page=`;
self.io_load_data();
}
self.IO_PAGINATED = paginated_mode;
self.IO_DATA_OBSERVER.observe(self.IO_DATA_MARKER);
});
self.define("io_load_data", async () => {

View file

@ -213,7 +213,7 @@ pub async fn upload_avatar_request(
if mime == "image/gif" {
// gif image, don't encode
if img.0.len() > MAXIMUM_GIF_FILE_SIZE {
return Json(Error::FileTooLarge.into());
return Json(Error::DataTooLong("gif".to_string()).into());
}
std::fs::write(&path, img.0).unwrap();
@ -226,7 +226,7 @@ pub async fn upload_avatar_request(
// check file size
if img.0.len() > MAXIMUM_FILE_SIZE {
return Json(Error::FileTooLarge.into());
return Json(Error::DataTooLong("image".to_string()).into());
}
// upload image
@ -314,7 +314,7 @@ pub async fn upload_banner_request(
if mime == "image/gif" {
// gif image, don't encode
if img.0.len() > MAXIMUM_GIF_FILE_SIZE {
return Json(Error::FileTooLarge.into());
return Json(Error::DataTooLong("gif".to_string()).into());
}
std::fs::write(&path, img.0).unwrap();
@ -327,7 +327,7 @@ pub async fn upload_banner_request(
// check file size
if img.0.len() > MAXIMUM_FILE_SIZE {
return Json(Error::FileTooLarge.into());
return Json(Error::DataTooLong("image".to_string()).into());
}
// upload image

View file

@ -136,7 +136,7 @@ pub async fn upload_avatar_request(
// check file size
if img.0.len() > MAXIMUM_FILE_SIZE {
return Json(Error::FileTooLarge.into());
return Json(Error::DataTooLong("image".to_string()).into());
}
// upload image
@ -191,7 +191,7 @@ pub async fn upload_banner_request(
// check file size
if img.0.len() > MAXIMUM_FILE_SIZE {
return Json(Error::FileTooLarge.into());
return Json(Error::DataTooLong("image".to_string()).into());
}
// upload image

View file

@ -133,7 +133,7 @@ pub async fn create_request(
// check sizes
for img in &images {
if img.len() > MAXIMUM_FILE_SIZE {
return Json(Error::FileTooLarge.into());
return Json(Error::DataTooLong("image".to_string()).into());
}
}

View file

@ -9,14 +9,11 @@ use crate::{
routes::api::v1::{UpdateJournalPrivacy, CreateJournal, UpdateJournalTitle},
State,
};
use tetratto_core::{
database::NAME_REGEX,
model::{
journals::{Journal, JournalPrivacyPermission},
oauth,
permissions::FinePermission,
ApiReturn, Error,
},
use tetratto_core::model::{
journals::{Journal, JournalPrivacyPermission},
oauth,
permissions::FinePermission,
ApiReturn, Error,
};
pub async fn get_request(
@ -49,20 +46,6 @@ pub async fn get_request(
})
}
pub async fn get_css_request(
Path(id): Path<usize>,
Extension(data): Extension<State>,
) -> impl IntoResponse {
let data = &(data.read().await).0;
let note = match data.get_note_by_journal_title(id, "journal.css").await {
Ok(x) => x,
Err(e) => return ([("Content-Type", "text/plain")], format!("/* {e} */")),
};
([("Content-Type", "text/css")], note.content)
}
pub async fn list_request(jar: CookieJar, Extension(data): Extension<State>) -> impl IntoResponse {
let data = &(data.read().await).0;
let user = match get_user_from_token!(jar, data, oauth::AppScope::UserReadJournals) {
@ -116,17 +99,7 @@ pub async fn update_title_request(
None => return Json(Error::NotAllowed.into()),
};
props.title = props.title.replace(" ", "_").to_lowercase();
// check name
let regex = regex::RegexBuilder::new(NAME_REGEX)
.multi_line(true)
.build()
.unwrap();
if regex.captures(&props.title).is_some() {
return Json(Error::MiscError("This title contains invalid characters".to_string()).into());
}
props.title = props.title.replace(" ", "_");
// make sure this title isn't already in use
if data

View file

@ -551,7 +551,6 @@ pub fn routes() -> Router {
.route("/journals", post(journals::create_request))
.route("/journals/{id}", get(journals::get_request))
.route("/journals/{id}", delete(journals::delete_request))
.route("/journals/{id}/journal.css", get(journals::get_css_request))
.route("/journals/{id}/title", post(journals::update_title_request))
.route(
"/journals/{id}/privacy",

View file

@ -10,15 +10,12 @@ use crate::{
routes::api::v1::{CreateNote, RenderMarkdown, UpdateNoteContent, UpdateNoteTitle},
State,
};
use tetratto_core::{
database::NAME_REGEX,
model::{
journals::{JournalPrivacyPermission, Note},
oauth,
permissions::FinePermission,
uploads::CustomEmoji,
ApiReturn, Error,
},
use tetratto_core::model::{
journals::{JournalPrivacyPermission, Note},
oauth,
permissions::FinePermission,
uploads::CustomEmoji,
ApiReturn, Error,
};
pub async fn get_request(
@ -138,17 +135,7 @@ pub async fn update_title_request(
Err(e) => return Json(e.into()),
};
props.title = props.title.replace(" ", "_").to_lowercase();
// check name
let regex = regex::RegexBuilder::new(NAME_REGEX)
.multi_line(true)
.build()
.unwrap();
if regex.captures(&props.title).is_some() {
return Json(Error::MiscError("This title contains invalid characters".to_string()).into());
}
props.title = props.title.replace(" ", "_");
// make sure this title isn't already in use
if data

View file

@ -116,7 +116,7 @@ pub async fn view_request(
}
// if we don't have a selected journal, we shouldn't be here probably
if selected_journal.is_empty() | (selected_note == "journal.css") {
if selected_journal.is_empty() {
return Err(Html(
render_error(Error::NotAllowed, &jar, &data, &user).await,
));
@ -207,81 +207,3 @@ pub async fn view_request(
// return
Ok(Html(data.1.render("journals/app.html", &context).unwrap()))
}
/// `/@{owner}/{journal}`
pub async fn index_view_request(
jar: CookieJar,
Extension(data): Extension<State>,
Path((owner, selected_journal)): Path<(String, String)>,
) -> impl IntoResponse {
let data = data.read().await;
let user = match get_user_from_token!(jar, data.0) {
Some(ua) => Some(ua),
None => None,
};
// get owner
let owner = match data.0.get_user_by_username(&owner).await {
Ok(ua) => ua,
Err(e) => {
return Err(Html(render_error(e, &jar, &data, &user).await));
}
};
check_user_blocked_or_private!(user, owner, data, jar);
// get journal and check privacy settings
let journal = match data
.0
.get_journal_by_owner_title(owner.id, &selected_journal)
.await
{
Ok(p) => p,
Err(e) => {
return Err(Html(render_error(e, &jar, &data, &user).await));
}
};
if journal.privacy == JournalPrivacyPermission::Private {
if let Some(ref user) = user {
if user.id != journal.owner {
return Err(Html(
render_error(Error::NotAllowed, &jar, &data, &Some(user.to_owned())).await,
));
}
} else {
return Err(Html(
render_error(Error::NotAllowed, &jar, &data, &user).await,
));
}
}
// ...
let notes = match data.0.get_notes_by_journal(journal.id).await {
Ok(p) => Some(p),
Err(e) => {
return Err(Html(render_error(e, &jar, &data, &user).await));
}
};
let lang = get_lang!(jar, data.0);
let mut context = initial_context(&data.0.0.0, lang, &user).await;
if selected_journal.is_empty() {
context.insert("selected_journal", &0);
} else {
context.insert("selected_journal", &selected_journal);
}
context.insert("selected_note", &0);
context.insert("journal", &journal);
context.insert("owner", &owner);
context.insert("notes", &notes);
context.insert("view_mode", &true);
context.insert("is_editor", &false);
// return
Ok(Html(data.1.render("journals/app.html", &context).unwrap()))
}

View file

@ -576,8 +576,6 @@ pub struct TimelineQuery {
pub user_id: usize,
#[serde(default)]
pub tag: String,
#[serde(default)]
pub paginated: bool,
}
/// `/_swiss_army_timeline`
@ -699,7 +697,6 @@ pub async fn swiss_army_timeline_request(
context.insert("list", &list);
context.insert("page", &req.page);
context.insert("paginated", &req.paginated);
Ok(Html(
data.1
.render("timelines/swiss_army.html", &context)

View file

@ -134,7 +134,7 @@ pub fn routes() -> Router {
// journals
.route("/journals", get(journals::redirect_request))
.route("/journals/{journal}/{note}", get(journals::app_request))
.route("/@{owner}/{journal}", get(journals::index_view_request))
.route("/@{owner}/{journal}", get(journals::view_request))
.route("/@{owner}/{journal}/{note}", get(journals::view_request))
}

View file

@ -1,10 +1,9 @@
use oiseau::{cache::Cache, query_row};
use crate::{
database::common::NAME_REGEX,
model::{
auth::User,
journals::{Journal, JournalPrivacyPermission},
permissions::FinePermission,
journals::{Journal, JournalPrivacyPermission},
Error, Result,
},
};
@ -70,7 +69,7 @@ impl DataManager {
Ok(res.unwrap())
}
const MAXIMUM_FREE_JOURNALS: usize = 5;
const MAXIMUM_FREE_JOURNALS: usize = 15;
/// Create a new journal in the database.
///
@ -84,19 +83,7 @@ impl DataManager {
return Err(Error::DataTooLong("title".to_string()));
}
data.title = data.title.replace(" ", "_").to_lowercase();
// check name
let regex = regex::RegexBuilder::new(NAME_REGEX)
.multi_line(true)
.build()
.unwrap();
if regex.captures(&data.title).is_some() {
return Err(Error::MiscError(
"This title contains invalid characters".to_string(),
));
}
data.title = data.title.replace(" ", "_");
// make sure this title isn't already in use
if self

View file

@ -30,4 +30,3 @@ mod userblocks;
mod userfollows;
pub use drivers::DataManager;
pub use common::NAME_REGEX;

View file

@ -1,5 +1,4 @@
use oiseau::cache::Cache;
use crate::database::common::NAME_REGEX;
use crate::model::{auth::User, journals::Note, permissions::FinePermission, Error, Result};
use crate::{auto_method, DataManager};
use oiseau::{execute, get, params, query_row, query_rows, PostgresRow};
@ -65,8 +64,6 @@ impl DataManager {
Ok(res.unwrap())
}
const MAXIMUM_FREE_NOTES_PER_JOURNAL: usize = 10;
/// Create a new note in the database.
///
/// # Arguments
@ -85,33 +82,7 @@ impl DataManager {
return Err(Error::DataTooLong("content".to_string()));
}
data.title = data.title.replace(" ", "_").to_lowercase();
// check number of notes
let owner = self.get_user_by_id(data.owner).await?;
if !owner.permissions.check(FinePermission::SUPPORTER) {
let journals = self.get_notes_by_journal(data.owner).await?;
if journals.len() >= Self::MAXIMUM_FREE_NOTES_PER_JOURNAL {
return Err(Error::MiscError(
"You already have the maximum number of notes you can have in this journal"
.to_string(),
));
}
}
// check name
let regex = regex::RegexBuilder::new(NAME_REGEX)
.multi_line(true)
.build()
.unwrap();
if regex.captures(&data.title).is_some() {
return Err(Error::MiscError(
"This title contains invalid characters".to_string(),
));
}
data.title = data.title.replace(" ", "_");
// make sure this title isn't already in use
if self

View file

@ -231,9 +231,6 @@ pub struct UserSettings {
/// A list of strings the user has muted.
#[serde(default)]
pub muted: Vec<String>,
/// If timelines are paged instead of infinitely scrolled.
#[serde(default)]
pub paged_timelines: bool,
}
fn mime_avif() -> String {
@ -335,7 +332,7 @@ impl User {
// parse
for char in input.chars() {
if ((char == '\\') | (char == '/')) && !escape {
if (char == '\\') && !escape {
escape = true;
continue;
}

View file

@ -41,7 +41,6 @@ pub enum Error {
AlreadyAuthenticated,
DataTooLong(String),
DataTooShort(String),
FileTooLarge,
UsernameInUse,
TitleInUse,
QuestionsDisabled,
@ -63,7 +62,6 @@ impl Display for Error {
Self::AlreadyAuthenticated => "Already authenticated".to_string(),
Self::DataTooLong(name) => format!("Given {name} is too long!"),
Self::DataTooShort(name) => format!("Given {name} is too short!"),
Self::FileTooLarge => "Given file is too large".to_string(),
Self::UsernameInUse => "Username in use".to_string(),
Self::TitleInUse => "Title in use".to_string(),
Self::QuestionsDisabled => "You are not allowed to ask questions there".to_string(),