Compare commits

..

2 commits

Author SHA1 Message Date
m1k1o
258ee10b76 Merge branch 'master' of github.com:m1k1o/blog into ldap-support 2020-06-25 19:31:18 +02:00
Miroslav Šedivý
f6b258321f ldap support 2020-06-12 00:35:13 +02:00
29 changed files with 176 additions and 806 deletions

1
.github/FUNDING.yml vendored
View file

@ -1 +0,0 @@
github: [ m1k1o ]

View file

@ -1,58 +0,0 @@
name: "CI for builds"
on:
push:
branches:
- master
tags:
- 'v*'
env:
IMAGE_NAME: m1k1o/blog
jobs:
build:
runs-on: ubuntu-latest
#
# do not run on forks
#
if: github.repository_owner == 'm1k1o'
steps:
-
name: Checkout
uses: actions/checkout@v2
-
name: Set up QEMU
uses: docker/setup-qemu-action@v1
-
name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1
-
name: Available platforms
run: echo ${{ steps.buildx.outputs.platforms }}
-
name: Extract metadata (tags, labels) for Docker
uses: docker/metadata-action@v3
id: meta
with:
images: ${{ env.IMAGE_NAME }}
tags: |
type=raw,value=latest,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }}
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
-
name: Log in to the Container registry
uses: docker/login-action@v1
with:
username: ${{ github.actor }}
password: ${{ secrets.DOCKER_TOKEN }}
-
name: Build and push
uses: docker/build-push-action@v2
with:
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64,linux/arm64,linux/arm/v7
push: true

View file

@ -1,7 +1,11 @@
FROM php:7.4-apache
MAINTAINER Miroslav Sedivy
ARG LDAP=false
RUN set -eux; apt-get update; \
apt-get install -y --no-install-recommends libpq-dev \
apt-get install -y --no-install-recommends \
#
# install curl
libcurl4-openssl-dev \
@ -10,18 +14,24 @@ RUN set -eux; apt-get update; \
zlib1g-dev libpng-dev libjpeg-dev \
libwebp-dev libxpm-dev libfreetype6-dev; \
#
# clean up
rm -rf /var/lib/apt/lists/*; \
#
# configure extensions
docker-php-ext-configure gd --enable-gd \
--with-jpeg --with-webp --with-xpm --with-freetype; \
#
# install extensions
docker-php-ext-install curl gd pdo pdo_mysql pdo_pgsql exif; \
docker-php-ext-install curl gd pdo pdo_mysql; \
#
# LDAP support
if [ -n "$LDAP" ] && [ "$LDAP" = "true" ]; then \
apt-get install -y --no-install-recommends libldb-dev libldap2-dev; \
docker-php-ext-install ldap; \
fi; \
#
# set up environment
a2enmod rewrite;
a2enmod rewrite; \
#
# clean up
rm -rf /var/lib/apt/lists/*;
#
# copy files

150
README.md
View file

@ -53,85 +53,32 @@ docker run -d \
m1k1o/blog:latest
```
Or for docker-compose format, see [docker-compose.yml](docker-compose.yml).
Or in docker-compose format:
```yml
version: "3"
services:
blog:
image: m1k1o/blog:latest
restart: unless-stopped
environment:
TZ: Europe/Vienna
BLOG_TITLE: Blog
BLOG_NAME: Max Musermann
BLOG_NICK: username
BLOG_PASS: password
BLOG_LANG: en
ports:
- 80:80
volumes:
- ./data:/var/www/html/data
```
## Install standalone app using `docker-compose` with external database
## Install standalone app using `docker-compose` with mysql
You need to install [docker-compose](https://docs.docker.com/compose/install/).
### MySQL
```yaml
version: "3"
services:
webserver:
image: m1k1o/blog:latest
container_name: blog_apache
environment:
TZ: Europe/Vienna
BLOG_DB_CONNECTION: mysql
BLOG_MYSQL_HOST: mariadb
BLOG_MYSQL_PORT: 3306
BLOG_MYSQL_USER: blog
BLOG_MYSQL_PASS: blog # use secure password
BLOG_DB_NAME: blog
restart: unless-stopped
ports:
- ${HTTP_PORT-80}:80
volumes:
- ${DATA-./data}:/var/www/html/data
mariadb:
image: mariadb:10.1
container_name: blog_mariadb
environment:
MYSQL_USER: blog
MYSQL_PASSWORD: blog # use secure password
MYSQL_DATABASE: blog
MYSQL_ROOT_PASSWORD: root # use secure password
restart: unless-stopped
volumes:
- mariadb:/var/lib/mysql
- ./app/db/mysql:/docker-entrypoint-initdb.d:ro
volumes:
mariadb:
```
### Postgres
```yaml
version: "3"
services:
webserver:
image: m1k1o/blog:latest
container_name: blog_apache
environment:
TZ: Europe/Vienna
BLOG_DB_CONNECTION: postgres
BLOG_POSTGRES_HOST: postgres
BLOG_POSTGRES_PORT: 5432
BLOG_POSTGRES_USER: blog
BLOG_POSTGRES_PASS: blog # use secure password
BLOG_DB_NAME: blog
restart: unless-stopped
ports:
- ${HTTP_PORT-80}:80
volumes:
- ${DATA-./data}:/var/www/html/data
postgres:
image: postgres:14
container_name: blog_postgres
environment:
POSTGRES_USER: blog
POSTGRES_PASSWORD: blog # use secure password
POSTGRES_DB: blog
restart: unless-stopped
volumes:
- postgres:/var/lib/postgresql/data
- ./app/db/postgres:/docker-entrypoint-initdb.d:ro
volumes:
postgres:
```
### Step 1: Run `docker-compose.yml`.
Select one of configurations above and save it to `docker-compose.yml`. Then run:
### Step 1: Download and run `docker-compose.yml`.
```sh
wget https://raw.githubusercontent.com/m1k1o/blog/master/docker-compose.yml
docker-compose up -d
```
@ -208,56 +155,13 @@ To check if your server is set up correctly, turn on a debug mode (in config add
* Upload images using CTRL + V *(paste it into textarea)*.
* Highlight code in post using `[code]..your code..[/code]`.
* Highlight your goal using `[goal]Text of your goal.[/goal]`.
* Use tags in posts (allowed characters `A-Za-z0-9-_` terminated by space or EOL): `#song`.
* Sort posts in reverse order (oldest first): `http://blog/#sort=reverse`.
* Filter posts by hashtags: `http://blog/#tag=songs`.
* Filter posts by location in url using: `http://blog/#loc=Vienna`.
* Use tags in posts (allowed characters `A-Za-z0-9-_` terminated by space or EOL): `#song`
* Sort posts by hashtags: `http://blog/#tag=songs`
* Sort posts by location in url using: `http://blog/#loc=Vienna`.
* Display posts from chosen date using (format YYYY-MM-DD or YYY-MM): `http://blog/#from=2017-06`.
* Display posts to chosen date using (format YYYY-MM-DD or YYY-MM): `http://blog/#to=2017-06`.
* Combine parameters in url using `&`, e.g. show posts between dates: `http://blog/#from=2017-06&to=2017-08`.
## Access control
This blog is using Mandatory Access Control (MAC), with 3 types of access levels:
* **Private** posts are visible only to your single account specified in `nick` and `pass`.
* You can specify group of your **friends** and share posts only for them.
* **Public** posts are visible to everyone, without login.
In `docker-compose.yml` file, specify your credentials and friends like this:
```yml
version: "3"
services:
blog:
image: m1k1o/blog:latest
restart: unless-stopped
environment:
TZ: Europe/Vienna
BLOG_NICK: admin_username
BLOG_PASS: admin_password
BLOG_FRIENDS: |
jane:mysecretpass
thomas:anotherpass
ports:
- 80:80
volumes:
- ./data:/var/www/html/data
```
You can specify your credentials and friends in your `config.ini` file e.g.:
```ini
[admin]
force_login = true
nick = admin_username
pass = admin_password
[friends]
friends[jane] = mysecretpass
friends[thomas] = anotherpass
```
## Localisation
Timezone can be set in config or, for docker users, `TZ` environment variable is supported. List of timezones can be found [here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones).
@ -268,7 +172,3 @@ Feel free to create new PR and add a new language. Specify language in config or
* de - 🇩🇪 German
* sk - 🇸🇰 Slovak
* fr - 🇫🇷 French (thanks @Phundrak)
* cz - 🇨🇿 Czech (thanks @djfinch)
* bs - 🇧🇦 Bosnian (thanks @hajro92)
* es - 🇪🇸 Spanish (thanks @ManuLinares)
* ru - 🇷🇺 Russian (thanks @ozzyst)

View file

@ -66,11 +66,6 @@ class Config
$value = false;
}
// Associative arrays in environment variables
if($key === 'visitor' || $key === 'friends'){
$value = self::parse_env_assoc($value);
}
self::$_settings[$key] = $value;
}
}
@ -97,22 +92,6 @@ class Config
return $value;
}
// Parse associative array from string in format key:value
private static function parse_env_assoc($data){
if(!preg_match_all("/([^\s]+):([^\s]+)/s", $data, $matches)){
return [];
}
list($_, $keys, $values) = $matches;
$array = [];
foreach ($values as $key => $value) {
$array[$keys[$key]] = $value;
}
return $array;
}
}
class ConfigException extends Exception {}

View file

@ -24,27 +24,12 @@ class DB
return Config::get_safe('db_connection', 'sqlite');
}
// CONCAT() does not exist in SQLite, using || instead
// for postgres, ERROR: could not determine data type of parameter $1
public final static function concat(){
$values = func_get_args();
if(DB::connection() === 'sqlite' || DB::connection() === 'postgres') {
return implode(" || ", $values);
} else {
return 'CONCAT('.implode(", ", $values).')';
}
}
// Initialise PDO object
private final function __construct(){
switch(DB::connection()) {
case 'mysql':
$this->mysql_connect();
break;
case 'postgres':
$this->postgres_connect();
break;
case 'sqlite':
$this->sqlite_connect();
break;
@ -96,48 +81,6 @@ class DB
}
}
private final function postgres_connect(){
$host = Config::get_safe('postgres_host', false);
$port = Config::get_safe('postgres_port', false);
$socket = Config::get_safe('postgres_socket', false);
if($socket === false && $host === false){
throw new DBException("Postgres host or socket must be defined.");
}
// Try to connect
try {
$this->_PDO = new \PDO(
// Server
'pgsql:'.
($socket !== false
? 'unix_socket='.$socket
: 'host='.$host.($port !== false ? ';port='.$port : '')
).
// DB
';dbname='.Config::get('db_name').
// Charset
';options=\'--client_encoding=UTF8\'',
// Username
Config::get('postgres_user'),
// Password
Config::get_safe('postgres_pass', ''),
// Set attributes
[
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_EMULATE_PREPARES => false
]
);
$this->_PDO->exec(
// Set timezone
'SET TIME ZONE "'.date('e').'";'
);
} catch (PDOException $e) {
throw new DBException($e->getMessage());
}
}
private final function sqlite_connect(){
$sqlite_db = PROJECT_PATH.Config::get_safe('sqlite_db', "data/sqlite.db");
@ -211,11 +154,6 @@ class DB
$sql = $params[0];
unset($params[0]);
// Replace backticks with " for postgres
if(DB::connection() === 'postgres') {
$sql = str_replace("`", '"', $sql);
}
// Debug mode
if(Config::get_safe('debug', false)){
echo "<!-- ".$sql." + ".json_encode($params)." -->\n";

View file

@ -1,26 +0,0 @@
CREATE TABLE images (
"id" serial PRIMARY KEY,
"name" varchar(255) NOT NULL,
"path" varchar(255) DEFAULT NULL,
"thumb" varchar(255) DEFAULT NULL,
"type" varchar(10) NOT NULL,
"md5" char(32) NOT NULL,
"datetime" timestamp NOT NULL,
"status" int NOT NULL
);
CREATE TYPE privacy_t as enum('private','friends','public');
CREATE TABLE posts (
"id" serial PRIMARY KEY,
"text" text NOT NULL,
"plain_text" text NOT NULL,
"feeling" varchar(255) NOT NULL,
"persons" varchar(255) NOT NULL,
"location" varchar(255) NOT NULL,
"content" varchar(1000) NOT NULL,
"content_type" varchar(255) NOT NULL,
"privacy" privacy_t NOT NULL,
"datetime" timestamp NOT NULL,
"status" int NOT NULL
);

View file

@ -110,16 +110,15 @@ class Image
}
$old_image = $imgcreatefrom($source_path);
if ($old_image === false) return false;
$new_image = imagecreatetruecolor($new_w, $new_h);
imagecopyresampled($new_image, $old_image, 0, 0, 0, 0, $new_w, $new_h, $source_w, $source_h);
$new_image = self::fix_orientation($source_path, $new_image);
$old_image = self::fix_orientation($source_path, $old_image);
return $imgt($new_image, $thumb_path)
&& $imgt($old_image, $source_path);
$imgt($new_image, $thumb_path);
$imgt($old_image, $source_path);
return true;
}
public static function upload(){

View file

@ -15,7 +15,7 @@ class Lang
}
public static function get($key){
if(is_null(self::$_dictionary) || !array_key_exists($key, self::$_dictionary)){
if(!array_key_exists($key, self::$_dictionary)){
return $key;
}
@ -25,4 +25,4 @@ class Lang
function __($key){
return Lang::get($key);
}
}

View file

@ -1,67 +0,0 @@
; index.html
Show More = "Prikaži još"
Login = "Prijava"
Logout = "Odjava"
Nick = "Nadimak"
Password = "Šifra"
Cancel = "Zatvori"
Post = "Objavi"
Edit Post = "Izmijeni Objavu"
Change Date = "Promijeni Datum"
Hide from Timeline = "Sakrij sa Vremenske Linije"
Show on Timeline = "Prikaži na Vremenskoj Liniji"
Delete Post = "Izbriši objavu"
Drag photos here = "Privuci fotografije ovdje"
Drop photos here = "Privuci fotografije ovdje"
What's on your mind? = "Šta vam je na umu?"
Feeling = "Osjećaj"
How are you feeling? = "Kako se osjećate?"
With = "Sa"
Who are you with? = "S kim ste?"
At = "Gdje"
Where are you? = "Gdje se nalazite?"
Save = "Sačuvaj"
January = "Januar"
February = "Februar"
March = "Mart"
April = "April"
May = "Maj"
June = "Juni"
July = "Juli"
August = "Avgust"
September = "Septembar"
October = "Oktobar"
November = "Novembar"
December = "Decembar"
Time: = "Vrijeme:"
Hour: = "Sati:"
Minute: = "Minute:"
This post will be deleted and you'll no longer be able to find it. You can also edit this post if you just want to change something. = "Ova će objava biti izbrisana i nećete je više moći pronaći. Takođe možete izmijeniti ovu objavu ako želite izvršiti izmjene."
with = "sa"
here: = "ovdje:"
Public = "Javno"
Friends = "Prijatelji"
Only me = "Samo ja"
Show hidden content = "Prikaži skriveni sadržaj"
Show all posts = "Prikaži sve objave"
; user.class.php
You are already logged in. = "Već ste prijavljeni."
The nick or password is incorrect. = "Nadimak ili šifra su pogrešni."
You can't log out. There is no account. = "Ne možete se odjaviti. Račun ne postoji."
You are not even logged in. = "Niste čak ni prijavljeni."
; post.class.php
You need to be logged in to perform this action. = "Morate biti priavljeni da biste izvršili ovu radnju."
No data. = "Nema podataka."

View file

@ -1,67 +0,0 @@
; index.html
Show More = "Zobrazit více"
Login = "Přihlásit se"
Logout = "Odhlásit se"
Nick = "Přihlašovací jméno"
Password = "Heslo"
Cancel = "Zrušit"
Post = "Nový příspěvek"
Edit Post = "Upravit příspěvek"
Change Date = "Změnit datum"
Hide from Timeline = "Skrýt na časové ose"
Show on Timeline = "Zobrazit na časové ose"
Delete Post = "Odstranit příspěvek"
Drag photos here = "Sem přesuňte fotografie"
Drop photos here = "Zde vložte fotografie"
What's on your mind? = "Na co myslíš?"
Feeling = "Pocit"
How are you feeling? = "Jak se cítíš?"
With = "Spolu s"
Who are you with? = "S kým jsi?"
At = "Místo"
Where are you? = "Kde jsi?"
Save = "Uložit"
January = "Leden"
February = "Únor"
March = "Březen"
April = "Duben"
May = "Květen"
June = "Červen"
July = "Červenec"
August = "Srpen"
September = "Září"
October = "Říjen"
November = "Listopad"
December = "Prosinec"
Time: = "Čas:"
Hour: = "Hodina:"
Minute: = "Minuta:"
This post will be deleted and you'll no longer be able to find it. You can also edit this post if you just want to change something. = "Tento příspěvek bude odstraněn a už ho nebude možné najít. Pokud chcete něco změnit, můžete tento příspěvek také upravit."
with = "s"
here: = "zde:"
Public = "Veřejné"
Friends = "Přátelé"
Only me = "Jen já"
Show hidden content = "Zobrazit skrytý obsah"
Show all posts = "Zobrazit všechny příspěvky"
; user.class.php
You are already logged in. = "Už jsi přihlášený."
The nick or password is incorrect. = "Přihlašovací jméno nebo heslo je nesprávné."
You can't log out. There is no account. = "Nemůžeš se odhlásit. Neexistuje žádný účet."
You are not even logged in. = "Nejsi ani přihlášený."
; post.class.php
You need to be logged in to perform this action. = "Na provedení této akce musíš být přihlášený."
No data. = "Žádná data."

View file

@ -1,67 +0,0 @@
; index.html
Show More = "Mostrar más"
Login = "Iniciar sesión"
Logout = "Cerrar sesión"
Nick = "Apodo"
Password = "Contraseña"
Cancel = "Cancelar"
Post = "Publicar"
Edit Post = "Editar publicación"
Change Date = "Cambiar fecha"
Hide from Timeline = "Ocultar de la línea de tiempo"
Show on Timeline = "Mostrar en la línea de tiempo"
Delete Post = "Eliminar publicación"
Drag photos here = "Arrastra aquí las fotos"
Drop photos here = "Suelta aquí las fotos"
What's on your mind? = "¿Qué estas pensando?"
Feeling = "Sintiendo"
How are you feeling? = "¿Como te sientes?"
With = "Con"
Who are you with? = "¿Con quién estás?"
At = "En"
Where are you? = "¿Dónde estás?"
Save = "Guardar"
January = "Enero"
February = "Febrero"
March = "Marzo"
April = "Abril"
May = "Mayo"
June = "Junio"
July = "Julio"
August = "Agosto"
September = "Septiembre"
October = "Octubre"
November = "Noviembre"
December = "Diciembre"
Time: = "Hora:"
Hour: = "Hora:"
Minute: = "Minuto:"
This post will be deleted and you'll no longer be able to find it. You can also edit this post if you just want to change something. = "Esta publicación se eliminará y ya no podrá ser encontrada. También puedes editar esta publicación si solo deseas cambiar algo."
with = "con"
here: = "aqui:"
Public = "Público"
Friends = "Amigos"
Only me = "Solo yo"
Show hidden content = "Mostrar contenido oculto"
Show all posts = "Mostrar todas las publicaciones"
; user.class.php
You are already logged in. = "Ya has iniciado sesión."
The nick or password is incorrect. = "El apodo o la contraseña son incorrectos."
You can't log out. There is no account. = "No puede cerrar sesión. No hay cuenta."
You are not even logged in. = "Ni siquiera estás conectado."
; post.class.php
You need to be logged in to perform this action. = "Debe haber iniciado una sesión para realizar esta acción."
No data. = "Sin datos."

View file

@ -40,9 +40,9 @@ October = "Octobre"
November = "Novembre"
December = "Décembre"
Time: = "Heure :"
Hour: = "Heure :"
Minute: = "Minutes :"
Time: = "Heure:"
Hour: = "Heure:"
Minute: = "Minutes:"
This post will be deleted and you'll no longer be able to find it. You can also edit this post if you just want to change something. = "Cette publication sera supprimée et vous ne pourrez plus la retrouver. Vous pouvez aussi la modifier si vous souhaitez simplement changer quelque chose."
@ -57,11 +57,11 @@ Show hidden content = "Montrer le contenu caché"
Show all posts = "Montrer toutes les publications"
; user.class.php
You are already logged in. = "Vous êtes déjà connecté."
You are already logged in. = "Vous êtes déjà connectés."
The nick or password is incorrect. = "Votre pseudo ou mot de passe est incorrect."
You can't log out. There is no account. = "Vous ne pouvez pas vous déconnecter, les comptes sont désactivés."
You are not even logged in. = "Vous nêtes pas connecté."
You are not even logged in. = "Vous nêtes même pas connectés."
; post.class.php
You need to be logged in to perform this action. = "Vous devez être connecté pour pouvoir faire ça."
You need to be logged in to perform this action. = "Vous devez être connectés pour pouvoir faire ça."
No data. = "Aucune données."

View file

@ -1,67 +0,0 @@
; index.html
Show More = "Verder Lezen"
Login = "Aanmelden"
Logout = "Afmelden"
Nick = "Gebruiker"
Password = "Wachtwoord"
Cancel = "Annuleren"
Post = "Bericht"
Edit Post = "Wijzig Bericht"
Change Date = "Wijzig Datum"
Hide from Timeline = "Verbergen van Tijdlijn"
Show on Timeline = "Laten zien op Tijdlijn"
Delete Post = "Verwijder Bericht"
Drag photos here = "Sleep foto's hier"
Drop photos here = "Gooi foto's hier"
What's on your mind? = "Waar ben je mee bezig?"
Feeling = "Gevoel"
How are you feeling? = "Hoe voel je je?"
With = "Met"
Who are you with? = "Met wie ben je?"
At = "Bij"
Where are you? = "Waar ben je?"
Save = "Opslaan"
January = "Januari"
February = "Februari"
March = "Maart"
April = "April"
May = "Mei"
June = "Juni"
July = "Juli"
August = "Augustus"
September = "September"
October = "Oktober"
November = "November"
December = "December"
Time: = "Tijd:"
Hour: = "Uur:"
Minute: = "Minuten:"
This post will be deleted and you'll no longer be able to find it. You can also edit this post if you just want to change something. = "Dit bericht word verwijderd en kan niet meer worden gevonden. Je kan ook het bericht wijzigen als je dat liever wilt."
with = "met"
here: = "Bij:"
Public = "Openbaar"
Friends = "Vrienden"
Only me = "Alleen ik"
Show hidden content = "Laat verborgen berichten zien"
Show all posts = "Alle berichten zien"
; user.class.php
You are already logged in. = "Je bent al ingelogt."
The nick or password is incorrect. = "De gebruiken of het Wachtwoord is onjuist."
You can't log out. There is no account. = "Je kan niet Afmelden. Er is geen Account."
You are not even logged in. = "Je ben niet eens ingelogt."
; post.class.php
You need to be logged in to perform this action. = "Je moet ingelogged zijn om deze actie uit te voeren."
No data. = "Geen data."

View file

@ -1,67 +0,0 @@
; index.html
Show More = "Показать больше"
Login = "Войти"
Logout = "Выйти"
Nick = "Логин"
Password = "Пароль"
Cancel = "Отмена"
Post = "Пост"
Edit Post = "Редактировать пост"
Change Date = "Изменить дату"
Hide from Timeline = "Скрыть из ленты"
Show on Timeline = "Показать в ленте"
Delete Post = "Удалить пост"
Drag photos here = "Перетащите сюда фото"
Drop photos here = "Оставь фото здесь"
What's on your mind? = "О чем ты думаешь?"
Feeling = "Ощущения"
How are you feeling? = "Как ты себя чувствуешь?"
With = "В компании"
Who are you with? = "С кем ты?"
At = "Локация"
Where are you? = "Где ты?"
Save = "Сохранить"
January = "Январь"
February = "Февраль"
March = "Март"
April = "Апрель"
May = "Май"
June = "Июнь"
July = "Июль"
August = "Август"
September = "Сентябрь"
October = "Октябрь"
November = "Ноябрь"
December = "Декабрь"
Time: = "Время:"
Hour: = аc:"
Minute: = "Минута:"
This post will be deleted and you'll no longer be able to find it. You can also edit this post if you just want to change something. = "Этот пост будет удален, и вы больше не сможете его найти. Вы также можете отредактировать этот пост, если просто хотите что-то изменить"
with = "с"
here: = "здесь:"
Public = "Публичный"
Friends = "Друзья"
Only me = "Только я"
Show hidden content = "Показать скрытый контент"
Show all posts = "Показать все посты"
; user.class.php
You are already logged in. = "Вы уже залогинены."
The nick or password is incorrect. = "Логин или пароль неверный."
You can't log out. There is no account. = "Вы не можете выйти. Вы не залогинены."
You are not even logged in. = "Вы не авторизовались."
; post.class.php
You need to be logged in to perform this action. = "Вы должны быть залогинены, чтобы выполнить это действие."
No data. = "Нет данных."

View file

@ -41,7 +41,7 @@ November = "November"
December = "December"
Time: = "Čas:"
Hour: = "Hodina:"
Hour: = "Hodnia:"
Minute: = "Minúta:"
This post will be deleted and you'll no longer be able to find it. You can also edit this post if you just want to change something. = "Tento príspevok bude odstránený a už ho nebudete môcť nájsť. Tento príspevok môžete tiež upraviť, ak chcete niečo zmeniť."

View file

@ -29,8 +29,7 @@ class Post
public function asHtml(\JBBCode\ElementNode $el){
$content = $this->getContent($el);
$class = $el->getAttribute()['code'];
return '<code class="'.$class.'">'.htmlentities($content).'</code>';
return '<code class="'.$el->getAttribute().'">'.htmlentities($content).'</code>';
}
});
}
@ -204,8 +203,6 @@ class Post
if (DB::connection() === 'sqlite') {
$datetime = "strftime('%Y %m %d %H %M', `posts`.`datetime`)";
} else if (DB::connection() === 'postgres') {
$datetime = "to_char(datetime,'YYYY MM DD HH24 MI')";
} else {
$datetime = "DATE_FORMAT(`datetime`,'%Y %c %e %k %i')";
}
@ -427,14 +424,10 @@ class Post
if (DB::connection() === 'sqlite') {
$datetime = "strftime('%d %m %Y %H:%M', `posts`.`datetime`)";
} else if (DB::connection() === 'postgres') {
$datetime = "to_char(posts.datetime,'DD Mon YYYY HH24:MI')";
} else {
$datetime = "DATE_FORMAT(`posts`.`datetime`,'%d %b %Y %H:%i')";
}
$like_match = "LIKE ".DB::concat("'%'", "?", "'%'");
return DB::get_instance()->query("
SELECT
`id`, `text`, `feeling`, `persons`, `location`, `privacy`, `content_type`, `content`,
@ -445,11 +438,11 @@ class Post
($from ? "`posts`.`datetime` > ? AND " : "").
($to ? "`posts`.`datetime` < ? AND " : "").
($id ? "`id` = ? AND " : "").
($tag ? "`plain_text` $like_match AND " : "").
($loc ? "`location` $like_match AND " : "").
($person ? "`persons` $like_match AND " : "").
($tag ? "`plain_text` LIKE CONCAT('%', ?, '%') AND " : "").
($loc ? "`location` LIKE CONCAT('%', ?, '%') AND " : "").
($person ? "`persons` LIKE CONCAT('%', ?, '%') AND " : "").
"`status` <> 5
ORDER BY `posts`.`datetime` ".(@$r["sort"] == 'reverse' ? "ASC" : "DESC")."
ORDER BY `posts`.`datetime` DESC
LIMIT ? OFFSET ?
", $from, $to, $id, $tag, $loc, $person, $r["limit"], $r["offset"]
)->all();

View file

@ -18,7 +18,13 @@ class user
return true;
}
return !empty($_SESSION[User::SESSION_NAME]) && $_SESSION[User::SESSION_NAME] === hash("crc32", Config::get("nick").Config::get_safe("pass", ""), false);
if(Config::get_safe("ldap_enabled", false)){
return !empty($_SESSION[User::SESSION_NAME]) &&
$_SESSION[User::SESSION_NAME] === 'admin';
}
return !empty($_SESSION[User::SESSION_NAME]) &&
$_SESSION[User::SESSION_NAME] === hash("crc32", Config::get("nick").Config::get_safe("pass", ""), false);
}
public static function login($nick, $pass){
@ -30,16 +36,20 @@ class user
throw new Exception(__("You are already logged in."));
}
if(Config::get_safe("ldap_enabled", false)){
return static::LDAP_login($nick, $pass);
} else {
return static::config_login($nick, $pass);
}
}
private static function config_login($nick, $pass){
if(Config::get("nick") === $nick && Config::get_safe("pass", "") === $pass){
$_SESSION[User::SESSION_NAME] = hash("crc32", $nick.$pass, false);
return ["logged_in" => true, "is_visitor" => false];
}
// Legacy: Visitors and Friends.
$visitors = array_merge(
Config::get_safe("friends", []),
Config::get_safe("visitor", [])
);
$visitors = Config::get_safe("visitor", []);
if(!empty($visitors) && isset($visitors[$nick]) && $visitors[$nick] === $pass){
$_SESSION[User::SESSION_NAME] = 'visitor';
return ["logged_in" => false, "is_visitor" => true];
@ -49,6 +59,34 @@ class user
throw new Exception(__("The nick or password is incorrect."));
}
private static function LDAP_login($nick, $pass){
$ldap_host = Config::get("ldap_host");
$ldap_port = Config::get_safe("ldap_port", 389);
$ldap_admin_dn = Config::get_safe("ldap_admin_dn", false);
$ldap_visitor_dn = Config::get_safe("ldap_visitor_dn", false);
if(!($ds = ldap_connect($ldap_host, $ldap_port))) {
throw new Exception(__("Could not connect to LDAP server."));
}
ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($ds, LDAP_OPT_REFERRALS, 0);
ldap_set_option($ds, LDAP_OPT_NETWORK_TIMEOUT, 10);
if ($ldap_admin_dn !== false && ldap_bind($ds, "cn=".$nick.",".$ldap_admin_dn, $pass)) {
$_SESSION[User::SESSION_NAME] = 'admin';
return ["logged_in" => true, "is_visitor" => false];
}
if ($ldap_visitor_dn !== false && ldap_bind($ds, "cn=".$nick.",".$ldap_visitor_dn, $pass)) {
$_SESSION[User::SESSION_NAME] = 'visitor';
return ["logged_in" => false, "is_visitor" => true];
}
Log::put("login_fails", $nick);
throw new Exception(__("The nick or password is incorrect."));
}
public static function logout(){
if(!Config::get_safe("force_login", false)){
throw new Exception(__("You can't log out. There is no account."));

View file

@ -17,10 +17,10 @@ if(Config::get_safe('debug', false)){
error_reporting(E_ALL);
// Check extensions
$required = ['curl', 'PDO', 'pdo_mysql', 'gd', 'exif'];
$required = ['curl', 'PDO', 'pdo_mysql', 'gd'];
$loaded = get_loaded_extensions();
if($missing = array_diff($required, $loaded)){
die("Missing extensions, please install: ".implode(", ", $missing));
die("Missing extensions, please install: ".impode(", ", $missing));
}
}

View file

@ -11,15 +11,6 @@ db_connection = sqlite
;mysql_pass = root
;db_name = blog
;[database]
;db_connection = postgres
;postgres_socket = /tmp/postgres.sock
;postgres_host = localhost
;postgres_port = 5432
;postgres_user = root
;postgres_pass = root
;db_name = blog
[profile]
title = Blog
name = Max Musermann
@ -39,7 +30,6 @@ theme = theme02
;styles[] = static/styles/custom1.css
;styles[] = static/styles/custom2.css
;scripts = static/styles/scripts.css
;footer = "Edit this if you really want to remove my backlink :("
[bbcode]
;bbtags[quote] = "<quote>{param}</quote>"
@ -49,9 +39,16 @@ force_login = true
nick = demo
pass = demo
[friends]
;friends[user] = pass
;friends[user] = pass
[visitors]
;visitor[user] = pass
;visitor[user] = pass
;[ldap]
;ldap_enabled = true
;ldap_host = localhost
;ldap_port = 389
;ldap_admin_dn = 'ou=admin,dc=example,dc=org'
;ldap_visitor_dn = 'ou=visitor,dc=example,dc=org'
[directories]
images_path = data/i/
@ -72,6 +69,7 @@ logs_path = data/logs/
[system]
;timezone = Europe/Vienna
version = 1.42
system_name = blog
version = 1.253
debug = false
logs = false
logs = false

View file

@ -1,17 +1,31 @@
version: "3"
services:
blog:
webserver:
image: m1k1o/blog:latest
restart: unless-stopped
container_name: blog_apache
environment:
TZ: Europe/Vienna
BLOG_TITLE: Blog
BLOG_NAME: Max Musermann
BLOG_NICK: username
BLOG_PASS: password
BLOG_LANG: en
TZ: Europe/Vienna
BLOG_DB_CONNECTION: mysql
BLOG_MYSQL_HOST: mariadb
BLOG_MYSQL_PORT: 3306
BLOG_MYSQL_USER: root
BLOG_MYSQL_PASS: root
BLOG_DB_NAME: blog
restart: unless-stopped
ports:
- 80:80
- ${HTTP_PORT-80}:80
volumes:
- ${DATA-./data}:/var/www/html/data
mariadb:
image: mariadb:10.1
container_name: blog_mariadb
environment:
MYSQL_DATABASE: blog
MYSQL_ROOT_PASSWORD: root
restart: unless-stopped
volumes:
- ./data:/var/www/html/data
- mariadb:/var/lib/mysql
- ./app/db/mysql:/docker-entrypoint-initdb.d:ro
volumes:
mariadb:

View file

@ -10,10 +10,6 @@ if(empty($_SESSION['token'])){
}
}
function escape($str) {
return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}
//$.ajaxSetup({headers:{'Csrf-Token':'token'}});
Log::put("visitors");
@ -44,7 +40,6 @@ if(!empty($styles)){
}
$styles = array_unique($styles);
$styles = array_map('escape', $styles);
$styles_html = '<link href="'.implode('" rel="stylesheet" type="text/css"/>'.PHP_EOL.'<link href="', $styles).'" rel="stylesheet" type="text/css"/>'.PHP_EOL;
}
@ -52,39 +47,32 @@ if(!empty($styles)){
$scripts = Config::get_safe("scripts", []);
$scripts_html = '';
if(!empty($scripts)){
if(!is_array($scripts)){
$scripts = [$scripts];
if(!is_array($styles)){
$styles = [$styles];
}
$scripts = array_unique($scripts);
$scripts = array_map('escape', $scripts);
$scripts_html = '<script src="'.implode('" type="text/javascript"></script>'.PHP_EOL.'<script src="', $scripts).'" type="text/javascript"></script>'.PHP_EOL;
}
// Use version suffix in URLs to prevent cache
$versionSuffix = '';
if (Config::get_safe("version", false)) {
$versionSuffix = '?v='.rawurlencode(Config::get("version"));
}
?><!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title><?php echo escape(Config::get("title")); ?></title>
<title><?php echo Config::get("title"); ?></title>
<meta name="robots" content="noindex, nofollow">
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<link href="static/styles/main.css<?php echo $versionSuffix?>" rel="stylesheet" type="text/css" />
<link href="static/styles/<?php echo rawurlencode(Config::get_safe("theme", "theme01")); ?>.css<?php echo $versionSuffix?>" rel="stylesheet" type="text/css" />
<link href="static/styles/main.css?v=<?php echo Config::get("version"); ?>" rel="stylesheet" type="text/css" />
<link href="static/styles/<?php echo Config::get_safe("theme", "theme01"); ?>.css?v=<?php echo Config::get("version"); ?>" rel="stylesheet" type="text/css" />
<link href="https://fonts.googleapis.com/css?family=Open+Sans&amp;subset=all" rel="stylesheet">
<link href="static/styles/lightbox.css" rel="stylesheet" type="text/css" />
<?php echo Config::get_safe("highlight", false) ? '<link href="static/styles/highlight-monokai-sublime.css" rel="stylesheet" type="text/css" />'.PHP_EOL : ''; ?>
<?php echo Config::get("highlight") ? '<link href="static/styles/highlight.css" rel="stylesheet" type="text/css" />' : ''; ?>
<?php echo $styles_html; ?>
</head>
@ -115,6 +103,7 @@ if (Config::get_safe("version", false)) {
</div>
<div class="modal-footer">
<div class="buttons">
<!--<div class="left"><a>Register</a> - <a>Forgot Password</a></div>-->
<a class="button gray close"><?php echo __("Cancel"); ?></a>
<button type="button" class="button blue do_login"><?php echo __("Login"); ?></button>
</div>
@ -181,7 +170,7 @@ if (Config::get_safe("version", false)) {
<div class="modal-body drop_space">
<div class="e_drag"><span><?php echo __("Drag photos here"); ?></span></div>
<div class="e_drop"><span><?php echo __("Drop photos here"); ?></span></div>
<img src="<?php echo escape(Config::get("pic_small")); ?>" width="40" height="40" class="e_profile">
<img src="<?php echo Config::get("pic_small"); ?>" width="40" height="40" class="e_profile">
<!--<div class="e_text" contenteditable="true"></div>-->
<div class="t_area">
<textarea class="e_text" placeholder="<?php echo __("What's on your mind?"); ?>"></textarea>
@ -293,10 +282,10 @@ if (Config::get_safe("version", false)) {
<a class="button"><?php echo __("Show hidden content"); ?></a>
</div>
<div class="b_header">
<img src="<?php echo escape(Config::get("pic_small")); ?>" width="40" height="40" class="b_profile">
<img src="<?php echo Config::get("pic_small"); ?>" width="40" height="40" class="b_profile">
<div class="b_desc">
<div class="b_sharer">
<span class="b_name"><?php echo escape(Config::get("name")); ?></span><span class="b_options"> - </span><span class="b_feeling"></span><span class="b_with"> <?php echo __("with"); ?> </span><span class="b_persons"></span><span class="b_here"> <?php echo __("here:"); ?> </span><span class="b_location"></span>
<span class="b_name"><?php echo Config::get("name"); ?></span><span class="b_options"> - </span><span class="b_feeling"></span><span class="b_with"> <?php echo __("with"); ?> </span><span class="b_persons"></span><span class="b_here"> <?php echo __("here:"); ?> </span><span class="b_location"></span>
</div>
<i class="privacy_icon"></i>
<a class="b_date"></a>
@ -316,18 +305,18 @@ if (Config::get_safe("version", false)) {
</div>
<div class="bluebar">
<h1><?php echo escape(Config::get("title")); ?></h1>
<h1><?php echo Config::get("title"); ?></h1>
</div>
<div class="headbar">
<div class="cover">
<?php echo $header; ?>
<div class="overlay"></div>
<?php echo (Config::get_safe("cover", false) ? '<img src="'.escape(Config::get("cover")).'">' : (empty($header) ? '<div style="padding-bottom: 37%;"></div>' : '')); ?>
<?php echo (Config::get_safe("cover", false) ? '<img src="'.Config::get("cover").'">' : (empty($header) ? '<div style="padding-bottom: 37%;"></div>' : '')); ?>
<div class="profile">
<img src="<?php echo escape(Config::get("pic_big")); ?>">
<img src="<?php echo Config::get("pic_big"); ?>">
</div>
<div class="name"><?php echo escape(Config::get("name")); ?></div>
<div class="name"><?php echo Config::get("name"); ?></div>
</div>
<div id="headline"></div>
</div>
@ -341,18 +330,16 @@ if (Config::get_safe("version", false)) {
<div id="eof_feed">
<img src="static/images/zpEYXu5Wdu6.png">
<p><?php echo escape(Config::get("version")); ?> &copy; 2016-2022<br>
<?php echo Config::get_safe("footer", false) ? escape(Config::get_safe("footer")) : '<a href="https://github.com/m1k1o/blog" class="link" title="m1k1o/blog github repository" target="_blank">m1k1o/blog</a>'; ?>
</p>
<p><?php echo Config::get("version"); ?> &copy; 2016-2020 <br>Miroslav Šedivý</p>
</div>
<script src="static/scripts/jquery.min.js"></script>
<script>$["\x61\x6A\x61\x78\x53\x65\x74\x75\x70"]({"\x68\x65\x61\x64\x65\x72\x73":{"\x43\x73\x72\x66-\x54\x6F\x6B\x65\x6E":"<?php echo $_SESSION['token'];?>"}});</script>
<script src="static/scripts/lightbox.js"></script>
<script src="static/scripts/datepick.js<?php echo $versionSuffix?>"></script>
<script src="static/scripts/datepick.js?v=<?php echo Config::get("version"); ?>"></script>
<script src="static/scripts/autosize.js"></script>
<?php echo Config::get_safe("highlight", false) ? '<script src="static/scripts/highlight-10.1.2.min.js"></script><script>hljs.initHighlightingOnLoad();</script>'.PHP_EOL : ''; ?>
<script src="static/scripts/app.js<?php echo $versionSuffix?>"></script>
<?php echo Config::get("highlight") ? '<script src="static/scripts/highlight.js"></script><script>hljs.initHighlightingOnLoad();</script>' : ''; ?>
<script src="static/scripts/app.js?v=<?php echo Config::get("version"); ?>"></script>
<?php echo $scripts_html; ?>
</body>

View file

@ -13,7 +13,6 @@ var posts = {
limit: 5, // Limit posts per load
offset: 0, // Current offset
sort: "default", // Default is from newest to oldest posts (use reverse for oldest to newest)
filter: {
from: null, // Show posts from specified date
@ -35,10 +34,6 @@ var posts = {
// Update ID hash
location.hash.replace(/([a-z]+)\=([^\&]+)/g, function(_, key, value){
if (key == "sort") {
posts.sort = decodeURIComponent(value);
return;
}
posts.filter[key] = decodeURIComponent(value);
$(".more_posts").show();
});
@ -77,7 +72,6 @@ var posts = {
action: "load",
limit: posts.limit,
offset: posts.offset,
sort: posts.sort,
filter: posts.filter
},
success: function(posts_data){

View file

@ -51,11 +51,9 @@ var datepick = function(container) {
var thead = $(
'<thead>' +
'<tr>' +
'<th><button type="button" class="button blue prev_y" title="Previous Year">&lt;&lt;</button></th>' +
'<th><button type="button" class="button blue prev" title="Previous Month">&lt;</button></th>' +
'<th class="month-pick" colspan="3" title="Select Month">'+this.months[this.m]+' '+this.y+'</th>' +
'<th class="month-pick" colspan="5" title="Select Month">'+this.months[this.m]+' '+this.y+'</th>' +
'<th><button type="button" class="button blue next" title="Next Month">&gt;</button></th>' +
'<th><button type="button" class="button blue next_y" title="Next Year">&gt;&gt;</button></th>' +
'</tr>' +
'<tr>' +
'<th>Mo</th>' +
@ -70,22 +68,12 @@ var datepick = function(container) {
);
var x = this;
$(thead).find(".prev_y").click(function(){
x.dec_y();
x.load_table();
$(thead).find(".month-pick").text(x.months[x.m]+' '+x.y);
});
$(thead).find(".prev").click(function(){
x.dec_m();
x.load_table();
$(thead).find(".month-pick").text(x.months[x.m]+' '+x.y);
});
$(thead).find(".next_y").click(function(){
x.inc_y();
x.load_table();
$(thead).find(".month-pick").text(x.months[x.m]+' '+x.y);
});
$(thead).find(".next").click(function(){
x.inc_m();
x.load_table();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -114,10 +114,6 @@ body {
text-transform: uppercase;
}
#eof_feed .link {
color: #90949c;
}
.show_more {
height: 40px;
line-height: 40px;
@ -130,6 +126,7 @@ body {
background: linear-gradient(to bottom, rgba(255,255,255,0) 0%,rgba(255,255,255,1) 75%,rgba(255,255,255,1) 100%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#00ffffff', endColorstr='#ffffff',GradientType=0 );
cursor: pointer;
vertical-align: bottom;
}
.b_post {
@ -590,6 +587,11 @@ body {
margin: 8px;
}
.modal-footer .buttons .left {
display: inline-block;
float: left;
}
.modal-footer:before,
.modal-footer:after {
content: " ";
@ -777,8 +779,6 @@ body {
min-height: 88px;
max-width: 100%;
min-width: 100%;
background: transparent;
border: 0;
}
.options {
@ -1055,8 +1055,7 @@ code {
text-align: center;
}
.datepicker table {
width: 100%;
margin: 5px 0;
display: inline-block;
}
.datepicker th,
@ -1064,9 +1063,6 @@ code {
width: 12.5%;
}
.datepicker th {
padding: 5px 0;
}
.datepicker td {
color: #999;
padding: 5px;

View file

@ -181,10 +181,6 @@ body {
text-transform: uppercase;
}
#eof_feed .link {
color: var(--secondary-text);
}
.show_more {
height: 40px;
line-height: 40px;
@ -196,6 +192,7 @@ body {
background: -webkit-linear-gradient(top, transparent 0%,var(--primary-background) 75%,var(--primary-background) 100%);
background: linear-gradient(to bottom, transparent 0%,var(--primary-background) 75%,var(--primary-background) 100%);
cursor: pointer;
vertical-align: bottom;
}
.b_post {
@ -717,6 +714,11 @@ body {
margin-right: 0;
}
.modal-footer .buttons .left {
display: inline-block;
float: left;
}
.modal-footer:before,
.modal-footer:after {
content: " ";
@ -1173,8 +1175,7 @@ code {
text-align: center;
}
.datepicker table {
width: 100%;
margin: 5px 0;
display: inline-block;
}
.datepicker th,
@ -1182,9 +1183,6 @@ code {
width: 12.5%;
}
.datepicker th {
padding: 5px 0;
}
.datepicker td {
color: var(--datepicker-inactive-month);
padding: 5px;