Usage
Working in the admin panel: posts and Markdown, albums and file uploads, authors and tags, SEO, short links, protection mode, the audit log and roles.
All content work happens through /dashboard. You never need to touch code for
anything except the static pages (history, about, projects, educational
programmes) — their markup lives in the templates.
Logging in and roles#
The panel opens at /auth. After logging in the user lands on /dashboard.
| Role | Value in the database | What it can reach |
|---|---|---|
| Moderator | ROLE_MODERATOR | posts, media, authors, tags |
| Administrator | ROLE_ADMIN | all of the above plus users, SEO, site settings, the log, protection mode, cache clearing |
The split is enforced at the controller level: "content" operations live in
StaffController and accept either role, "administrative" ones live in
AdminController behind a check for ROLE_ADMIN specifically.
Заметка
The role is a plain string set when the user is created, and you must type the
full value — ROLE_ADMIN or ROLE_MODERATOR. A typo raises no error: the
user is created, can log in, and then gets a 403 on every page of the panel.
The password must contain at least 8 characters, at least one Latin letter and at least one digit; the login must be at least 4 characters. When editing a user, an empty password field means "keep the current one", not "clear it".
The last administrator cannot be deleted — the server returns 406 and explains why.
The order to fill things in#
The dependencies between entities are strict: a post and an album must have an author, so authors come first and everything else after.
Authors#
/dashboard/authors. An author has a name (2–30 characters) and an optional
"about" text. On the site this produces a page at /author?id=N listing all of
their posts and albums, newest first.
Осторожно
Deleting an author deletes all of their content. The relationship is
configured with orphanRemoval, so every post and every album record
belonging to the author goes away with them. The photo directories are left
behind on disk as orphans and have to be removed by hand. If all you want is
to rename someone, edit the author rather than creating a new one.
Tags#
/dashboard/tags. A tag name is 2–30 characters and must be unique; the
description starts at 4 characters. A tag is shared between posts and albums,
and its page is /tag?id=N.
Deleting a tag is safe: its links to posts and albums are severed automatically beforehand, and the content itself stays where it is.
Posts#
/dashboard/posts. The fields:
| Field | Constraint | Where it shows |
|---|---|---|
| Title | 4–50 characters | in the card, in the page title |
| Introduction | from 4 characters | in the feed card and in the social preview |
| Body | Markdown, required | on the /records/read?id=N page |
| Type | NEWS, STORY, ANNOUNCEMENT | as a localized display label |
| Author | exactly one, required | as a link under the title |
| Tags | any number, optional | in the card and in the filters |
The date is set automatically at creation time and cannot be edited. The "published by" field is filled with the name of the signed-in staff member — which is not the same thing as the author: the author is whose name appears on the site, "published by" is whoever pressed the button. The latter is only visible inside the panel.
The body of a post#
The body is written in Markdown in the EasyMDE editor. An important
characteristic: Markdown is turned into HTML in the reader's browser, not on
the server. The database holds the original Markdown, the /records/read page
serves it as-is, and marked.js renders it on the client.
Two practical consequences follow:
- HTML inside Markdown works — you can drop in a table, a
<details>block or a YouTube clip. The sources you are allowed to embed are limited by the content security policy: YouTube, Rutube, Instagram, Telegram, MAX; - dangerous tags are stripped — with protection enabled the client-side
sanitizer throws out
script,iframe,form,object,svg, event handlers such asonclick, and links using thejavascript:anddata:schemes.
Внимание
iframe is on the list of stripped tags. That means embedding a video
directly into the body of a post will not work while protection is
enabled — the sanitizer removes it. Turning protection off for the sake of
this is a bad trade: without it anyone who can edit posts gains the ability to
run arbitrary scripts in every visitor's browser.
Editing a post changes the title, introduction, body and type. The author and tags of an existing post cannot be changed through the edit form — the edit handler ignores those fields.
Albums: photo and video#
/dashboard/media. This is the least obvious part of the workflow, so here it
is step by step.
- Open the creation form. The browser immediately generates a
batchId— a random UUID. This is the future directory name on disk and the only link between the database record and the files. - Upload the cover. A separate drop zone that accepts exactly one file and
only
.jpg, up to 15 MB. The file is always saved aspreview.jpgno matter what it was called on your machine. This is the album's cover in the feeds. - Upload the contents. The second drop zone accepts images and video. Files go up one at a time and keep their names.
- Fill in the fields and press "Create". The title is 4–50 characters, the
type is
PHOTOorVIDEO, there is exactly one author, and tags are optional.
Важно
The order really is that: the files are physically on disk by the time the record is created. If you close the form without pressing "Create", the directory with the uploaded files stays on the server with no record pointing at it — leftover data that can only be removed from the filesystem by hand.
Things worth knowing about the gallery:
- file order is alphabetical. The only way to control it is through names,
so number them:
01-...,02-.... Otherwise10sorts before2; preview.jpgnever appears in the gallery — it is always excluded from the listing;- a cover is not mandatory. Without
preview.jpgthe fallback image fromweb.storage.default-previewis used, but the album will look anonymous; - the type selects the template, not the contents. An album typed
VIDEOopens in a video player; a request to/media/album?id=Nfor a video album forwards itself to the right template, so you cannot get the link wrong; - editing an album changes only the title and the type. The edit form does not touch the author, the tags or the set of files.
Deleting an album deletes the files from disk — the whole batchId
directory, irreversibly. It is the only operation in the panel that physically
erases data.
SEO#
/dashboard/seo. A title and a meta description are stored for each address.
There are two kinds of entry:
Ordinary pages — /, /records, /media, /history, /about,
/education, /projects. The title and description are used verbatim.
Templates for dynamic pages — /media/album, /media/video_album,
/media/read, /author, /tag. These support placeholders that are replaced
with the data of the specific item:
| Placeholder | Value | Where it is available |
|---|---|---|
<title> | the post or album title | albums, posts |
<author> | the author's name | albums, posts |
<date> | the publication date | albums, posts |
<type> | the post type | posts |
<name> | the author's name or the tag name | author and tag pages |
For example, the template <title> | PHOTO | Our site applied to an album
called "Summer trip" yields Summer trip | PHOTO | Our site.
Заметка
Only title and description are editable through the panel. Keywords, the
Schema.org type and the parent page used for breadcrumbs are set in code on
first startup, and changing them means editing properties.json by hand (with
a restart afterwards) or editing the sources.
sitemap.xml is assembled automatically and includes every static page, every
author, every tag, every post and every album. It is cached for an hour — new
material shows up in the sitemap either after that hour or immediately after a
cache clear.
Site settings#
All of this is on the panel's front page, /dashboard, and is available to
administrators only.
Contacts — phone, address, e-mail. Displayed in the footer and included in the Organization markup for search engines.
Social links — a list of links with a name, a description and an icon. The
icon is given as a path to a file, for example /assets/icons/telegram.svg.
Deletion looks the link up by name, so avoid two entries with the same name.
Notification banner — a strip of text shown on every page. Toggled with a checkbox, the text is arbitrary. Handy for things like "Registration is open until 15 August".
Short links — "from → to" pairs. Add /camp → /records/read?id=42 and you
get a working address at example.org/go/camp. Case does not matter: both the
key and the request are lowercased. Technically this is not a redirect but an
internal forward — the address bar keeps showing /go/camp, which is
convenient for printed materials and QR codes.
Clear cache — a single button that flushes the caches for posts, albums,
authors, tags and the sitemap. Needed if you edited properties.json outside
the panel, or if you want to see changes right away.
Protection mode#
While the site is being filled in, the public part can be closed off. It is
toggled with a checkbox in /dashboard, which also holds a ready-made link with
the token — you can send that to a client for acceptance testing.
Points that matter:
- the token is remembered in the session. Open the token link once and you browse the rest of the site without it. The token itself is stripped from the address bar by a redirect so it does not leak through browser history or somebody else's screenshot;
- staff always pass, so locking yourself out is impossible;
- the panel stays reachable —
/auth,/process_login,/dashboard, the error page,robots.txtand the icons are not part of what gets closed; - the token can be reissued with a button in the panel: earlier links stop working, though sessions that are already open keep their access until they expire;
- protection is not meant for guarding secrets. It is a screen against casual visitors and crawlers, not an access control system.
The audit log#
/dashboard/journal, administrators only. Every significant operation in the
panel is recorded: who, what, when and with which arguments. Successful and
failed logins are logged separately.
Passwords never reach the log — ****** is written instead of the value. The
body of a post is not written in full either, to keep the log from ballooning.
Log entries have no lifetime and accumulate indefinitely. For a site with a few
editors that is not a problem, but clearing out the action_logs table from
time to time is worthwhile.
What the panel cannot change#
An honest list of what requires going into the code and rebuilding:
- the text and layout of the history, about, projects and educational programmes pages — these are Thymeleaf templates with the content written into the markup;
- the domain in
robots.txt; - keywords, Schema.org types and the breadcrumb structure;
- the design, colours and fonts — plain CSS in
src/main/resources/static/assets/; - the page size in the feeds (
page-size, 6 by default) and the cache lifetime.
Совет
Template edits can also be made without rebuilding: uncomment
spring.thymeleaf.prefix=file:/opt/OtryadWebsite/templates/ and the
application will read templates from a directory on disk. Handy for quick text
fixes, but keep an eye on that directory not drifting away from the code
version.