Compare commits

...
2 Commits
Author SHA1 Message Date
Riyyi 155dc88620 Create personal website blogpost 2025-04-05 11:02:10 +02:00
Riyyi f44e399748 Tweak Markdown page rendering 2025-04-05 11:01:18 +02:00
11 changed files with 183 additions and 11 deletions
-2
View File
@@ -4,8 +4,6 @@ description: "This is a test article."
navigation: true
---
## Hello World
Foobarbazbuz.
## Component Rendering
+114 -4
View File
@@ -1,10 +1,120 @@
---
title: "Personal Website"
description: "Stuff go here."
description: "Open-source content management system."
navigation: false
---
# Test!
<small>Open-source content management system.<br>
Repository at
[GitHub](https://github.com/riyyi/website),
[GitLab](https://gitlab.com/riyyi/website) or
[Gitea](https://git.riyyi.com/riyyi/website).
</small>
This is another article.
This is the CMS that is used for this website! It's written in PHP 7, MySQL and
jQuery, with the libraries Klein.php and Mailer.
![database design](/img/personal-website/database-design.png)
Features:
- PHP 7
- Composer
- [Klein.php](https://github.com/klein/klein.php)
- [Mailer](https://github.com/txthinking/Mailer)
- MVC design pattern
- MySQL database for storing data
- CMS with CRUD functions for managing data
- ORM for mapping between PHP classes and data
- Login system
- Stay logged in using cookies
- Forget password with a generated link send using mail
- Security mitigations
- Password hashing using BCrypt
- Per-user cryptographically secure generated salt
- SQL injection protection using prepared statements
- XSS
- Cookies set to 'HttpOnly'
- Escape rendered user input using: `htmlentities(ENT_QUOTES | ENT_HTML5, 'UTF-8');`
- CSRF
- Cookies 'SameSite' set to 'Strict'
- Token for each session used in POST/PUT/DELETE requests
- Bootstrap
- jQuery
Directory structure:
```
.
├── app
│ ├── classes
│ │ └── <classes>
│ ├── controllers
│ │ └── <controllers>
│ ├── helper.php
│ ├── model
│ │ └── <models>
│ ├── seed.php
│ ├── traits
│ │ └── Log.php
│ └── views
│ └── <views>
├── composer.json
├── config.php
├── config.php.example
├── public
│ ├── index.php
│ └── <files>
├── route.php
├── syncconfig.sh
├── syncconfig.sh.example
└── sync.sh
```
<div class="row">
<div class="col-12 col-lg-6">
Pictured below is the EER (Enhanced entity-relationship) diagram of the MySQL database:
![website database design](/img/personal-website/database-design.png "website database design"){.img-fluid loading=lazy}
</div>
</div>
Some of the pages of the CMS.
<div class="row">
<div class="col-12 col-lg-6">
Admin menu.
![admin menu](/img/personal-website/admin-menu.png "admin menu")
</div>
<div class="col-12 col-lg-6">
CRUD index page, displaying all the entries of this table, including pagination.
![crud index page](/img/personal-website/crud-index.png "crud index page")
</div>
<div class="col-12 col-lg-6">
CRUD edit page, editing an entry.
![crud edit page](/img/personal-website/crud-edit.png "crud edit page")
</div>
<div class="col-12 col-lg-6">
CRUD show page, show all values of an entry.
![crud show page](/img/personal-website/crud-show.png "crud show page")
</div>
<div class="col-12 col-lg-6">
Login page.
![login page](/img/personal-website/login.png "login page")
</div>
<div class="col-12 col-lg-6">
Password reset page, showing a flash message.
![password reset page](/img/personal-website/reset-password.png "password reset page")
</div>
</div>
-2
View File
@@ -3,6 +3,4 @@ title: "My Second Blog Post"
description: "This is another article."
---
# Test!
This is another article.
Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

+67
View File
@@ -0,0 +1,67 @@
<template>
<a :href="refinedSrc" target="_blank">
<component :is="ImageComponent" :class="props.class" :src="refinedSrc" :alt="props.alt" :loading="props.loading"
:title="props.title" :width="props.width" :height="props.height" v-bind="$attrs" />
</a>
</template>
<script setup lang="ts">
import { withTrailingSlash, withLeadingSlash, joinURL } from "ufo"
import { useRuntimeConfig, computed } from "#imports"
import ImageComponent from "#build/mdc-image-component.mjs"
const props = defineProps({
src: {
type: String,
default: ""
},
alt: {
type: String,
default: ""
},
width: {
type: [String, Number],
default: undefined
},
height: {
type: [String, Number],
default: undefined
},
// Added props compared to upstream
class: {
type: String,
default: "img-fluid"
},
title: {
type: String,
default: ""
},
loading: {
type: String,
default: "lazy" // eager, lazy
},
})
const refinedSrc = computed(() => {
if (props.src?.startsWith("/") && !props.src.startsWith("//")) {
const _base = withLeadingSlash(withTrailingSlash(useRuntimeConfig().app.baseURL))
if (_base !== "/" && !props.src.startsWith(_base)) {
return joinURL(_base, props.src)
}
}
return props.src
})
</script>
<!--
Usage:
![alt text](/img/path-to-img.png "title text"){.img-fluid loading=lazy}
<a href="/img/path-to-img.png" target="_blank">
<img class="img-fluid" src="/img/path-to-img.png" alt="alt text" loading="lazy" title="title text">
</a>
-->
<!-- v-bind="$attrs" will pass through any additional props that haven't been specified in defineProps -->
+2 -3
View File
@@ -4,7 +4,6 @@
<ArticlesTableOfContents v-if="article.navigation" :toc="article.body.toc" />
<h1>{{ article.title }}</h1>
<p>{{ article.description }}</p>
<ContentRenderer :value="article" />
</template>
<template v-else>
@@ -35,7 +34,7 @@
}
/* Select <pre class="shiki"> */
:deep(pre.shiki) {
:deep(pre[class^="language-"]) {
display: flex;
justify-content: space-between;
margin-bottom: 1rem;
@@ -54,7 +53,7 @@
}
/* Select <code> */
:deep(code:not(.shiki):not(pre code)) {
:deep(code:not([class^="language-"]):not(pre code)) {
/* reset font to black */
color: inherit;
}