forked from Public/pics
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d631a07d3d | |||
| 65d5cb62e5 |
8
app.php
8
app.php
@@ -14,13 +14,7 @@ require_once 'vendor/autoload.php';
|
|||||||
|
|
||||||
// Initialise the database.
|
// Initialise the database.
|
||||||
Registry::set('start', microtime(true));
|
Registry::set('start', microtime(true));
|
||||||
if (defined('DB_DRIVER') && DB_DRIVER === 'sqlite')
|
Registry::set('db', new Database(DB_SERVER, DB_USER, DB_PASS, DB_NAME));
|
||||||
Registry::set('db', new Database('sqlite', ['file' => DB_FILE]));
|
|
||||||
else
|
|
||||||
Registry::set('db', new Database('mysql', [
|
|
||||||
'host' => DB_SERVER, 'user' => DB_USER,
|
|
||||||
'password' => DB_PASS, 'name' => DB_NAME,
|
|
||||||
]));
|
|
||||||
|
|
||||||
// Handle errors our own way.
|
// Handle errors our own way.
|
||||||
ErrorHandler::enable();
|
ErrorHandler::enable();
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ class EditAlbum extends HTMLController
|
|||||||
$data = $this->form->getData();
|
$data = $this->form->getData();
|
||||||
|
|
||||||
// Sanity check: don't let an album be its own parent
|
// Sanity check: don't let an album be its own parent
|
||||||
if ($id_tag && $data['id_parent'] == $id_tag)
|
if ($data['id_parent'] == $id_tag)
|
||||||
{
|
{
|
||||||
return $this->formview->adopt(new Alert('Invalid parent', 'An album cannot be its own parent.', 'danger'));
|
return $this->formview->adopt(new Alert('Invalid parent', 'An album cannot be its own parent.', 'danger'));
|
||||||
}
|
}
|
||||||
|
|||||||
27
flake.lock
generated
27
flake.lock
generated
@@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
"nodes": {
|
|
||||||
"nixpkgs": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1770843696,
|
|
||||||
"narHash": "sha256-LovWTGDwXhkfCOmbgLVA10bvsi/P8eDDpRudgk68HA8=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "2343bbb58f99267223bc2aac4fc9ea301a155a16",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "NixOS",
|
|
||||||
"ref": "nixpkgs-unstable",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": "nixpkgs"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": "root",
|
|
||||||
"version": 7
|
|
||||||
}
|
|
||||||
47
flake.nix
47
flake.nix
@@ -1,47 +0,0 @@
|
|||||||
{
|
|
||||||
description = "HashRU Pics dev environment";
|
|
||||||
|
|
||||||
inputs = {
|
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
|
||||||
};
|
|
||||||
|
|
||||||
outputs = { self, nixpkgs }:
|
|
||||||
let
|
|
||||||
forAllSystems = f: nixpkgs.lib.genAttrs [
|
|
||||||
"x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin"
|
|
||||||
] (system: f nixpkgs.legacyPackages.${system});
|
|
||||||
|
|
||||||
php = pkgs: pkgs.php.buildEnv {
|
|
||||||
extensions = { enabled, all }: enabled ++ (with all; [
|
|
||||||
imagick
|
|
||||||
pdo_mysql
|
|
||||||
pdo_sqlite
|
|
||||||
]);
|
|
||||||
extraConfig = ''
|
|
||||||
memory_limit = 256M
|
|
||||||
upload_max_filesize = 50M
|
|
||||||
post_max_size = 50M
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
in
|
|
||||||
{
|
|
||||||
devShells = forAllSystems (pkgs: {
|
|
||||||
default = pkgs.mkShell {
|
|
||||||
buildInputs = [
|
|
||||||
(php pkgs)
|
|
||||||
(php pkgs).packages.composer
|
|
||||||
pkgs.sqlite
|
|
||||||
];
|
|
||||||
|
|
||||||
shellHook = ''
|
|
||||||
export COMPOSER_HOME="$PWD/.composer"
|
|
||||||
|
|
||||||
if [ ! -d vendor ]; then
|
|
||||||
echo "Running composer install..."
|
|
||||||
composer install
|
|
||||||
fi
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -10,6 +10,7 @@ class AssetIterator implements Iterator
|
|||||||
{
|
{
|
||||||
private $direction;
|
private $direction;
|
||||||
private $return_format;
|
private $return_format;
|
||||||
|
private $rowCount;
|
||||||
|
|
||||||
private $assets_iterator;
|
private $assets_iterator;
|
||||||
private $meta_iterator;
|
private $meta_iterator;
|
||||||
@@ -20,6 +21,7 @@ class AssetIterator implements Iterator
|
|||||||
{
|
{
|
||||||
$this->direction = $direction;
|
$this->direction = $direction;
|
||||||
$this->return_format = $return_format;
|
$this->return_format = $return_format;
|
||||||
|
$this->rowCount = $stmt_assets->rowCount();
|
||||||
|
|
||||||
$this->assets_iterator = new CachedPDOIterator($stmt_assets);
|
$this->assets_iterator = new CachedPDOIterator($stmt_assets);
|
||||||
$this->assets_iterator->rewind();
|
$this->assets_iterator->rewind();
|
||||||
@@ -207,7 +209,7 @@ class AssetIterator implements Iterator
|
|||||||
|
|
||||||
public function num(): int
|
public function num(): int
|
||||||
{
|
{
|
||||||
return count($this->assets_iterator);
|
return $this->rowCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function rewind(): void
|
public function rewind(): void
|
||||||
|
|||||||
@@ -9,35 +9,18 @@
|
|||||||
class Database
|
class Database
|
||||||
{
|
{
|
||||||
private $connection;
|
private $connection;
|
||||||
private $driver;
|
|
||||||
private $query_count = 0;
|
private $query_count = 0;
|
||||||
private $logged_queries = [];
|
private $logged_queries = [];
|
||||||
|
|
||||||
public function __construct($driver, array $options)
|
public function __construct($host, $user, $password, $name)
|
||||||
{
|
{
|
||||||
$this->driver = $driver;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if ($driver === 'sqlite')
|
$this->connection = new PDO("mysql:host=$host;dbname=$name;charset=utf8mb4", $user, $password, [
|
||||||
{
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||||
$this->connection = new PDO("sqlite:" . $options['file'], null, null, [
|
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
PDO::ATTR_EMULATE_PREPARES => false,
|
||||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
]);
|
||||||
]);
|
|
||||||
$this->connection->exec('PRAGMA journal_mode=WAL');
|
|
||||||
$this->registerSQLiteFunctions();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$this->connection = new PDO(
|
|
||||||
"mysql:host={$options['host']};dbname={$options['name']};charset=utf8mb4",
|
|
||||||
$options['user'], $options['password'], [
|
|
||||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
|
||||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
|
||||||
PDO::ATTR_EMULATE_PREPARES => false,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Give up if we have a connection error.
|
// Give up if we have a connection error.
|
||||||
catch (PDOException $e)
|
catch (PDOException $e)
|
||||||
@@ -48,54 +31,6 @@ class Database
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function registerSQLiteFunctions()
|
|
||||||
{
|
|
||||||
$pdo = $this->connection;
|
|
||||||
|
|
||||||
$pdo->sqliteCreateFunction('CONCAT', function () {
|
|
||||||
return implode('', func_get_args());
|
|
||||||
}, -1);
|
|
||||||
|
|
||||||
$pdo->sqliteCreateFunction('IF', function ($cond, $t, $f) {
|
|
||||||
return $cond ? $t : $f;
|
|
||||||
}, 3);
|
|
||||||
|
|
||||||
$pdo->sqliteCreateFunction('FROM_UNIXTIME', function ($ts) {
|
|
||||||
return date('Y-m-d H:i:s', $ts);
|
|
||||||
}, 1);
|
|
||||||
|
|
||||||
$pdo->sqliteCreateFunction('UNIX_TIMESTAMP', function () {
|
|
||||||
return time();
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
$pdo->sqliteCreateFunction('CURRENT_TIMESTAMP', function () {
|
|
||||||
return date('Y-m-d H:i:s');
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getDriver()
|
|
||||||
{
|
|
||||||
return $this->driver;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function rewriteForSQLite($sql)
|
|
||||||
{
|
|
||||||
// REPLACE INTO → INSERT OR REPLACE INTO
|
|
||||||
$sql = preg_replace('/\bREPLACE\s+INTO\b/i', 'INSERT OR REPLACE INTO', $sql);
|
|
||||||
|
|
||||||
// INSERT IGNORE INTO → INSERT OR IGNORE INTO
|
|
||||||
$sql = preg_replace('/\bINSERT\s+IGNORE\s+INTO\b/i', 'INSERT OR IGNORE INTO', $sql);
|
|
||||||
|
|
||||||
// LIMIT :offset, :limit → LIMIT :limit OFFSET :offset
|
|
||||||
$sql = preg_replace(
|
|
||||||
'/\bLIMIT\s+:offset\s*,\s*:limit\b/i',
|
|
||||||
'LIMIT :limit OFFSET :offset',
|
|
||||||
$sql
|
|
||||||
);
|
|
||||||
|
|
||||||
return $sql;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getQueryCount()
|
public function getQueryCount()
|
||||||
{
|
{
|
||||||
return $this->query_count;
|
return $this->query_count;
|
||||||
@@ -245,10 +180,6 @@ class Database
|
|||||||
// Preprocessing/checks: prepare any arrays for binding
|
// Preprocessing/checks: prepare any arrays for binding
|
||||||
$db_string = $this->expandPlaceholders($db_string, $db_values);
|
$db_string = $this->expandPlaceholders($db_string, $db_values);
|
||||||
|
|
||||||
// SQLite query rewriting
|
|
||||||
if ($this->driver === 'sqlite')
|
|
||||||
$db_string = $this->rewriteForSQLite($db_string);
|
|
||||||
|
|
||||||
// Prepare query for execution
|
// Prepare query for execution
|
||||||
$statement = $this->connection->prepare($db_string);
|
$statement = $this->connection->prepare($db_string);
|
||||||
|
|
||||||
@@ -292,10 +223,13 @@ class Database
|
|||||||
{
|
{
|
||||||
$res = $this->query($db_string, $db_values);
|
$res = $this->query($db_string, $db_values);
|
||||||
|
|
||||||
|
if (!$res || $this->rowCount($res) === 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
$object = $this->fetchObject($res, $class);
|
$object = $this->fetchObject($res, $class);
|
||||||
$this->free($res);
|
$this->free($res);
|
||||||
|
|
||||||
return $object ?: null;
|
return $object;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -305,6 +239,9 @@ class Database
|
|||||||
{
|
{
|
||||||
$res = $this->query($db_string, $db_values);
|
$res = $this->query($db_string, $db_values);
|
||||||
|
|
||||||
|
if (!$res || $this->rowCount($res) === 0)
|
||||||
|
return [];
|
||||||
|
|
||||||
$rows = [];
|
$rows = [];
|
||||||
while ($object = $this->fetchObject($res, $class))
|
while ($object = $this->fetchObject($res, $class))
|
||||||
$rows[] = $object;
|
$rows[] = $object;
|
||||||
@@ -321,10 +258,13 @@ class Database
|
|||||||
{
|
{
|
||||||
$res = $this->query($db_string, $db_values);
|
$res = $this->query($db_string, $db_values);
|
||||||
|
|
||||||
|
if ($this->rowCount($res) === 0)
|
||||||
|
return [];
|
||||||
|
|
||||||
$row = $this->fetchNum($res);
|
$row = $this->fetchNum($res);
|
||||||
$this->free($res);
|
$this->free($res);
|
||||||
|
|
||||||
return $row ?: [];
|
return $row;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -334,6 +274,9 @@ class Database
|
|||||||
{
|
{
|
||||||
$res = $this->query($db_string, $db_values);
|
$res = $this->query($db_string, $db_values);
|
||||||
|
|
||||||
|
if ($this->rowCount($res) === 0)
|
||||||
|
return [];
|
||||||
|
|
||||||
$rows = [];
|
$rows = [];
|
||||||
while ($row = $this->fetchNum($res))
|
while ($row = $this->fetchNum($res))
|
||||||
$rows[] = $row;
|
$rows[] = $row;
|
||||||
@@ -350,6 +293,9 @@ class Database
|
|||||||
{
|
{
|
||||||
$res = $this->query($db_string, $db_values);
|
$res = $this->query($db_string, $db_values);
|
||||||
|
|
||||||
|
if ($this->rowCount($res) === 0)
|
||||||
|
return [];
|
||||||
|
|
||||||
$rows = [];
|
$rows = [];
|
||||||
while ($row = $this->fetchNum($res))
|
while ($row = $this->fetchNum($res))
|
||||||
$rows[$row[0]] = $row[1];
|
$rows[$row[0]] = $row[1];
|
||||||
@@ -366,6 +312,9 @@ class Database
|
|||||||
{
|
{
|
||||||
$res = $this->query($db_string, $db_values);
|
$res = $this->query($db_string, $db_values);
|
||||||
|
|
||||||
|
if (!$res || $this->rowCount($res) === 0)
|
||||||
|
return [];
|
||||||
|
|
||||||
$rows = [];
|
$rows = [];
|
||||||
while ($row = $this->fetchAssoc($res))
|
while ($row = $this->fetchAssoc($res))
|
||||||
{
|
{
|
||||||
@@ -385,10 +334,13 @@ class Database
|
|||||||
{
|
{
|
||||||
$res = $this->query($db_string, $db_values);
|
$res = $this->query($db_string, $db_values);
|
||||||
|
|
||||||
|
if ($this->rowCount($res) === 0)
|
||||||
|
return [];
|
||||||
|
|
||||||
$row = $this->fetchAssoc($res);
|
$row = $this->fetchAssoc($res);
|
||||||
$this->free($res);
|
$this->free($res);
|
||||||
|
|
||||||
return $row ?: [];
|
return $row;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -398,6 +350,9 @@ class Database
|
|||||||
{
|
{
|
||||||
$res = $this->query($db_string, $db_values);
|
$res = $this->query($db_string, $db_values);
|
||||||
|
|
||||||
|
if ($this->rowCount($res) === 0)
|
||||||
|
return [];
|
||||||
|
|
||||||
$rows = [];
|
$rows = [];
|
||||||
while ($row = $this->fetchAssoc($res))
|
while ($row = $this->fetchAssoc($res))
|
||||||
$rows[] = $row;
|
$rows[] = $row;
|
||||||
@@ -414,13 +369,14 @@ class Database
|
|||||||
{
|
{
|
||||||
$res = $this->query($db_string, $db_values);
|
$res = $this->query($db_string, $db_values);
|
||||||
|
|
||||||
$row = $this->fetchNum($res);
|
// If this happens, you're doing it wrong.
|
||||||
$this->free($res);
|
if ($this->rowCount($res) === 0)
|
||||||
|
|
||||||
if (!$row)
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return $row[0];
|
list($value) = $this->fetchNum($res);
|
||||||
|
$this->free($res);
|
||||||
|
|
||||||
|
return $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -430,6 +386,9 @@ class Database
|
|||||||
{
|
{
|
||||||
$res = $this->query($db_string, $db_values);
|
$res = $this->query($db_string, $db_values);
|
||||||
|
|
||||||
|
if ($this->rowCount($res) === 0)
|
||||||
|
return [];
|
||||||
|
|
||||||
$rows = [];
|
$rows = [];
|
||||||
while ($row = $this->fetchNum($res))
|
while ($row = $this->fetchNum($res))
|
||||||
$rows[] = $row[0];
|
$rows[] = $row[0];
|
||||||
@@ -453,10 +412,7 @@ class Database
|
|||||||
$data = [$data];
|
$data = [$data];
|
||||||
|
|
||||||
// Determine the method of insertion.
|
// Determine the method of insertion.
|
||||||
if ($this->driver === 'sqlite')
|
$method = $method == 'replace' ? 'REPLACE' : ($method == 'ignore' ? 'INSERT IGNORE' : 'INSERT');
|
||||||
$method = $method == 'replace' ? 'INSERT OR REPLACE' : ($method == 'ignore' ? 'INSERT OR IGNORE' : 'INSERT');
|
|
||||||
else
|
|
||||||
$method = $method == 'replace' ? 'REPLACE' : ($method == 'ignore' ? 'INSERT IGNORE' : 'INSERT');
|
|
||||||
|
|
||||||
// What columns are we inserting?
|
// What columns are we inserting?
|
||||||
$columns = array_keys($data[0]);
|
$columns = array_keys($data[0]);
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ class Dispatcher
|
|||||||
private static function trigger404()
|
private static function trigger404()
|
||||||
{
|
{
|
||||||
http_response_code(404);
|
http_response_code(404);
|
||||||
self::errorPage('Page not found!', 'The page you requested could not be found.');
|
$page = new ViewErrorPage('Page not found!');
|
||||||
exit;
|
$page->showContent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ class ErrorHandler
|
|||||||
'debug_info' => $debug_info,
|
'debug_info' => $debug_info,
|
||||||
'file' => str_replace(BASEDIR, '', $file),
|
'file' => str_replace(BASEDIR, '', $file),
|
||||||
'line' => $line,
|
'line' => $line,
|
||||||
'id_user' => Registry::has('user') && Registry::get('user')->getUserId() ? Registry::get('user')->getUserId() : 0,
|
'id_user' => Registry::has('user') ? Registry::get('user')->getUserId() : 0,
|
||||||
'ip_address' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '',
|
'ip_address' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '',
|
||||||
'request_uri' => isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '',
|
'request_uri' => isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '',
|
||||||
]))
|
]))
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class Router
|
|||||||
$possibleActions = [
|
$possibleActions = [
|
||||||
'accountsettings' => 'AccountSettings',
|
'accountsettings' => 'AccountSettings',
|
||||||
'addalbum' => 'EditAlbum',
|
'addalbum' => 'EditAlbum',
|
||||||
'albums' => 'ViewPhotoAlbum',
|
'albums' => 'ViewPhotoAlbums',
|
||||||
'editalbum' => 'EditAlbum',
|
'editalbum' => 'EditAlbum',
|
||||||
'editasset' => 'EditAsset',
|
'editasset' => 'EditAsset',
|
||||||
'edittag' => 'EditTag',
|
'edittag' => 'EditTag',
|
||||||
@@ -55,7 +55,7 @@ class Router
|
|||||||
return new GenerateThumbnail();
|
return new GenerateThumbnail();
|
||||||
}
|
}
|
||||||
// Look for particular actions...
|
// Look for particular actions...
|
||||||
elseif (preg_match('~^/(?<action>[a-z]+)(?:/page/(?<page>\d+))?/?$~', $_SERVER['PATH_INFO'], $path) && isset($possibleActions[$path['action']]))
|
elseif (preg_match('~^/(?<action>[a-z]+)(?:/page/(?<page>\d+))?/?~', $_SERVER['PATH_INFO'], $path) && isset($possibleActions[$path['action']]))
|
||||||
{
|
{
|
||||||
$_GET = array_merge($_GET, $path);
|
$_GET = array_merge($_GET, $path);
|
||||||
return new $possibleActions[$path['action']]();
|
return new $possibleActions[$path['action']]();
|
||||||
|
|||||||
@@ -122,12 +122,10 @@ class Tag
|
|||||||
|
|
||||||
public static function getAlbums($id_parent = 0, $offset = 0, $limit = 24, $return_format = 'array')
|
public static function getAlbums($id_parent = 0, $offset = 0, $limit = 24, $return_format = 'array')
|
||||||
{
|
{
|
||||||
$parent_clause = empty($id_parent) ? '(id_parent = :id_parent OR id_parent IS NULL)' : 'id_parent = :id_parent';
|
|
||||||
|
|
||||||
$rows = Registry::get('db')->queryAssocs('
|
$rows = Registry::get('db')->queryAssocs('
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM tags
|
FROM tags
|
||||||
WHERE ' . $parent_clause . ' AND kind = :kind
|
WHERE id_parent = :id_parent AND kind = :kind
|
||||||
ORDER BY tag ASC
|
ORDER BY tag ASC
|
||||||
LIMIT :offset, :limit',
|
LIMIT :offset, :limit',
|
||||||
[
|
[
|
||||||
@@ -165,12 +163,10 @@ class Tag
|
|||||||
|
|
||||||
public static function getPeople($id_parent = 0, $offset = 0, $limit = 24, $return_format = 'array')
|
public static function getPeople($id_parent = 0, $offset = 0, $limit = 24, $return_format = 'array')
|
||||||
{
|
{
|
||||||
$parent_clause = empty($id_parent) ? '(id_parent = :id_parent OR id_parent IS NULL)' : 'id_parent = :id_parent';
|
|
||||||
|
|
||||||
$rows = Registry::get('db')->queryAssocs('
|
$rows = Registry::get('db')->queryAssocs('
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM tags
|
FROM tags
|
||||||
WHERE ' . $parent_clause . ' AND kind = :kind
|
WHERE id_parent = :id_parent AND kind = :kind
|
||||||
ORDER BY tag ASC
|
ORDER BY tag ASC
|
||||||
LIMIT :offset, :limit',
|
LIMIT :offset, :limit',
|
||||||
[
|
[
|
||||||
@@ -253,21 +249,7 @@ class Tag
|
|||||||
|
|
||||||
public static function recount(array $id_tags = [])
|
public static function recount(array $id_tags = [])
|
||||||
{
|
{
|
||||||
$db = Registry::get('db');
|
return Registry::get('db')->query('
|
||||||
|
|
||||||
if ($db->getDriver() === 'sqlite')
|
|
||||||
{
|
|
||||||
return $db->query('
|
|
||||||
UPDATE tags SET count = (
|
|
||||||
SELECT COUNT(*)
|
|
||||||
FROM `assets_tags` AS at
|
|
||||||
WHERE at.id_tag = tags.id_tag
|
|
||||||
)' . (!empty($id_tags) ? '
|
|
||||||
WHERE tags.id_tag IN(@id_tags)' : ''),
|
|
||||||
['id_tags' => $id_tags]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $db->query('
|
|
||||||
UPDATE tags AS t SET count = (
|
UPDATE tags AS t SET count = (
|
||||||
SELECT COUNT(*)
|
SELECT COUNT(*)
|
||||||
FROM `assets_tags` AS at
|
FROM `assets_tags` AS at
|
||||||
@@ -290,26 +272,13 @@ class Tag
|
|||||||
if (!isset($data['count']))
|
if (!isset($data['count']))
|
||||||
$data['count'] = 0;
|
$data['count'] = 0;
|
||||||
|
|
||||||
if ($db->getDriver() === 'sqlite')
|
$res = $db->query('
|
||||||
{
|
INSERT IGNORE INTO tags
|
||||||
$res = $db->query('
|
(id_parent, tag, slug, kind, description, count)
|
||||||
INSERT INTO tags
|
VALUES
|
||||||
(id_parent, tag, slug, kind, description, count)
|
(:id_parent, :tag, :slug, :kind, :description, :count)
|
||||||
VALUES
|
ON DUPLICATE KEY UPDATE count = count + 1',
|
||||||
(:id_parent, :tag, :slug, :kind, :description, :count)
|
$data);
|
||||||
ON CONFLICT(slug) DO UPDATE SET count = count + 1',
|
|
||||||
$data);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$res = $db->query('
|
|
||||||
INSERT IGNORE INTO tags
|
|
||||||
(id_parent, tag, slug, kind, description, count)
|
|
||||||
VALUES
|
|
||||||
(:id_parent, :tag, :slug, :kind, :description, :count)
|
|
||||||
ON DUPLICATE KEY UPDATE count = count + 1',
|
|
||||||
$data);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$res)
|
if (!$res)
|
||||||
throw new Exception('Could not create the requested tag.');
|
throw new Exception('Could not create the requested tag.');
|
||||||
@@ -325,8 +294,6 @@ class Tag
|
|||||||
|
|
||||||
public function save()
|
public function save()
|
||||||
{
|
{
|
||||||
$vars = get_object_vars($this);
|
|
||||||
|
|
||||||
return Registry::get('db')->query('
|
return Registry::get('db')->query('
|
||||||
UPDATE tags
|
UPDATE tags
|
||||||
SET
|
SET
|
||||||
@@ -339,7 +306,7 @@ class Tag
|
|||||||
description = :description,
|
description = :description,
|
||||||
count = :count
|
count = :count
|
||||||
WHERE id_tag = :id_tag',
|
WHERE id_tag = :id_tag',
|
||||||
$vars);
|
get_object_vars($this));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete()
|
public function delete()
|
||||||
@@ -494,12 +461,10 @@ class Tag
|
|||||||
$albums_by_parent = [];
|
$albums_by_parent = [];
|
||||||
while ($row = $db->fetchAssoc($res))
|
while ($row = $db->fetchAssoc($res))
|
||||||
{
|
{
|
||||||
$parent = $row['id_parent'];
|
if (!isset($albums_by_parent[$row['id_parent']]))
|
||||||
|
$albums_by_parent[$row['id_parent']] = [];
|
||||||
|
|
||||||
if (!isset($albums_by_parent[$parent]))
|
$albums_by_parent[$row['id_parent']][] = $row + ['children' => []];
|
||||||
$albums_by_parent[$parent] = [];
|
|
||||||
|
|
||||||
$albums_by_parent[$parent][] = $row + ['children' => []];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$albums = self::getChildrenRecursively(0, 0, $albums_by_parent);
|
$albums = self::getChildrenRecursively(0, 0, $albums_by_parent);
|
||||||
|
|||||||
@@ -30,38 +30,21 @@ function disableKeyDownPropagation(obj) {
|
|||||||
function enableTouchNavigation() {
|
function enableTouchNavigation() {
|
||||||
var x_down = null;
|
var x_down = null;
|
||||||
var y_down = null;
|
var y_down = null;
|
||||||
var cancelled = false;
|
|
||||||
|
|
||||||
document.addEventListener('touchstart', function(event) {
|
document.addEventListener('touchstart', function(event) {
|
||||||
if (event.touches.length > 1) {
|
|
||||||
cancelled = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
x_down = event.touches[0].clientX;
|
x_down = event.touches[0].clientX;
|
||||||
y_down = event.touches[0].clientY;
|
y_down = event.touches[0].clientY;
|
||||||
cancelled = false;
|
|
||||||
}, false);
|
}, false);
|
||||||
|
|
||||||
document.addEventListener('touchmove', function(event) {
|
document.addEventListener('touchmove', function(event) {
|
||||||
if (event.touches.length > 1) {
|
if (!x_down || !y_down) {
|
||||||
cancelled = true;
|
|
||||||
}
|
|
||||||
}, false);
|
|
||||||
|
|
||||||
document.addEventListener('touchend', function(event) {
|
|
||||||
if (cancelled || x_down === null || y_down === null) {
|
|
||||||
x_down = null;
|
|
||||||
y_down = null;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var x_diff = x_down - event.changedTouches[0].clientX;
|
var x_diff = x_down - event.touches[0].clientX;
|
||||||
var y_diff = y_down - event.changedTouches[0].clientY;
|
var y_diff = y_down - event.touches[0].clientY;
|
||||||
|
|
||||||
x_down = null;
|
if (Math.abs(y_diff) > 50) {
|
||||||
y_down = null;
|
|
||||||
|
|
||||||
if (Math.abs(x_diff) < 40 || Math.abs(y_diff) > 50) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,11 +52,13 @@ function enableTouchNavigation() {
|
|||||||
if (x_diff > 0) {
|
if (x_diff > 0) {
|
||||||
var target = document.getElementById("previous_photo").href;
|
var target = document.getElementById("previous_photo").href;
|
||||||
if (target) {
|
if (target) {
|
||||||
|
event.preventDefault();
|
||||||
document.location.href = target + '#photo_frame';
|
document.location.href = target + '#photo_frame';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var target = document.getElementById("next_photo").href;
|
var target = document.getElementById("next_photo").href;
|
||||||
if (target) {
|
if (target) {
|
||||||
|
event.preventDefault();
|
||||||
document.location.href = target + '#photo_frame';
|
document.location.href = target + '#photo_frame';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,104 +0,0 @@
|
|||||||
-- SQLite schema for Kabuki CMS / pics
|
|
||||||
--
|
|
||||||
-- Usage:
|
|
||||||
-- sqlite3 data/pics.sqlite < schema.sqlite.sql
|
|
||||||
--
|
|
||||||
-- Config (add to config.php):
|
|
||||||
-- define('DB_DRIVER', 'sqlite');
|
|
||||||
-- define('DB_FILE', __DIR__ . '/data/pics.sqlite');
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS users (
|
|
||||||
id_user INTEGER PRIMARY KEY,
|
|
||||||
first_name TEXT NOT NULL,
|
|
||||||
surname TEXT NOT NULL,
|
|
||||||
slug TEXT NOT NULL UNIQUE,
|
|
||||||
emailaddress TEXT NOT NULL UNIQUE,
|
|
||||||
password_hash TEXT NOT NULL,
|
|
||||||
creation_time INTEGER NOT NULL,
|
|
||||||
last_action_time INTEGER,
|
|
||||||
ip_address TEXT,
|
|
||||||
is_admin INTEGER NOT NULL DEFAULT 0,
|
|
||||||
reset_key TEXT,
|
|
||||||
reset_blocked_until INTEGER,
|
|
||||||
oidc_sub TEXT UNIQUE
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS assets (
|
|
||||||
id_asset INTEGER PRIMARY KEY,
|
|
||||||
id_user_uploaded INTEGER NOT NULL,
|
|
||||||
subdir TEXT NOT NULL,
|
|
||||||
filename TEXT NOT NULL,
|
|
||||||
title TEXT,
|
|
||||||
slug TEXT UNIQUE,
|
|
||||||
mimetype TEXT,
|
|
||||||
image_width INTEGER,
|
|
||||||
image_height INTEGER,
|
|
||||||
date_captured TEXT,
|
|
||||||
priority INTEGER DEFAULT 0
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS assets_meta (
|
|
||||||
id_asset INTEGER NOT NULL,
|
|
||||||
variable TEXT NOT NULL,
|
|
||||||
value TEXT,
|
|
||||||
PRIMARY KEY (id_asset, variable)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS assets_thumbs (
|
|
||||||
id_asset INTEGER NOT NULL,
|
|
||||||
width INTEGER NOT NULL,
|
|
||||||
height INTEGER NOT NULL,
|
|
||||||
mode TEXT,
|
|
||||||
filename TEXT,
|
|
||||||
PRIMARY KEY (id_asset, width, height, mode)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS tags (
|
|
||||||
id_tag INTEGER PRIMARY KEY,
|
|
||||||
id_parent INTEGER,
|
|
||||||
id_asset_thumb INTEGER,
|
|
||||||
id_user_owner INTEGER,
|
|
||||||
tag TEXT NOT NULL,
|
|
||||||
slug TEXT NOT NULL UNIQUE,
|
|
||||||
description TEXT,
|
|
||||||
kind TEXT NOT NULL DEFAULT 'Tag',
|
|
||||||
count INTEGER DEFAULT 0
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS assets_tags (
|
|
||||||
id_asset INTEGER NOT NULL,
|
|
||||||
id_tag INTEGER NOT NULL,
|
|
||||||
PRIMARY KEY (id_asset, id_tag)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS posts_assets (
|
|
||||||
id_post INTEGER NOT NULL,
|
|
||||||
id_asset INTEGER NOT NULL,
|
|
||||||
PRIMARY KEY (id_post, id_asset)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS posts_tags (
|
|
||||||
id_post INTEGER NOT NULL,
|
|
||||||
id_tag INTEGER NOT NULL,
|
|
||||||
PRIMARY KEY (id_post, id_tag)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS settings (
|
|
||||||
id_user INTEGER NOT NULL,
|
|
||||||
variable TEXT NOT NULL,
|
|
||||||
value TEXT,
|
|
||||||
time_set TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY (id_user, variable)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS log_errors (
|
|
||||||
id_entry INTEGER PRIMARY KEY,
|
|
||||||
id_user INTEGER,
|
|
||||||
message TEXT,
|
|
||||||
debug_info TEXT,
|
|
||||||
file TEXT,
|
|
||||||
line INTEGER,
|
|
||||||
request_uri TEXT,
|
|
||||||
time TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
ip_address TEXT
|
|
||||||
);
|
|
||||||
58
seed.php
58
seed.php
@@ -1,58 +0,0 @@
|
|||||||
<?php
|
|
||||||
/*****************************************************************************
|
|
||||||
* seed.php
|
|
||||||
* Seeds a fresh database with an admin user and root album.
|
|
||||||
*
|
|
||||||
* Usage: php seed.php
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
require_once __DIR__ . '/config.php';
|
|
||||||
require_once __DIR__ . '/vendor/autoload.php';
|
|
||||||
|
|
||||||
if (!defined('DB_DRIVER') || DB_DRIVER !== 'sqlite')
|
|
||||||
{
|
|
||||||
echo "Error: seed.php currently only supports SQLite.\n";
|
|
||||||
echo "Set DB_DRIVER to 'sqlite' in config.php.\n";
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!file_exists(DB_FILE))
|
|
||||||
{
|
|
||||||
echo "Error: database file not found at " . DB_FILE . "\n";
|
|
||||||
echo "Create it first: sqlite3 " . DB_FILE . " < schema.sqlite.sql\n";
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
$db = new Database('sqlite', ['file' => DB_FILE]);
|
|
||||||
Registry::set('db', $db);
|
|
||||||
|
|
||||||
// Create admin user.
|
|
||||||
$password = 'admin';
|
|
||||||
$hash = password_hash($password, PASSWORD_DEFAULT);
|
|
||||||
|
|
||||||
$db->insert('insert', 'users', [], [
|
|
||||||
'first_name' => 'Admin',
|
|
||||||
'surname' => 'User',
|
|
||||||
'slug' => 'admin',
|
|
||||||
'emailaddress' => 'admin@localhost',
|
|
||||||
'password_hash' => $hash,
|
|
||||||
'creation_time' => time(),
|
|
||||||
'ip_address' => '',
|
|
||||||
'is_admin' => 1,
|
|
||||||
'reset_key' => '',
|
|
||||||
]);
|
|
||||||
|
|
||||||
echo "Created admin user (admin@localhost / admin)\n";
|
|
||||||
|
|
||||||
// Create root album (id_tag = 1).
|
|
||||||
$db->insert('insert', 'tags', [], [
|
|
||||||
'id_parent' => 0,
|
|
||||||
'tag' => 'Albums',
|
|
||||||
'slug' => 'albums',
|
|
||||||
'kind' => 'Album',
|
|
||||||
'description' => '',
|
|
||||||
'count' => 0,
|
|
||||||
]);
|
|
||||||
|
|
||||||
echo "Created root album (id_tag = 1)\n";
|
|
||||||
echo "\nDone. You can now log in at the web UI.\n";
|
|
||||||
Reference in New Issue
Block a user