ミラーる
This commit is contained in:
376
README-JP.md
376
README-JP.md
@@ -1,188 +1,188 @@
|
|||||||
# Little Beast
|
# Little Beast
|
||||||
シンプル、実践的、アンチ・ブロート
|
シンプル、実践的、アンチ・ブロート
|
||||||
|
|
||||||
## Little Beast とは?
|
## Little Beast とは?
|
||||||
Little Beast は PHP 8.3 以上向けのフレームワークで、076.moe(ゲーム開発会社)と technicalsuwako.moe(社長のブログ)向けに作られました。\
|
Little Beast は PHP 8.3 以上向けのフレームワークで、076.moe(ゲーム開発会社)と technicalsuwako.moe(社長のブログ)向けに作られました。\
|
||||||
メイン考え方は「必要な物だけ拡張し、不要な物は削除する」です。\
|
メイン考え方は「必要な物だけ拡張し、不要な物は削除する」です。\
|
||||||
各コア機能はライブラリに分割されている為、必要な物だけを選び易い設計になっています。\
|
各コア機能はライブラリに分割されている為、必要な物だけを選び易い設計になっています。\
|
||||||
全てのモジュールはゼロから書かれており、データベースは一切必要ありません。
|
全てのモジュールはゼロから書かれており、データベースは一切必要ありません。
|
||||||
|
|
||||||
Little Beast はテクニカル諏訪子がゲーム開発に完全復帰する前の最後の Web プロジェクトです。
|
Little Beast はテクニカル諏訪子がゲーム開発に完全復帰する前の最後の Web プロジェクトです。
|
||||||
|
|
||||||
## Little Beast が「ではない」物
|
## Little Beast が「ではない」物
|
||||||
* 汎用フレームワーク
|
* 汎用フレームワーク
|
||||||
* 使い憎い
|
* 使い憎い
|
||||||
* Web 開発者向け
|
* Web 開発者向け
|
||||||
* 万人向け
|
* 万人向け
|
||||||
* インストールが面倒
|
* インストールが面倒
|
||||||
* 教条的
|
* 教条的
|
||||||
* 民主的に運営される
|
* 民主的に運営される
|
||||||
|
|
||||||
## Little Beast が「持っていない」物
|
## Little Beast が「持っていない」物
|
||||||
* データベース(全てファイルベース)
|
* データベース(全てファイルベース)
|
||||||
* 依存関係
|
* 依存関係
|
||||||
* パッケージマネージャー
|
* パッケージマネージャー
|
||||||
* Docker/Kubernetes/Vagrant/Nix 等のコンテナ
|
* Docker/Kubernetes/Vagrant/Nix 等のコンテナ
|
||||||
* JavaScript
|
* JavaScript
|
||||||
* ブロートウェア
|
* ブロートウェア
|
||||||
* ORM
|
* ORM
|
||||||
* 認証/認可システム
|
* 認証/認可システム
|
||||||
* キューやバックグラウンドジョブ
|
* キューやバックグラウンドジョブ
|
||||||
* クラウド/サーバーレス統合
|
* クラウド/サーバーレス統合
|
||||||
* スキャフォールディング
|
* スキャフォールディング
|
||||||
* コード生成
|
* コード生成
|
||||||
* 抽象化レイヤー
|
* 抽象化レイヤー
|
||||||
* 不要なファイル
|
* 不要なファイル
|
||||||
* DEI、行動規範、その他の差別的慣行
|
* DEI、行動規範、その他の差別的慣行
|
||||||
|
|
||||||
## 独自機能
|
## 独自機能
|
||||||
* データベース不要
|
* データベース不要
|
||||||
* Composer や PEAR 不要
|
* Composer や PEAR 不要
|
||||||
* サーバーオーバーヘッドゼロ
|
* サーバーオーバーヘッドゼロ
|
||||||
* コンテナ不要
|
* コンテナ不要
|
||||||
* Maron テンプレートエンジン
|
* Maron テンプレートエンジン
|
||||||
* カスタム Markdown
|
* カスタム Markdown
|
||||||
* ActivityPub
|
* ActivityPub
|
||||||
* 多言語対応
|
* 多言語対応
|
||||||
* マルチブログ対応
|
* マルチブログ対応
|
||||||
* 100% 綺麗で正しい HTML5 と CSS3
|
* 100% 綺麗で正しい HTML5 と CSS3
|
||||||
* 100% 正しい PHP
|
* 100% 正しい PHP
|
||||||
* SEO フレンドリー
|
* SEO フレンドリー
|
||||||
* モジュラー CSS
|
* モジュラー CSS
|
||||||
* Atom フィード
|
* Atom フィード
|
||||||
* 組み込みテストスイート
|
* 組み込みテストスイート
|
||||||
|
|
||||||
## インストール方法
|
## インストール方法
|
||||||
```sh
|
```sh
|
||||||
cd /var/www/htdocs
|
cd /var/www/htdocs
|
||||||
git clone https://github.com/TechnicalSuwako/LittleBeast.git .
|
git clone https://github.com/TechnicalSuwako/LittleBeast.git .
|
||||||
mv config/config.sample.php config/config.php
|
mv config/config.sample.php config/config.php
|
||||||
```
|
```
|
||||||
|
|
||||||
HTTP サーバーを設定して `/public` をルートとして `php-fpm` で実行して下さい。\
|
HTTP サーバーを設定して `/public` をルートとして `php-fpm` で実行して下さい。\
|
||||||
それだけです!
|
それだけです!
|
||||||
|
|
||||||
### OpenBSD サーバー
|
### OpenBSD サーバー
|
||||||
```sh
|
```sh
|
||||||
pkg_add php-8.4.14 php-gmp-8.4.14
|
pkg_add php-8.4.14 php-gmp-8.4.14
|
||||||
rcctl enable php84_fpm httpd relayd
|
rcctl enable php84_fpm httpd relayd
|
||||||
rcctl start php84_fpm httpd relayd
|
rcctl start php84_fpm httpd relayd
|
||||||
```
|
```
|
||||||
|
|
||||||
#### httpd
|
#### httpd
|
||||||
```
|
```
|
||||||
server "technicalsuwako.moe" {
|
server "technicalsuwako.moe" {
|
||||||
listen on * tls port 8443
|
listen on * tls port 8443
|
||||||
gzip-static
|
gzip-static
|
||||||
tls {
|
tls {
|
||||||
certificate "/etc/ssl/technicalsuwako.moe.crt"
|
certificate "/etc/ssl/technicalsuwako.moe.crt"
|
||||||
key "/etc/ssl/private/technicalsuwako.moe.key"
|
key "/etc/ssl/private/technicalsuwako.moe.key"
|
||||||
}
|
}
|
||||||
root "/htdocs/technicalsuwako.moe/www/public"
|
root "/htdocs/technicalsuwako.moe/www/public"
|
||||||
directory index "index.php"
|
directory index "index.php"
|
||||||
location "/.well-known/acme-challenge/*" {
|
location "/.well-known/acme-challenge/*" {
|
||||||
root "/acme"
|
root "/acme"
|
||||||
request strip 2
|
request strip 2
|
||||||
}
|
}
|
||||||
|
|
||||||
location "/*.php" {
|
location "/*.php" {
|
||||||
fastcgi socket "/run/php-fpm.sock"
|
fastcgi socket "/run/php-fpm.sock"
|
||||||
}
|
}
|
||||||
|
|
||||||
location "/*.php[/?]*" {
|
location "/*.php[/?]*" {
|
||||||
fastcgi socket "/run/php-fpm.sock"
|
fastcgi socket "/run/php-fpm.sock"
|
||||||
}
|
}
|
||||||
|
|
||||||
location "/" {
|
location "/" {
|
||||||
directory index "index.php"
|
directory index "index.php"
|
||||||
}
|
}
|
||||||
location match "/blog/" {
|
location match "/blog/" {
|
||||||
request rewrite "/index.php"
|
request rewrite "/index.php"
|
||||||
}
|
}
|
||||||
location match "/about" {
|
location match "/about" {
|
||||||
request rewrite "/index.php"
|
request rewrite "/index.php"
|
||||||
}
|
}
|
||||||
location match "/monero" {
|
location match "/monero" {
|
||||||
request rewrite "/index.php"
|
request rewrite "/index.php"
|
||||||
}
|
}
|
||||||
location match "/secret" {
|
location match "/secret" {
|
||||||
request rewrite "/index.php"
|
request rewrite "/index.php"
|
||||||
}
|
}
|
||||||
location match "/ap/" {
|
location match "/ap/" {
|
||||||
request rewrite "/index.php"
|
request rewrite "/index.php"
|
||||||
}
|
}
|
||||||
location "/.well-known/webfinger" {
|
location "/.well-known/webfinger" {
|
||||||
request rewrite "/index.php"
|
request rewrite "/index.php"
|
||||||
}
|
}
|
||||||
location "/blog.atom" {
|
location "/blog.atom" {
|
||||||
request rewrite "/index.php"
|
request rewrite "/index.php"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
server "www.technicalsuwako.moe" {
|
server "www.technicalsuwako.moe" {
|
||||||
listen on * tls port 8443
|
listen on * tls port 8443
|
||||||
gzip-static
|
gzip-static
|
||||||
tls {
|
tls {
|
||||||
certificate "/etc/ssl/technicalsuwako.moe.crt"
|
certificate "/etc/ssl/technicalsuwako.moe.crt"
|
||||||
key "/etc/ssl/private/technicalsuwako.moe.key"
|
key "/etc/ssl/private/technicalsuwako.moe.key"
|
||||||
}
|
}
|
||||||
block return 301 "https://technicalsuwako.moe$REQUEST_URI"
|
block return 301 "https://technicalsuwako.moe$REQUEST_URI"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### relayd
|
#### relayd
|
||||||
```
|
```
|
||||||
relayd_addr="0.0.0.0"
|
relayd_addr="0.0.0.0"
|
||||||
router_addr="192.168.10.106"
|
router_addr="192.168.10.106"
|
||||||
|
|
||||||
table <httpd> { $router_addr }
|
table <httpd> { $router_addr }
|
||||||
|
|
||||||
http protocol reverse {
|
http protocol reverse {
|
||||||
tcp { nodelay, sack, socket buffer 65536, backlog 100 }
|
tcp { nodelay, sack, socket buffer 65536, backlog 100 }
|
||||||
tls ciphers "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256"
|
tls ciphers "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256"
|
||||||
tls keypair "technicalsuwako.moe"
|
tls keypair "technicalsuwako.moe"
|
||||||
|
|
||||||
return error
|
return error
|
||||||
|
|
||||||
match request header append "X-Forwarded-For" value "$REMOTE_ADDR"
|
match request header append "X-Forwarded-For" value "$REMOTE_ADDR"
|
||||||
match request header append "X-Forwarded-Port" value "$REMOTE_PORT"
|
match request header append "X-Forwarded-Port" value "$REMOTE_PORT"
|
||||||
|
|
||||||
#match response header set "Referrer-Policy" value "same-origin"
|
#match response header set "Referrer-Policy" value "same-origin"
|
||||||
match response header set "X-Frame-Options" value "deny"
|
match response header set "X-Frame-Options" value "deny"
|
||||||
match response header set "X-Content-Type-Options" value "nosniff"
|
match response header set "X-Content-Type-Options" value "nosniff"
|
||||||
match response header set "Referrer-Policy" value "strict-origin-when-cross-origin"
|
match response header set "Referrer-Policy" value "strict-origin-when-cross-origin"
|
||||||
match response header set "Strict-Transport-Security" value "max-age=31536000; includeSubDomains; preload"
|
match response header set "Strict-Transport-Security" value "max-age=31536000; includeSubDomains; preload"
|
||||||
match response header set "Cross-Origin-Opener-Policy" value "same-origin"
|
match response header set "Cross-Origin-Opener-Policy" value "same-origin"
|
||||||
match response header set "Content-Security-Policy" value "img-src 'self' https://*.076.moe http://*.076.moe https://*.technicalsuwako.moe http://*.technicalsuwako.moe; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; object-src 'none';"
|
match response header set "Content-Security-Policy" value "img-src 'self' https://*.076.moe http://*.076.moe https://*.technicalsuwako.moe http://*.technicalsuwako.moe; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; object-src 'none';"
|
||||||
match response header append "Permissions-Policy" value "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=()"
|
match response header append "Permissions-Policy" value "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=()"
|
||||||
|
|
||||||
match request header "Accept-Encoding" value "gzip" tag "gzip"
|
match request header "Accept-Encoding" value "gzip" tag "gzip"
|
||||||
match response header "Content-Type" value "text/*" tag "compress"
|
match response header "Content-Type" value "text/*" tag "compress"
|
||||||
match response header "Content-Type" value "text/html" tag "charset=UTF-8"
|
match response header "Content-Type" value "text/html" tag "charset=UTF-8"
|
||||||
match response header "Content-Type" value "application/javascript" tag "compress"
|
match response header "Content-Type" value "application/javascript" tag "compress"
|
||||||
match response header "Content-Type" value "application/json" tag "compress"
|
match response header "Content-Type" value "application/json" tag "compress"
|
||||||
|
|
||||||
pass request quick header "Host" value "technicalsuwako.moe" forward to <httpd>
|
pass request quick header "Host" value "technicalsuwako.moe" forward to <httpd>
|
||||||
|
|
||||||
pass
|
pass
|
||||||
}
|
}
|
||||||
|
|
||||||
relay www_tls {
|
relay www_tls {
|
||||||
listen on $relayd_addr port 443 tls
|
listen on $relayd_addr port 443 tls
|
||||||
protocol reverse
|
protocol reverse
|
||||||
|
|
||||||
# Default
|
# Default
|
||||||
forward to <httpd> port 8443 check tcp
|
forward to <httpd> port 8443 check tcp
|
||||||
}
|
}
|
||||||
|
|
||||||
relay www_www {
|
relay www_www {
|
||||||
listen on $relayd_addr port 80
|
listen on $relayd_addr port 80
|
||||||
protocol reverse
|
protocol reverse
|
||||||
|
|
||||||
# Default
|
# Default
|
||||||
forward to <httpd> port 8080 check tcp
|
forward to <httpd> port 8080 check tcp
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## 必要な PHP モジュール
|
## 必要な PHP モジュール
|
||||||
* php_gmp
|
* php_gmp
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
title: テーブルの例
|
title: テーブルの例
|
||||||
uuid: 684d234e-f0d6-4da0-8f38-71c7105262d5
|
uuid: 684d234e-f0d6-4da0-8f38-71c7105262d5
|
||||||
author: Little-san
|
author: Little-san
|
||||||
date: 2025-04-11 08:50:57
|
date: 2025-04-11 08:50:57
|
||||||
category: test,css
|
category: test,css
|
||||||
css: table
|
css: table
|
||||||
----
|
----
|
||||||
テーブルの例だ。
|
テーブルの例だ。
|
||||||
複数CSSファイルを含むには、コンマで分けて下さい。
|
複数CSSファイルを含むには、コンマで分けて下さい。
|
||||||
例えば: `css: table,search`
|
例えば: `css: table,search`
|
||||||
|
|
||||||
## プログラミング言語のランキング
|
## プログラミング言語のランキング
|
||||||
| プログラミング言語 | ランキング | 理由 |
|
| プログラミング言語 | ランキング | 理由 |
|
||||||
|-------|-----|----|
|
|-------|-----|----|
|
||||||
| C | ★★★★★ | どこでも使える |
|
| C | ★★★★★ | どこでも使える |
|
||||||
| C++ | ★★★★★ | たーのし~ |
|
| C++ | ★★★★★ | たーのし~ |
|
||||||
| PHP | ★★★★☆ | Little BeastをPHPで作ったから |
|
| PHP | ★★★★☆ | Little BeastをPHPで作ったから |
|
||||||
| Go | ★★★☆☆ | 言語は問題ないけど、会社は親DEI・・・ |
|
| Go | ★★★☆☆ | 言語は問題ないけど、会社は親DEI・・・ |
|
||||||
| Ruby | ★☆☆☆☆ | 遅過ぎる |
|
| Ruby | ★☆☆☆☆ | 遅過ぎる |
|
||||||
| Javascript | ★☆☆☆☆ | 🤡 |
|
| Javascript | ★☆☆☆☆ | 🤡 |
|
||||||
| Rust | ★☆☆☆☆ | ゲイ!! |
|
| Rust | ★☆☆☆☆ | ゲイ!! |
|
||||||
| Zig | ★★★☆☆ | 良いけど、未だ開発中 |
|
| Zig | ★★★☆☆ | 良いけど、未だ開発中 |
|
||||||
@@ -1,20 +1,20 @@
|
|||||||
.fraction {
|
.fraction {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: 0 0.2em;
|
margin: 0 0.2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fraction .numerator, .fraction .denominator {
|
.fraction .numerator, .fraction .denominator {
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fraction .numerator {
|
.fraction .numerator {
|
||||||
border-bottom: 1px solid #fcfcfc;
|
border-bottom: 1px solid #fcfcfc;
|
||||||
}
|
}
|
||||||
|
|
||||||
.algebraic {
|
.algebraic {
|
||||||
font-family: 'Times New Roman', serif;
|
font-family: 'Times New Roman', serif;
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
.blink {
|
.blink {
|
||||||
animation: blinker 1s linear infinite;
|
animation: blinker 1s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes blinker {
|
@keyframes blinker {
|
||||||
50% {
|
50% {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
blockquote {
|
blockquote {
|
||||||
background: #121012;
|
background: #121012;
|
||||||
border: 2px solid #f545f5;
|
border: 2px solid #f545f5;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
border-left: 12px solid #c016c6;
|
border-left: 12px solid #c016c6;
|
||||||
margin: 1.5em 10px;
|
margin: 1.5em 10px;
|
||||||
padding: 0.5em 10px;
|
padding: 0.5em 10px;
|
||||||
}
|
}
|
||||||
@@ -1,36 +1,36 @@
|
|||||||
.blog-type > p {
|
.blog-type > p {
|
||||||
font-weight: bolder;
|
font-weight: bolder;
|
||||||
}
|
}
|
||||||
|
|
||||||
a.blog-type-btn {
|
a.blog-type-btn {
|
||||||
background-color: #550f75;
|
background-color: #550f75;
|
||||||
color: #120f12;
|
color: #120f12;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
border: 1px solid #fcfcfc;
|
border: 1px solid #fcfcfc;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
margin: 4px;
|
margin: 4px;
|
||||||
transition: background-color 0.9s;
|
transition: background-color 0.9s;
|
||||||
}
|
}
|
||||||
|
|
||||||
a.blog-type-btn.active {
|
a.blog-type-btn.active {
|
||||||
background-color: #c016c6;
|
background-color: #c016c6;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr.blog-type-line {
|
hr.blog-type-line {
|
||||||
border: 3px dotted #c016c6;
|
border: 3px dotted #c016c6;
|
||||||
}
|
}
|
||||||
|
|
||||||
a.blog-type-btn:hover {
|
a.blog-type-btn:hover {
|
||||||
background-color: #ea79d8;
|
background-color: #ea79d8;
|
||||||
}
|
}
|
||||||
|
|
||||||
a.blog-type-btn.active:ae6bdb {
|
a.blog-type-btn.active:ae6bdb {
|
||||||
background-color: #ae6bdb;
|
background-color: #ae6bdb;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 768px) {
|
@media only screen and (max-width: 768px) {
|
||||||
a.blog-type-btn {
|
a.blog-type-btn {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,40 +1,40 @@
|
|||||||
.diff-table {
|
.diff-table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
color: #fcfcfc;
|
color: #fcfcfc;
|
||||||
}
|
}
|
||||||
|
|
||||||
.diff-table td {
|
.diff-table td {
|
||||||
border: 1px solid #bcb4bc;
|
border: 1px solid #bcb4bc;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
.diff-header th {
|
.diff-header th {
|
||||||
border: 1px solid #bcb4bc;
|
border: 1px solid #bcb4bc;
|
||||||
}
|
}
|
||||||
|
|
||||||
.line-number {
|
.line-number {
|
||||||
width: 50px;
|
width: 50px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
color: #c016c6;
|
color: #c016c6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.removed {
|
.removed {
|
||||||
background-color: #fa9faa;
|
background-color: #fa9faa;
|
||||||
color: #b61729;
|
color: #b61729;
|
||||||
}
|
}
|
||||||
|
|
||||||
.added {
|
.added {
|
||||||
background-color: #88ecc1;
|
background-color: #88ecc1;
|
||||||
color: #2c980c;
|
color: #2c980c;
|
||||||
}
|
}
|
||||||
|
|
||||||
.context {
|
.context {
|
||||||
background-color: #232320;
|
background-color: #232320;
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty {
|
.empty {
|
||||||
background-color: #746c75;
|
background-color: #746c75;
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
hr {
|
hr {
|
||||||
border: 1px solid #ea79d8;
|
border: 1px solid #ea79d8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pager {
|
.pager {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(2, 1fr);
|
grid-template-columns: repeat(2, 1fr);
|
||||||
}
|
}
|
||||||
|
|
||||||
.prev {
|
.prev {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.next {
|
.next {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
@@ -1,94 +1,94 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Site\Controller;
|
namespace Site\Controller;
|
||||||
|
|
||||||
use Site\Controller\BlogPost;
|
use Site\Controller\BlogPost;
|
||||||
use Site\Lib\Markdown;
|
use Site\Lib\Markdown;
|
||||||
|
|
||||||
class Atom extends BlogPost {
|
class Atom extends BlogPost {
|
||||||
private string $domain = 'technicalsuwako.moe';
|
private string $domain = 'technicalsuwako.moe';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 最新の5記事のAtomフィードを生成する
|
* 最新の5記事のAtomフィードを生成する
|
||||||
*
|
*
|
||||||
* @param array $params パラメータ配列
|
* @param array $params パラメータ配列
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function feed(array $params): void {
|
public function feed(array $params): void {
|
||||||
try {
|
try {
|
||||||
// 最新の投稿を取得
|
// 最新の投稿を取得
|
||||||
$posts = $this->getPosts('/blog/');
|
$posts = $this->getPosts('/blog/');
|
||||||
// 最新の5件に制限
|
// 最新の5件に制限
|
||||||
$posts = array_slice($posts, 0, 5);
|
$posts = array_slice($posts, 0, 5);
|
||||||
|
|
||||||
// サイトのドメインを取得
|
// サイトのドメインを取得
|
||||||
$domain = $_SERVER['HTTP_HOST'];
|
$domain = $_SERVER['HTTP_HOST'];
|
||||||
$baseUrl = 'https://'.$domain;
|
$baseUrl = 'https://'.$domain;
|
||||||
|
|
||||||
// 現在の日時(RFC3339形式)
|
// 現在の日時(RFC3339形式)
|
||||||
$published = date('c');
|
$published = date('c');
|
||||||
|
|
||||||
// XMLヘッダーとコンテンツタイプを設定
|
// XMLヘッダーとコンテンツタイプを設定
|
||||||
header('Content-Type: application/atom+xml; charset=utf-8');
|
header('Content-Type: application/atom+xml; charset=utf-8');
|
||||||
|
|
||||||
// Atomフィードの開始部分
|
// Atomフィードの開始部分
|
||||||
echo '<?xml version="1.0" encoding="utf-8"?>'."\n";
|
echo '<?xml version="1.0" encoding="utf-8"?>'."\n";
|
||||||
echo '<feed xmlns="http://www.w3.org/2005/Atom">'."\n";
|
echo '<feed xmlns="http://www.w3.org/2005/Atom">'."\n";
|
||||||
|
|
||||||
// フィードの基本情報
|
// フィードの基本情報
|
||||||
echo ' <title>'.SITEINFO['title'].'</title>'."\n";
|
echo ' <title>'.SITEINFO['title'].'</title>'."\n";
|
||||||
echo ' <link href="'.$baseUrl.'" />'."\n";
|
echo ' <link href="'.$baseUrl.'" />'."\n";
|
||||||
echo ' <link href="'.$baseUrl.'/blog.atom" rel="self" />'."\n";
|
echo ' <link href="'.$baseUrl.'/blog.atom" rel="self" />'."\n";
|
||||||
echo ' <id>'.$baseUrl.'/</id>'."\n";
|
echo ' <id>'.$baseUrl.'/</id>'."\n";
|
||||||
echo ' <published>'.$published.'</published>'."\n";
|
echo ' <published>'.$published.'</published>'."\n";
|
||||||
echo ' <updated>'.$published.'</updated>'."\n";
|
echo ' <updated>'.$published.'</updated>'."\n";
|
||||||
echo ' <author>'."\n";
|
echo ' <author>'."\n";
|
||||||
echo ' <name>'.SITEINFO['title'].'</name>'."\n";
|
echo ' <name>'.SITEINFO['title'].'</name>'."\n";
|
||||||
echo ' </author>'."\n";
|
echo ' </author>'."\n";
|
||||||
|
|
||||||
// 各エントリー(記事)
|
// 各エントリー(記事)
|
||||||
foreach ($posts as $post) {
|
foreach ($posts as $post) {
|
||||||
// 記事の本文を取得(プレーンテキスト)
|
// 記事の本文を取得(プレーンテキスト)
|
||||||
$path = ROOT.'/blog/'.$post['slug'].'.md';
|
$path = ROOT.'/blog/'.$post['slug'].'.md';
|
||||||
$content = '';
|
$content = '';
|
||||||
$postPublished = date('c', strtotime($post['date']));
|
$postPublished = date('c', strtotime($post['date']));
|
||||||
|
|
||||||
if (file_exists($path)) {
|
if (file_exists($path)) {
|
||||||
$fileContent = file_get_contents($path);
|
$fileContent = file_get_contents($path);
|
||||||
$parts = explode('----', $fileContent, 2);
|
$parts = explode('----', $fileContent, 2);
|
||||||
if (count($parts) > 1) {
|
if (count($parts) > 1) {
|
||||||
// 本文をHTMLとして準備
|
// 本文をHTMLとして準備
|
||||||
$md = new Markdown($post['slug'], '/blog/');
|
$md = new Markdown($post['slug'], '/blog/');
|
||||||
$content = $md->parse();
|
$content = $md->parse();
|
||||||
// HTMLタグを取り除かないようにCDATAで囲む
|
// HTMLタグを取り除かないようにCDATAで囲む
|
||||||
$content = '<![CDATA['.$content.']]>';
|
$content = '<![CDATA['.$content.']]>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
echo ' <entry>'."\n";
|
echo ' <entry>'."\n";
|
||||||
echo ' <title>'.htmlspecialchars($post['title']).'</title>'."\n";
|
echo ' <title>'.htmlspecialchars($post['title']).'</title>'."\n";
|
||||||
echo ' <link href="'.$baseUrl.'/blog/'.$post['slug'].'" />'."\n";
|
echo ' <link href="'.$baseUrl.'/blog/'.$post['slug'].'" />'."\n";
|
||||||
echo ' <id>'.$baseUrl.'/blog/'.$post['slug'].'</id>'."\n";
|
echo ' <id>'.$baseUrl.'/blog/'.$post['slug'].'</id>'."\n";
|
||||||
echo ' <published>'.$postPublished.'</published>'."\n";
|
echo ' <published>'.$postPublished.'</published>'."\n";
|
||||||
|
|
||||||
// カテゴリ(タグ)
|
// カテゴリ(タグ)
|
||||||
if (isset($post['category']) && is_array($post['category'])) {
|
if (isset($post['category']) && is_array($post['category'])) {
|
||||||
foreach ($post['category'] as $category) {
|
foreach ($post['category'] as $category) {
|
||||||
echo ' <category term="'.htmlspecialchars($category).'" />'."\n";
|
echo ' <category term="'.htmlspecialchars($category).'" />'."\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 本文(要約または全文)
|
// 本文(要約または全文)
|
||||||
echo ' <content type="html">'.$content.'</content>'."\n";
|
echo ' <content type="html">'.$content.'</content>'."\n";
|
||||||
echo ' </entry>'."\n";
|
echo ' </entry>'."\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
// フィードの終了
|
// フィードの終了
|
||||||
echo '</feed>';
|
echo '</feed>';
|
||||||
exit;
|
exit;
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
header('Content-Type: text/plain; charset=utf-8');
|
header('Content-Type: text/plain; charset=utf-8');
|
||||||
echo 'フィードの作成に失敗: '.$e->getMessage();
|
echo 'フィードの作成に失敗: '.$e->getMessage();
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,66 +1,66 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Site\Controller;
|
namespace Site\Controller;
|
||||||
|
|
||||||
class BlogPost {
|
class BlogPost {
|
||||||
/**
|
/**
|
||||||
* ブログ投稿を取得する
|
* ブログ投稿を取得する
|
||||||
*
|
*
|
||||||
* @return array 投稿の配列
|
* @return array 投稿の配列
|
||||||
*/
|
*/
|
||||||
public function getPosts(string $section): array {
|
public function getPosts(string $section): array {
|
||||||
$path = ROOT.$section;
|
$path = ROOT.$section;
|
||||||
$posts = [];
|
$posts = [];
|
||||||
|
|
||||||
if (!is_dir($path)) return $posts;
|
if (!is_dir($path)) return $posts;
|
||||||
$files = glob($path.'/*.md');
|
$files = glob($path.'/*.md');
|
||||||
|
|
||||||
foreach ($files as $file) {
|
foreach ($files as $file) {
|
||||||
$content = file_get_contents($file);
|
$content = file_get_contents($file);
|
||||||
$parts = explode('----', $content, 2);
|
$parts = explode('----', $content, 2);
|
||||||
if (count($parts) != 2) continue;
|
if (count($parts) != 2) continue;
|
||||||
|
|
||||||
$metadata = [];
|
$metadata = [];
|
||||||
$meta = explode("\n", trim($parts[0]));
|
$meta = explode("\n", trim($parts[0]));
|
||||||
|
|
||||||
foreach ($meta as $line) {
|
foreach ($meta as $line) {
|
||||||
$line = trim($line);
|
$line = trim($line);
|
||||||
if (empty($line)) continue;
|
if (empty($line)) continue;
|
||||||
|
|
||||||
$colonPos = strpos($line, ':');
|
$colonPos = strpos($line, ':');
|
||||||
if ($colonPos === false) continue;
|
if ($colonPos === false) continue;
|
||||||
|
|
||||||
$key = trim(substr($line, 0, $colonPos));
|
$key = trim(substr($line, 0, $colonPos));
|
||||||
$value = trim(substr($line, $colonPos + 1));
|
$value = trim(substr($line, $colonPos + 1));
|
||||||
$value = trim($value, '"\'');
|
$value = trim($value, '"\'');
|
||||||
|
|
||||||
if ($key == 'category') {
|
if ($key == 'category') {
|
||||||
$metadata[$key] = array_map('trim', explode(',', $value));
|
$metadata[$key] = array_map('trim', explode(',', $value));
|
||||||
} else {
|
} else {
|
||||||
$metadata[$key] = $value;
|
$metadata[$key] = $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$articleBody = trim($parts[1]);
|
$articleBody = trim($parts[1]);
|
||||||
$preview = mb_substr(strip_tags($articleBody), 0, 50).'...';
|
$preview = mb_substr(strip_tags($articleBody), 0, 50).'...';
|
||||||
$slug = basename($file, '.md');
|
$slug = basename($file, '.md');
|
||||||
|
|
||||||
$posts[] = [
|
$posts[] = [
|
||||||
'title' => $metadata['title'] ?? '',
|
'title' => $metadata['title'] ?? '',
|
||||||
'date' => $metadata['date'] ?? '',
|
'date' => $metadata['date'] ?? '',
|
||||||
'thumbnail' => $metadata['thumbnail'] ?? '',
|
'thumbnail' => $metadata['thumbnail'] ?? '',
|
||||||
'thumborient' => $metadata['thumborient'] ?? '',
|
'thumborient' => $metadata['thumborient'] ?? '',
|
||||||
'category' => $metadata['category'] ?? [],
|
'category' => $metadata['category'] ?? [],
|
||||||
'uuid' => $metadata['uuid'] ?? '',
|
'uuid' => $metadata['uuid'] ?? '',
|
||||||
'preview' => $preview,
|
'preview' => $preview,
|
||||||
'slug' => $slug,
|
'slug' => $slug,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 日付でソート(新しい順)
|
// 日付でソート(新しい順)
|
||||||
usort($posts, function($a, $b) {
|
usort($posts, function($a, $b) {
|
||||||
return strtotime($b['date']) - strtotime($a['date']);
|
return strtotime($b['date']) - strtotime($a['date']);
|
||||||
});
|
});
|
||||||
|
|
||||||
return $posts;
|
return $posts;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,151 +1,151 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Site\Controller;
|
namespace Site\Controller;
|
||||||
|
|
||||||
use Site\Controller\BlogPost;
|
use Site\Controller\BlogPost;
|
||||||
use Site\Controller\Mods;
|
use Site\Controller\Mods;
|
||||||
use Site\Lib\Activitypub;
|
use Site\Lib\Activitypub;
|
||||||
|
|
||||||
class Fediverse extends BlogPost {
|
class Fediverse extends BlogPost {
|
||||||
use Mods;
|
use Mods;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $params パラメータ配列
|
* @param array $params パラメータ配列
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function apfinger(array $params): void {
|
public function apfinger(array $params): void {
|
||||||
try {
|
try {
|
||||||
header('Content-Type: application/jrd+json');
|
header('Content-Type: application/jrd+json');
|
||||||
$ap = new Activitypub();
|
$ap = new Activitypub();
|
||||||
echo $ap->getWebfinger();
|
echo $ap->getWebfinger();
|
||||||
exit;
|
exit;
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
header('Content-Type: text/plain; charset=utf-8');
|
header('Content-Type: text/plain; charset=utf-8');
|
||||||
echo 'フェディバースの作成に失敗: '.$e->getMessage();
|
echo 'フェディバースの作成に失敗: '.$e->getMessage();
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $params パラメータ配列
|
* @param array $params パラメータ配列
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function apactor(array $params): void {
|
public function apactor(array $params): void {
|
||||||
try {
|
try {
|
||||||
header('Content-Type: application/activity+json');
|
header('Content-Type: application/activity+json');
|
||||||
$ap = new Activitypub();
|
$ap = new Activitypub();
|
||||||
echo $ap->getActor();
|
echo $ap->getActor();
|
||||||
exit;
|
exit;
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
header('Content-Type: text/plain; charset=utf-8');
|
header('Content-Type: text/plain; charset=utf-8');
|
||||||
echo 'フェディバースの作成に失敗: '.$e->getMessage();
|
echo 'フェディバースの作成に失敗: '.$e->getMessage();
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $params パラメータ配列
|
* @param array $params パラメータ配列
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function apinbox(array $params): void {
|
public function apinbox(array $params): void {
|
||||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
header('HTTP/1.1 405 Method Not Allowed');
|
header('HTTP/1.1 405 Method Not Allowed');
|
||||||
header('Allow: POST');
|
header('Allow: POST');
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$input = file_get_contents('php://input');
|
$input = file_get_contents('php://input');
|
||||||
$activity = json_decode($input, true);
|
$activity = json_decode($input, true);
|
||||||
if (!$activity || !isset($activity['type'])) {
|
if (!$activity || !isset($activity['type'])) {
|
||||||
header('HTTP/1.1 400 Bad Request');
|
header('HTTP/1.1 400 Bad Request');
|
||||||
header('Content-Type: application/activity+json');
|
header('Content-Type: application/activity+json');
|
||||||
echo json_encode(['error' => '不正なアクティビティ']);
|
echo json_encode(['error' => '不正なアクティビティ']);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger(\LogType::ActivityPub, "受付に入れた:".json_encode($activity));
|
logger(\LogType::ActivityPub, "受付に入れた:".json_encode($activity));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
header('Content-Type: application/activity+json');
|
header('Content-Type: application/activity+json');
|
||||||
$ap = new Activitypub();
|
$ap = new Activitypub();
|
||||||
$ap->postInbox($activity);
|
$ap->postInbox($activity);
|
||||||
exit;
|
exit;
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
header('Content-Type: text/plain; charset=utf-8');
|
header('Content-Type: text/plain; charset=utf-8');
|
||||||
echo 'フェディバースの作成に失敗: '.$e->getMessage();
|
echo 'フェディバースの作成に失敗: '.$e->getMessage();
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $params パラメータ配列
|
* @param array $params パラメータ配列
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function apactivity(array $params): void {
|
public function apactivity(array $params): void {
|
||||||
$uuid = '';
|
$uuid = '';
|
||||||
if (isset($params['uuid'])) $uuid = $params['uuid'];
|
if (isset($params['uuid'])) $uuid = $params['uuid'];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
header('Content-Type: application/activity+json');
|
header('Content-Type: application/activity+json');
|
||||||
$posts = $this->getPosts('/blog/');
|
$posts = $this->getPosts('/blog/');
|
||||||
$ap = new Activitypub($posts);
|
$ap = new Activitypub($posts);
|
||||||
echo $ap->getActivity($uuid);
|
echo $ap->getActivity($uuid);
|
||||||
exit;
|
exit;
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
header('Content-Type: text/plain; charset=utf-8');
|
header('Content-Type: text/plain; charset=utf-8');
|
||||||
echo 'フェディバースの作成に失敗: '.$e->getMessage();
|
echo 'フェディバースの作成に失敗: '.$e->getMessage();
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $params パラメータ配列
|
* @param array $params パラメータ配列
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function apoutbox(array $params): void {
|
public function apoutbox(array $params): void {
|
||||||
try {
|
try {
|
||||||
header('Content-Type: application/activity+json');
|
header('Content-Type: application/activity+json');
|
||||||
$posts = $this->getPosts('/blog/');
|
$posts = $this->getPosts('/blog/');
|
||||||
$ap = new Activitypub($posts);
|
$ap = new Activitypub($posts);
|
||||||
echo $ap->getOutbox();
|
echo $ap->getOutbox();
|
||||||
exit;
|
exit;
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
header('Content-Type: text/plain; charset=utf-8');
|
header('Content-Type: text/plain; charset=utf-8');
|
||||||
echo 'フェディバースの作成に失敗: '.$e->getMessage();
|
echo 'フェディバースの作成に失敗: '.$e->getMessage();
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $params パラメータ配列
|
* @param array $params パラメータ配列
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function apfollowers(array $params): void {
|
public function apfollowers(array $params): void {
|
||||||
try {
|
try {
|
||||||
header('Content-Type: application/activity+json');
|
header('Content-Type: application/activity+json');
|
||||||
$ap = new Activitypub();
|
$ap = new Activitypub();
|
||||||
echo $ap->getFollowers();
|
echo $ap->getFollowers();
|
||||||
exit;
|
exit;
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
header('Content-Type: text/plain; charset=utf-8');
|
header('Content-Type: text/plain; charset=utf-8');
|
||||||
echo 'フェディバースの作成に失敗: '.$e->getMessage();
|
echo 'フェディバースの作成に失敗: '.$e->getMessage();
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $params パラメータ配列
|
* @param array $params パラメータ配列
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function apfollowing(array $params): void {
|
public function apfollowing(array $params): void {
|
||||||
try {
|
try {
|
||||||
header('Content-Type: application/activity+json');
|
header('Content-Type: application/activity+json');
|
||||||
$ap = new Activitypub();
|
$ap = new Activitypub();
|
||||||
echo $ap->getFollowing();
|
echo $ap->getFollowing();
|
||||||
exit;
|
exit;
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
header('Content-Type: text/plain; charset=utf-8');
|
header('Content-Type: text/plain; charset=utf-8');
|
||||||
echo 'フェディバースの作成に失敗: '.$e->getMessage();
|
echo 'フェディバースの作成に失敗: '.$e->getMessage();
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,130 +1,130 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Site\Lib;
|
namespace Site\Lib;
|
||||||
|
|
||||||
class DiffViewer {
|
class DiffViewer {
|
||||||
private $diffContent;
|
private $diffContent;
|
||||||
|
|
||||||
public function __construct(string $filePath) {
|
public function __construct(string $filePath) {
|
||||||
if (!file_exists($filePath)) {
|
if (!file_exists($filePath)) {
|
||||||
throw new \Exception("Diff file not found: $filePath");
|
throw new \Exception("Diff file not found: $filePath");
|
||||||
}
|
}
|
||||||
$this->diffContent = file_get_contents($filePath);
|
$this->diffContent = file_get_contents($filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function displaySideBySide(): string {
|
public function displaySideBySide(): string {
|
||||||
$lines = explode("\n", $this->diffContent);
|
$lines = explode("\n", $this->diffContent);
|
||||||
$fileDiffs = [];
|
$fileDiffs = [];
|
||||||
$currentFile = null;
|
$currentFile = null;
|
||||||
$hunk = [];
|
$hunk = [];
|
||||||
$lineNumbers = ['left' => 0, 'right' => 0];
|
$lineNumbers = ['left' => 0, 'right' => 0];
|
||||||
$currentLeftLines = [];
|
$currentLeftLines = [];
|
||||||
$currentRightLines = [];
|
$currentRightLines = [];
|
||||||
|
|
||||||
foreach ($lines as $line) {
|
foreach ($lines as $line) {
|
||||||
// ファイルヘッダーの処理
|
// ファイルヘッダーの処理
|
||||||
if (preg_match('/^---\s+(.+)/', $line, $matches)) {
|
if (preg_match('/^---\s+(.+)/', $line, $matches)) {
|
||||||
// ファイルを処理する場合、データを保存する
|
// ファイルを処理する場合、データを保存する
|
||||||
if ($currentFile !== null) {
|
if ($currentFile !== null) {
|
||||||
$this->processHunk($hunk, $currentLeftLines, $currentRightLines, $lineNumbers);
|
$this->processHunk($hunk, $currentLeftLines, $currentRightLines, $lineNumbers);
|
||||||
$fileDiffs[$currentFile] = [
|
$fileDiffs[$currentFile] = [
|
||||||
'leftLines' => $currentLeftLines,
|
'leftLines' => $currentLeftLines,
|
||||||
'rightLines' => $currentRightLines
|
'rightLines' => $currentRightLines
|
||||||
];
|
];
|
||||||
$hunk = [];
|
$hunk = [];
|
||||||
$currentLeftLines = [];
|
$currentLeftLines = [];
|
||||||
$currentRightLines = [];
|
$currentRightLines = [];
|
||||||
$lineNumbers = ['left' => 0, 'right' => 0];
|
$lineNumbers = ['left' => 0, 'right' => 0];
|
||||||
}
|
}
|
||||||
$currentFile = $matches[1];
|
$currentFile = $matches[1];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (preg_match('/^\+\+\+\s+(.+)/', $line)) {
|
if (preg_match('/^\+\+\+\s+(.+)/', $line)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ハンクヘッダーの処理 (例:@@ -10,6 +10,7 @@)
|
// ハンクヘッダーの処理 (例:@@ -10,6 +10,7 @@)
|
||||||
if (preg_match('/^@@\s+-(\d+),\d+\s+\+(\d+),\d+\s+@@/', $line, $matches)) {
|
if (preg_match('/^@@\s+-(\d+),\d+\s+\+(\d+),\d+\s+@@/', $line, $matches)) {
|
||||||
$this->processHunk($hunk, $currentLeftLines, $currentRightLines, $lineNumbers);
|
$this->processHunk($hunk, $currentLeftLines, $currentRightLines, $lineNumbers);
|
||||||
$hunk = [];
|
$hunk = [];
|
||||||
$lineNumbers['left'] = (int)$matches[1];
|
$lineNumbers['left'] = (int)$matches[1];
|
||||||
$lineNumbers['right'] = (int)$matches[2];
|
$lineNumbers['right'] = (int)$matches[2];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ハンクでの行列の集まり
|
// ハンクでの行列の集まり
|
||||||
if (substr($line, 0, 1) === '-' || substr($line, 0, 1) === '+' || substr($line, 0, 1) === ' ') {
|
if (substr($line, 0, 1) === '-' || substr($line, 0, 1) === '+' || substr($line, 0, 1) === ' ') {
|
||||||
$hunk[] = $line;
|
$hunk[] = $line;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 最後のハンク・ファイルの処理
|
// 最後のハンク・ファイルの処理
|
||||||
if ($currentFile !== null) {
|
if ($currentFile !== null) {
|
||||||
$this->processHunk($hunk, $currentLeftLines, $currentRightLines, $lineNumbers);
|
$this->processHunk($hunk, $currentLeftLines, $currentRightLines, $lineNumbers);
|
||||||
$fileDiffs[$currentFile] = [
|
$fileDiffs[$currentFile] = [
|
||||||
'leftLines' => $currentLeftLines,
|
'leftLines' => $currentLeftLines,
|
||||||
'rightLines' => $currentRightLines
|
'rightLines' => $currentRightLines
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 各ファイルにHTMLの出力の作成
|
// 各ファイルにHTMLの出力の作成
|
||||||
$html = '';
|
$html = '';
|
||||||
foreach ($fileDiffs as $fileName => $diff) {
|
foreach ($fileDiffs as $fileName => $diff) {
|
||||||
$html .= "<h2>ファイル: ".htmlspecialchars($fileName)."</h2>\n";
|
$html .= "<h2>ファイル: ".htmlspecialchars($fileName)."</h2>\n";
|
||||||
$html .= $this->generateHtml($diff['leftLines'], $diff['rightLines']);
|
$html .= $this->generateHtml($diff['leftLines'], $diff['rightLines']);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $html;
|
return $html;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function processHunk(array $hunk, array &$leftLines, array &$rightLines, array &$lineNumbers): void {
|
private function processHunk(array $hunk, array &$leftLines, array &$rightLines, array &$lineNumbers): void {
|
||||||
foreach ($hunk as $line) {
|
foreach ($hunk as $line) {
|
||||||
$prefix = substr($line, 0, 1);
|
$prefix = substr($line, 0, 1);
|
||||||
$content = substr($line, 1);
|
$content = substr($line, 1);
|
||||||
|
|
||||||
if ($prefix === '-') {
|
if ($prefix === '-') {
|
||||||
$leftLines[] = ['content' => htmlspecialchars($content), 'type' => 'removed', 'line' => $lineNumbers['left']];
|
$leftLines[] = ['content' => htmlspecialchars($content), 'type' => 'removed', 'line' => $lineNumbers['left']];
|
||||||
$lineNumbers['left']++;
|
$lineNumbers['left']++;
|
||||||
} elseif ($prefix === '+') {
|
} elseif ($prefix === '+') {
|
||||||
$rightLines[] = ['content' => htmlspecialchars($content), 'type' => 'added', 'line' => $lineNumbers['right']];
|
$rightLines[] = ['content' => htmlspecialchars($content), 'type' => 'added', 'line' => $lineNumbers['right']];
|
||||||
$lineNumbers['right']++;
|
$lineNumbers['right']++;
|
||||||
} elseif ($prefix === ' ') {
|
} elseif ($prefix === ' ') {
|
||||||
// 両側のコンテキストは同じ行列があるかの確認
|
// 両側のコンテキストは同じ行列があるかの確認
|
||||||
while ($lineNumbers['left'] < $lineNumbers['right']) {
|
while ($lineNumbers['left'] < $lineNumbers['right']) {
|
||||||
$leftLines[] = ['content' => '', 'type' => 'empty', 'line' => $lineNumbers['left']];
|
$leftLines[] = ['content' => '', 'type' => 'empty', 'line' => $lineNumbers['left']];
|
||||||
$lineNumbers['left']++;
|
$lineNumbers['left']++;
|
||||||
}
|
}
|
||||||
while ($lineNumbers['right'] < $lineNumbers['left']) {
|
while ($lineNumbers['right'] < $lineNumbers['left']) {
|
||||||
$rightLines[] = ['content' => '', 'type' => 'empty', 'line' => $lineNumbers['right']];
|
$rightLines[] = ['content' => '', 'type' => 'empty', 'line' => $lineNumbers['right']];
|
||||||
$lineNumbers['right']++;
|
$lineNumbers['right']++;
|
||||||
}
|
}
|
||||||
$leftLines[] = ['content' => htmlspecialchars($content), 'type' => 'context', 'line' => $lineNumbers['left']];
|
$leftLines[] = ['content' => htmlspecialchars($content), 'type' => 'context', 'line' => $lineNumbers['left']];
|
||||||
$rightLines[] = ['content' => htmlspecialchars($content), 'type' => 'context', 'line' => $lineNumbers['right']];
|
$rightLines[] = ['content' => htmlspecialchars($content), 'type' => 'context', 'line' => $lineNumbers['right']];
|
||||||
$lineNumbers['left']++;
|
$lineNumbers['left']++;
|
||||||
$lineNumbers['right']++;
|
$lineNumbers['right']++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generateHtml(array $leftLines, array $rightLines): string {
|
private function generateHtml(array $leftLines, array $rightLines): string {
|
||||||
$html = '<table class="diff-table">';
|
$html = '<table class="diff-table">';
|
||||||
$html .= '<tr class="diff-header"><th colspan="2">前</th><th colspan="2">新</th></tr>';
|
$html .= '<tr class="diff-header"><th colspan="2">前</th><th colspan="2">新</th></tr>';
|
||||||
|
|
||||||
$maxLines = max(count($leftLines), count($rightLines));
|
$maxLines = max(count($leftLines), count($rightLines));
|
||||||
for ($i = 0; $i < $maxLines; $i++) {
|
for ($i = 0; $i < $maxLines; $i++) {
|
||||||
$left = isset($leftLines[$i]) ? $leftLines[$i] : ['content' => '', 'type' => 'empty', 'line' => ''];
|
$left = isset($leftLines[$i]) ? $leftLines[$i] : ['content' => '', 'type' => 'empty', 'line' => ''];
|
||||||
$right = isset($rightLines[$i]) ? $rightLines[$i] : ['content' => '', 'type' => 'empty', 'line' => ''];
|
$right = isset($rightLines[$i]) ? $rightLines[$i] : ['content' => '', 'type' => 'empty', 'line' => ''];
|
||||||
|
|
||||||
$html .= '<tr>';
|
$html .= '<tr>';
|
||||||
// 左(変更前)
|
// 左(変更前)
|
||||||
$html .= '<td class="line-number">' . ($left['line'] ?: ' ') . '</td>';
|
$html .= '<td class="line-number">' . ($left['line'] ?: ' ') . '</td>';
|
||||||
$html .= '<td class="' . $left['type'] . '">' . ($left['content'] ?: ' ') . '</td>';
|
$html .= '<td class="' . $left['type'] . '">' . ($left['content'] ?: ' ') . '</td>';
|
||||||
// 右(変更後)
|
// 右(変更後)
|
||||||
$html .= '<td class="line-number">' . ($right['line'] ?: ' ') . '</td>';
|
$html .= '<td class="line-number">' . ($right['line'] ?: ' ') . '</td>';
|
||||||
$html .= '<td class="' . $right['type'] . '">' . ($right['content'] ?: ' ') . '</td>';
|
$html .= '<td class="' . $right['type'] . '">' . ($right['content'] ?: ' ') . '</td>';
|
||||||
$html .= '</tr>';
|
$html .= '</tr>';
|
||||||
}
|
}
|
||||||
|
|
||||||
$html .= '</table>';
|
$html .= '</table>';
|
||||||
return $html;
|
return $html;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,64 +1,64 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Site\Test;
|
namespace Site\Test;
|
||||||
|
|
||||||
require_once __DIR__.'/../../../autoload.php';
|
require_once __DIR__.'/../../../autoload.php';
|
||||||
|
|
||||||
use Site\Lib\Tester;
|
use Site\Lib\Tester;
|
||||||
use Site\Lib\Mysql;
|
use Site\Lib\Mysql;
|
||||||
|
|
||||||
$test = new Tester([
|
$test = new Tester([
|
||||||
'colorOutput' => true,
|
'colorOutput' => true,
|
||||||
'verboseOutput' => true
|
'verboseOutput' => true
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$test->describe('パケットのデバッグ', function($test): void {
|
$test->describe('パケットのデバッグ', function($test): void {
|
||||||
try {
|
try {
|
||||||
$db = new Mysql();
|
$db = new Mysql();
|
||||||
|
|
||||||
$db->setDebug(true);
|
$db->setDebug(true);
|
||||||
$db->connect();
|
$db->connect();
|
||||||
|
|
||||||
$result = $db->query('SELECT * FROM user WHERE id = 1');
|
$result = $db->query('SELECT * FROM user WHERE id = 1');
|
||||||
|
|
||||||
foreach ($result['rows'] as $row) {
|
foreach ($result['rows'] as $row) {
|
||||||
echo "ユーザー名: ".$row['nickname']."\n";
|
echo "ユーザー名: ".$row['nickname']."\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
$db->savePacketLogToFile('mysql_log.txt');
|
$db->savePacketLogToFile('mysql_log.txt');
|
||||||
$db->close();
|
$db->close();
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
echo 'エラー: '.$e->getMessage()."\n";
|
echo 'エラー: '.$e->getMessage()."\n";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$test->describe('プリペアドステートメント', function($test): void {
|
$test->describe('プリペアドステートメント', function($test): void {
|
||||||
try {
|
try {
|
||||||
$db = new Mysql();
|
$db = new Mysql();
|
||||||
$db->connect();
|
$db->connect();
|
||||||
|
|
||||||
// データの入り
|
// データの入り
|
||||||
$stmt = $db->prepare('INSERT INTO users (name, age) VALUES (?, ?)');
|
$stmt = $db->prepare('INSERT INTO users (name, age) VALUES (?, ?)');
|
||||||
$test->assertTrue($stmt);
|
$test->assertTrue($stmt);
|
||||||
|
|
||||||
$db->execute($stmt, ['山田太郎', 25]);
|
$db->execute($stmt, ['山田太郎', 25]);
|
||||||
// TODO: assert
|
// TODO: assert
|
||||||
|
|
||||||
$close = $db->demolish($stmt);
|
$close = $db->demolish($stmt);
|
||||||
$this->assertTrue($close);
|
$this->assertTrue($close);
|
||||||
|
|
||||||
// データの受け取り
|
// データの受け取り
|
||||||
$stmt = $db->prepare('SELECT * FROM users WHERE age > ?');
|
$stmt = $db->prepare('SELECT * FROM users WHERE age > ?');
|
||||||
$test->assertTrue($stmt);
|
$test->assertTrue($stmt);
|
||||||
|
|
||||||
$res = $db->execute($stmt, [20]);
|
$res = $db->execute($stmt, [20]);
|
||||||
// TODO: assert
|
// TODO: assert
|
||||||
print_r($res);
|
print_r($res);
|
||||||
|
|
||||||
$close = $db->demolish($stmt);
|
$close = $db->demolish($stmt);
|
||||||
$this->assertTrue($close);
|
$this->assertTrue($close);
|
||||||
|
|
||||||
$db->close();
|
$db->close();
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
echo 'エラー: '.$e->getMessage()."\n";
|
echo 'エラー: '.$e->getMessage()."\n";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1,23 +1,23 @@
|
|||||||
<article class="news-card">
|
<article class="news-card">
|
||||||
{@ if (isset($post['thumbnail']) && $post['thumbnail'] != '') @}
|
{@ if (isset($post['thumbnail']) && $post['thumbnail'] != '') @}
|
||||||
<div class="news-image">
|
<div class="news-image">
|
||||||
<a href="/{{ $section }}/{{ $post['slug'] }}">
|
<a href="/{{ $section }}/{{ $post['slug'] }}">
|
||||||
<img src="/static/article/{{ $post['thumbnail'] }}" alt="{{ $post['title'] }}" loading="lazy" />
|
<img src="/static/article/{{ $post['thumbnail'] }}" alt="{{ $post['title'] }}" loading="lazy" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{@ endif @}
|
{@ endif @}
|
||||||
<div class="news-content">
|
<div class="news-content">
|
||||||
<div class="news-meta">
|
<div class="news-meta">
|
||||||
<span class="news-date">{{ $post['date'] }}</span>
|
<span class="news-date">{{ $post['date'] }}</span>
|
||||||
{# {@ if (isset($post['category']) && is_array($post['category'])) @} #}
|
{# {@ if (isset($post['category']) && is_array($post['category'])) @} #}
|
||||||
{@ foreach ($post['category'] as $cat) @}
|
{@ foreach ($post['category'] as $cat) @}
|
||||||
<span class="news-category">{{ $cat }}</span>
|
<span class="news-category">{{ $cat }}</span>
|
||||||
{@ endforeach @}
|
{@ endforeach @}
|
||||||
{# {@ endif @} #}
|
{# {@ endif @} #}
|
||||||
</div>
|
</div>
|
||||||
<h2 class="news-title">
|
<h2 class="news-title">
|
||||||
<a href="/{{ $section }}/{{ $post['slug'] }}{{{ isset($_GET['q']) ? '?q='.urlencode($_GET['q']) : '' }}}">{{{ $post['title'] }}}</a>
|
<a href="/{{ $section }}/{{ $post['slug'] }}{{{ isset($_GET['q']) ? '?q='.urlencode($_GET['q']) : '' }}}">{{{ $post['title'] }}}</a>
|
||||||
</h2>
|
</h2>
|
||||||
<p class="news-preview">{{{ $post['preview'] }}}</p>
|
<p class="news-preview">{{{ $post['preview'] }}}</p>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
@@ -1,71 +1,71 @@
|
|||||||
{@ if (isset($totalPages) && $totalPages > 1) @}
|
{@ if (isset($totalPages) && $totalPages > 1) @}
|
||||||
<div class="pagination">
|
<div class="pagination">
|
||||||
{# 検索クエリがある場合はページネーションリンクに含める #}
|
{# 検索クエリがある場合はページネーションリンクに含める #}
|
||||||
{$ $queryParams = [] $}
|
{$ $queryParams = [] $}
|
||||||
{@ if (isset($_GET['q']) && !empty($_GET['q'])) @}
|
{@ if (isset($_GET['q']) && !empty($_GET['q'])) @}
|
||||||
{$ $queryParams['q'] = $_GET['q'] $}
|
{$ $queryParams['q'] = $_GET['q'] $}
|
||||||
{@ endif @}
|
{@ endif @}
|
||||||
|
|
||||||
{# 前のページへのリンク #}
|
{# 前のページへのリンク #}
|
||||||
{@ if (isset($currentPage) && $currentPage > 1) @}
|
{@ if (isset($currentPage) && $currentPage > 1) @}
|
||||||
{$ $prevParams = $queryParams $}
|
{$ $prevParams = $queryParams $}
|
||||||
{$ $prevParams['page'] = $currentPage - 1 $}
|
{$ $prevParams['page'] = $currentPage - 1 $}
|
||||||
{$ $prevQueryString = http_build_query($prevParams) $}
|
{$ $prevQueryString = http_build_query($prevParams) $}
|
||||||
<a href="?{{ $prevQueryString }}" class="page-link">« 前</a>
|
<a href="?{{ $prevQueryString }}" class="page-link">« 前</a>
|
||||||
{@ endif @}
|
{@ endif @}
|
||||||
|
|
||||||
{# 表示するページ番号の範囲を計算(モバイル対応の為) #}
|
{# 表示するページ番号の範囲を計算(モバイル対応の為) #}
|
||||||
{# 最大表示ページ数 #}
|
{# 最大表示ページ数 #}
|
||||||
{$ $rangeSize = 2 $}
|
{$ $rangeSize = 2 $}
|
||||||
{$ $startPage = max(1, $currentPage - floor($rangeSize / 2)) $}
|
{$ $startPage = max(1, $currentPage - floor($rangeSize / 2)) $}
|
||||||
{$ $endPage = min($totalPages, $startPage + $rangeSize - 1) $}
|
{$ $endPage = min($totalPages, $startPage + $rangeSize - 1) $}
|
||||||
|
|
||||||
{# 範囲の調整 #}
|
{# 範囲の調整 #}
|
||||||
{@ if ($endPage - $startPage + 1 < $rangeSize && $startPage > 1) @}
|
{@ if ($endPage - $startPage + 1 < $rangeSize && $startPage > 1) @}
|
||||||
{$ $startPage = max(1, $endPage - $rangeSize + 1) $}
|
{$ $startPage = max(1, $endPage - $rangeSize + 1) $}
|
||||||
{@ endif @}
|
{@ endif @}
|
||||||
|
|
||||||
{# 最初のページへのリンク(多数のページがある場合) #}
|
{# 最初のページへのリンク(多数のページがある場合) #}
|
||||||
{@ if ($startPage > 1) @}
|
{@ if ($startPage > 1) @}
|
||||||
{$ $firstParams = $queryParams $}
|
{$ $firstParams = $queryParams $}
|
||||||
{$ $firstParams['page'] = 1 $}
|
{$ $firstParams['page'] = 1 $}
|
||||||
{$ $firstQueryString = http_build_query($firstParams) $}
|
{$ $firstQueryString = http_build_query($firstParams) $}
|
||||||
|
|
||||||
<a href="?{{ $firstQueryString }}" class="page-link">1</a>
|
<a href="?{{ $firstQueryString }}" class="page-link">1</a>
|
||||||
{@ if ($startPage > 2) @}
|
{@ if ($startPage > 2) @}
|
||||||
<span class="page-ellipsis">...</span>
|
<span class="page-ellipsis">...</span>
|
||||||
{@ endif @}
|
{@ endif @}
|
||||||
{@ endif @}
|
{@ endif @}
|
||||||
|
|
||||||
{@ for ($i = $startPage; $i <= $endPage; $i++) @}
|
{@ for ($i = $startPage; $i <= $endPage; $i++) @}
|
||||||
{$ $pageParams = $queryParams $}
|
{$ $pageParams = $queryParams $}
|
||||||
{$ $pageParams['page'] = $i $}
|
{$ $pageParams['page'] = $i $}
|
||||||
{$ $pageQueryString = http_build_query($pageParams) $}
|
{$ $pageQueryString = http_build_query($pageParams) $}
|
||||||
|
|
||||||
{@ if ($i == $currentPage) @}
|
{@ if ($i == $currentPage) @}
|
||||||
<span class="page-current" aria-current="page">{{ $i }}</span>
|
<span class="page-current" aria-current="page">{{ $i }}</span>
|
||||||
{@ else @}
|
{@ else @}
|
||||||
<a href="?{{ $pageQueryString }}" class="page-link">{{ $i }}</a>
|
<a href="?{{ $pageQueryString }}" class="page-link">{{ $i }}</a>
|
||||||
{@ endif @}
|
{@ endif @}
|
||||||
{@ endfor @}
|
{@ endfor @}
|
||||||
|
|
||||||
{@ if ($endPage < $totalPages) @}
|
{@ if ($endPage < $totalPages) @}
|
||||||
{# 最後のページへのリンク(多数のページがある場合) #}
|
{# 最後のページへのリンク(多数のページがある場合) #}
|
||||||
{$ $lastParams = $queryParams $}
|
{$ $lastParams = $queryParams $}
|
||||||
{$ $lastParams['page'] = $totalPages $}
|
{$ $lastParams['page'] = $totalPages $}
|
||||||
{$ $lastQueryString = http_build_query($lastParams) $}
|
{$ $lastQueryString = http_build_query($lastParams) $}
|
||||||
{@ if ($endPage < $totalPages - 1) @}
|
{@ if ($endPage < $totalPages - 1) @}
|
||||||
<span class="page-ellipsis">...</span>
|
<span class="page-ellipsis">...</span>
|
||||||
{@ endif @}
|
{@ endif @}
|
||||||
<a href="?{{ $lastQueryString }}" class="page-link">{{ $totalPages }}</a>
|
<a href="?{{ $lastQueryString }}" class="page-link">{{ $totalPages }}</a>
|
||||||
{@ endif @}
|
{@ endif @}
|
||||||
|
|
||||||
{# 次のページへのリンク #}
|
{# 次のページへのリンク #}
|
||||||
{@ if (isset($currentPage) && $currentPage < $totalPages) @}
|
{@ if (isset($currentPage) && $currentPage < $totalPages) @}
|
||||||
{$ $nextParams = $queryParams $}
|
{$ $nextParams = $queryParams $}
|
||||||
{$ $nextParams['page'] = $currentPage + 1 $}
|
{$ $nextParams['page'] = $currentPage + 1 $}
|
||||||
{$ $nextQueryString = http_build_query($nextParams) $}
|
{$ $nextQueryString = http_build_query($nextParams) $}
|
||||||
<a href="?{{ $nextQueryString }}" class="page-link">次 »</a>
|
<a href="?{{ $nextQueryString }}" class="page-link">次 »</a>
|
||||||
{@ endif @}
|
{@ endif @}
|
||||||
</div>
|
</div>
|
||||||
{@ endif @}
|
{@ endif @}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<div class="search-form">
|
<div class="search-form">
|
||||||
<form action="/{{ $curblog != 'gd' ? $section : '' }}" method="GET">
|
<form action="/{{ $curblog != 'gd' ? $section : '' }}" method="GET">
|
||||||
<input type="text" name="q" value="{{{ isset($_GET['q']) ? htmlspecialchars($_GET['q']) : '' }}}" placeholder="キーワードを入力して下さい" />
|
<input type="text" name="q" value="{{{ isset($_GET['q']) ? htmlspecialchars($_GET['q']) : '' }}}" placeholder="キーワードを入力して下さい" />
|
||||||
<input type="submit" value="検索" />
|
<input type="submit" value="検索" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
{@ include(common/header) @}
|
{@ include(common/header) @}
|
||||||
<h1 class="paragraph">Moneroで支援♡</h1>
|
<h1 class="paragraph">Moneroで支援♡</h1>
|
||||||
<p class="paragraph">
|
<p class="paragraph">
|
||||||
欲しければ、モネロ(XMR)でご支援お願い申し上げます。
|
欲しければ、モネロ(XMR)でご支援お願い申し上げます。
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<img src="https://ass.technicalsuwako.moe/keroxmr.png" alt="" />
|
<img src="https://ass.technicalsuwako.moe/keroxmr.png" alt="" />
|
||||||
<img src="https://ass.technicalsuwako.moe/xmr-qr.png" alt="" />
|
<img src="https://ass.technicalsuwako.moe/xmr-qr.png" alt="" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p class="paragraph">
|
<p class="paragraph">
|
||||||
<pre><code>88daW9ANXGVg9zHe6tzHSpQjxHN6JPFDz9wvZBecL1BfTFwmkuLYm9xRsLUt1WAVGPQ6h5pZX6nyu9zXFwE5efSz1gtE1oz</code></pre>
|
<pre><code>88daW9ANXGVg9zHe6tzHSpQjxHN6JPFDz9wvZBecL1BfTFwmkuLYm9xRsLUt1WAVGPQ6h5pZX6nyu9zXFwE5efSz1gtE1oz</code></pre>
|
||||||
</p>
|
</p>
|
||||||
{@ include(common/footer) @}
|
{@ include(common/footer) @}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{@ include(common/header) @}
|
{@ include(common/header) @}
|
||||||
<h1 class="paragraph">秘密のページ</h1>
|
<h1 class="paragraph">秘密のページ</h1>
|
||||||
<p class="paragraph">
|
<p class="paragraph">
|
||||||
内緒だね~
|
内緒だね~
|
||||||
</p>
|
</p>
|
||||||
{@ include(common/footer) @}
|
{@ include(common/footer) @}
|
||||||
|
|||||||
Reference in New Issue
Block a user