commit 70344076ae0e8e7a679d71a9f12455236029ad74 Author: mirzaev Date: Thu Jan 11 04:35:40 2024 +0700 template is created diff --git a/${REPO_OWNER}/${REPO_NAME}/system/controllers/core.php b/${REPO_OWNER}/${REPO_NAME}/system/controllers/core.php new file mode 100644 index 0000000..9d3859a --- /dev/null +++ b/${REPO_OWNER}/${REPO_NAME}/system/controllers/core.php @@ -0,0 +1,121 @@ + + */ +class core extends controller +{ + /** + * Postfix for name of controllers files + */ + final public const POSTFIX = ''; + + /** + * Instance of a session + */ + protected readonly session $session; + + /** + * Instance of an account + */ + protected readonly ?account $account; + + /** + * Registry of errors + */ + protected array $errors = [ + 'session' => [], + 'account' => [] + ]; + + /** + * Constructor of an instance + * + * @param bool $initialize Initialize a controller? + * + * @return void + */ + public function __construct(bool $initialize = true) + { + // Blocking requests from CloudFlare (better to write this blocking into nginx config file) + if ($_SERVER['HTTP_USER_AGENT'] === 'nginx-ssl early hints') return; + + // For the extends system + parent::__construct($initialize); + + if ($initialize) { + // Initializing is requested + + // Initializing of models core (connect to ArangoDB...) + new models(); + + // Initializing of the date until which the session will be active + $expires = strtotime('+1 week'); + + // Initializing of default value of hash of the session + $_COOKIE["session"] ??= null; + + // Initializing of session + $this->session = new session($_COOKIE["session"], $expires, $this->errors['session']); + + // Handle a problems with initializing a session + if (!empty($this->errors['session'])) die; + else if ($_COOKIE["session"] !== $this->session->hash) { + // Hash of the session is changed (implies that the session has expired and recreated) + + // Write a new hash of the session to cookies + setcookie( + 'session', + $this->session->hash, + [ + 'expires' => $expires, + 'path' => '/', + 'secure' => true, + 'httponly' => true, + 'samesite' => 'strict' + ] + ); + } + + // Initializing of preprocessor of views + $this->view = new templater($this->session); + } + } + + /** + * Check of initialization + * + * Checks whether a property is initialized in a document instance from ArangoDB + * + * @param string $name Name of the property from ArangoDB + * + * @return bool The property is initialized? + */ + public function __isset(string $name): bool + { + // Check of initialization of the property and exit (success) + return match ($name) { + default => isset($this->{$name}) + }; + } + +} diff --git a/${REPO_OWNER}/${REPO_NAME}/system/controllers/index.php b/${REPO_OWNER}/${REPO_NAME}/system/controllers/index.php new file mode 100644 index 0000000..84aadb4 --- /dev/null +++ b/${REPO_OWNER}/${REPO_NAME}/system/controllers/index.php @@ -0,0 +1,32 @@ + + */ +final class index extends core +{ + /** + * Render the main page + * + * @param array $parameters Parameters of the request (POST + GET) + */ + public function index(array $parameters = []): ?string + { + // Exit (success) + if ($_SERVER['REQUEST_METHOD'] === 'GET') return $this->view->render(DIRECTORY_SEPARATOR . 'index.html'); + else if ($_SERVER['REQUEST_METHOD'] === 'POST') return $main; + + // Exit (fail) + return null; + } +} diff --git a/${REPO_OWNER}/${REPO_NAME}/system/models/core.php b/${REPO_OWNER}/${REPO_NAME}/system/models/core.php new file mode 100644 index 0000000..3d353c0 --- /dev/null +++ b/${REPO_OWNER}/${REPO_NAME}/system/models/core.php @@ -0,0 +1,289 @@ + + */ +class core extends model +{ + /** + * Postfix for name of models files + */ + final public const POSTFIX = ''; + + /** + * Path to the file with settings of connecting to the ArangoDB + */ + final public const ARANGODB = '../settings/arangodb.php'; + + /** + * Instance of the session of ArangoDB + */ + protected static arangodb $arangodb; + + /** + * Name of the collection in ArangoDB + */ + public const COLLECTION = 'THIS_COLLECTION_SHOULD_NOT_EXIST_REPLACE_IT_IN_THE_MODEL'; + + /** + * Constructor of an instance + * + * @param bool $initialize Initialize a model? + * @param ?arangodb $arangodb Instance of a session of ArangoDB + * + * @return void + */ + public function __construct(bool $initialize = true, ?arangodb $arangodb = null) + { + // For the extends system + parent::__construct($initialize); + + if ($initialize) { + // Initializing is requested + + if (isset($arangodb)) { + // Recieved an instance of a session of ArangoDB + + // Write an instance of a session of ArangoDB to the property + $this->__set('arangodb', $arangodb); + } else { + // Not recieved an instance of a session of ArangoDB + + // Initializing of an instance of a session of ArangoDB + $this->__get('arangodb'); + } + } + } + + /** + * Read from ArangoDB + * + * @param string $filter Expression for filtering (AQL) + * @param string $sort Expression for sorting (AQL) + * @param int $amount Amount of documents for collect + * @param int $page Page + * @param string $return Expression describing the parameters to return (AQL) + * @param array &$errors The registry on errors + * + * @return _document|array|null An array of instances of documents from ArangoDB, if they are found + */ + public static function read( + string $filter = '', + string $sort = 'd.created DESC, d._key DESC', + int $amount = 1, + int $page = 1, + string $return = 'd', + array &$errors = [] + ): _document|array|null { + try { + if (collection::init(static::$arangodb->session, static::COLLECTION)) { + // Initialized the collection + + // Read from ArangoDB and exit (success) + return collection::search( + static::$arangodb->session, + sprintf( + <<<'AQL' + FOR d IN %s + %s + %s + LIMIT %d, %d + RETURN %s + AQL, + static::COLLECTION, + empty($filter) ? '' : "FILTER $filter", + empty($sort) ? '' : "SORT $sort", + --$page <= 0 ? 0 : $amount * $page, + $amount, + $return + ) + ); + } else throw new exception('Failed to initialize the collection'); + } catch (exception $e) { + // Write to the registry of errors + $errors[] = [ + 'text' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'stack' => $e->getTrace() + ]; + } + + // Exit (fail) + return null; + } + + /** + * Delete from ArangoDB + * + * @param _document $instance Instance of the document from ArangoDB + * @param array &$errors The registry on errors + * + * @return bool Deleted from ArangoDB without errors? + */ + public static function delete(_document $instance, array &$errors = []): bool + { + try { + if (collection::init(static::$arangodb->session, static::COLLECTION)) { + // Initialized the collection + + // Delete from ArangoDB and exit (success) + return (new _document_handler(static::$arangodb->session))->remove($instance); + } else throw new exception('Failed to initialize the collection'); + } catch (exception $e) { + // Write to the registry of errors + $errors[] = [ + 'text' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'stack' => $e->getTrace() + ]; + } + + // Exit (fail) + return false; + } + + /** + * Update in ArangoDB + * + * @param _document $instance Instance of the document from ArangoDB + * + * @return bool Writed to ArangoDB without errors? + */ + public static function update(_document $instance): bool + { + // Update in ArangoDB and exit (success) + return document::update(static::$arangodb->session, $instance); + } + + /** + * Write + * + * @param string $name Name of the property + * @param mixed $value Value of the property + * + * @return void + */ + public function __set(string $name, mixed $value = null): void + { + match ($name) { + 'arangodb' => (function () use ($value) { + if ($this->__isset('arangodb')) { + // Is alredy initialized + + // Exit (fail) + throw new exception('Forbidden to reinitialize the session of ArangoDB ($this::$arangodb)', 500); + } else { + // Is not already initialized + + if ($value instanceof arangodb) { + // Recieved an appropriate value + + // Write the property and exit (success) + self::$arangodb = $value; + } else { + // Recieved an inappropriate value + + // Exit (fail) + throw new exception('Session of ArangoDB ($this::$arangodb) is need to be mirzaev\arangodb\connection', 500); + } + } + })(), + default => parent::__set($name, $value) + }; + } + + /** + * Read + * + * @param string $name Name of the property + * + * @return mixed Content of the property, if they are found + */ + public function __get(string $name): mixed + { + return match ($name) { + 'arangodb' => (function () { + try { + if (!$this->__isset('arangodb')) { + // Is not initialized + + // Initializing of a default value from settings + $this->__set('arangodb', new arangodb(require static::ARANGODB)); + } + + // Exit (success) + return self::$arangodb; + } catch (exception) { + // Exit (fail) + return null; + } + })(), + default => parent::__get($name) + }; + } + + /** + * Delete + * + * @param string $name Name of the property + * + * @return void + */ + public function __unset(string $name): void + { + // Deleting a property and exit (success) + parent::__unset($name); + } + + /** + * Check of initialization + * + * @param string $name Name of the property + * + * @return bool The property is initialized? + */ + public function __isset(string $name): bool + { + // Check of initialization of the property and exit (success) + return parent::__isset($name); + } + + /** + * Call a static property or method + * + * @param string $name Name of the property or the method + * @param array $arguments Arguments for the method + */ + public static function __callStatic(string $name, array $arguments): mixed + { + match ($name) { + 'arangodb' => (new static)->__get('arangodb'), + default => throw new exception("Not found: $name", 500) + }; + } +} + diff --git a/${REPO_OWNER}/${REPO_NAME}/system/models/session.php b/${REPO_OWNER}/${REPO_NAME}/system/models/session.php new file mode 100644 index 0000000..4665b59 --- /dev/null +++ b/${REPO_OWNER}/${REPO_NAME}/system/models/session.php @@ -0,0 +1,276 @@ + + */ +final class session extends core +{ + /** + * Name of the collection in ArangoDB + */ + final public const COLLECTION = 'session'; + + /** + * An instance of the ArangoDB document from ArangoDB + */ + protected readonly _document $document; + + /** + * Constructor of an instance + * + * Initialize of a session and write them to the $this->document property + * + * @param ?string $hash Hash of the session in ArangoDB + * @param ?int $expires Date of expiring of the session (used for creating a new session) + * @param array &$errors Registry of errors + * + * @return static instance of the ArangoDB document of session + */ + public function __construct(?string $hash = null, ?int $expires = null, array &$errors = []) + { + try { + if (collection::init(static::$arangodb->session, self::COLLECTION)) { + // Initialized the collection + + if ($this->search($hash, $errors)) { + // Found an instance of the ArangoDB document of session and received a session hash + } else { + // Not found an instance of the ArangoDB document of session + + // Initializing a new session and write they into ArangoDB + $_id = document::write($this::$arangodb->session, self::COLLECTION, [ + 'active' => true, + 'expires' => $expires ?? time() + 604800, + 'ip' => $_SERVER['REMOTE_ADDR'], + 'x-forwarded-for' => $_SERVER['HTTP_X_FORWARDED_FOR'] ?? null, + 'referer' => $_SERVER['HTTP_REFERER'] ?? null, + 'useragent' => $_SERVER['HTTP_USER_AGENT'] ?? null + ]); + + if ($session = collection::search($this::$arangodb->session, sprintf( + << %d && d.active == true + RETURN d + AQL, + self::COLLECTION, + $_id, + time() + ))) { + // Found an instance of just created new session + + // Generate a hash and write into an instance of the ArangoDB document of session property + $session->hash = sodium_bin2hex(sodium_crypto_generichash($_id)); + + if (document::update($this::$arangodb->session, $session)) { + // Is writed update + + // Write instance of the ArangoDB document of session into property and exit (success) + $this->document = $session; + } else throw new exception('Could not write the session data'); + } else throw new exception('Could not create or find just created session'); + } + } else throw new exception('Could not initialize the collection'); + } catch (exception $e) { + // Write to the registry of errors + $errors[] = [ + 'text' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'stack' => $e->getTrace() + ]; + } + } + + /** + * Search + * + * Search for the session in ArangoDB by hash and write they into $this->document property if they are found + * + * @param ?string $hash Hash of the session in ArangoDB + * @param array &$errors Registry of errors + * + * @return static instance of the ArangoDB document of session + */ + public function search(?string $hash, array &$errors = []): bool + { + try { + if (isset($hash)) { + // Recieved a hash + + // Search the session data in ArangoDB + $_document = $session = collection::search($this::$arangodb->session, sprintf( + << %d && d.active == true + RETURN d + AQL, + self::COLLECTION, + $hash, + time() + )); + + if ($_document instanceof _document) { + // An instance of the ArangoDB document of session is found + + // Write the session data to the property + $this->document = $_document; + + // Exit (success) + return true; + } + } + } catch (exception $e) { + // Write to the registry of errors + $errors[] = [ + 'text' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'stack' => $e->getTrace() + ]; + } + + // Exit (fail) + return false; + } + + /** + * Write to buffer of the session + * + * @param array $data Data for merging + * @param array &$errors Registry of errors + * + * @return bool Is data has written into the session buffer? + */ + public function write(array $data, array &$errors = []): bool + { + try { + if (collection::init($this::$arangodb->session, self::COLLECTION)) { + // Initialized the collection + + // An instance of the ArangoDB document of session is initialized? + if (!isset($this->document)) throw new exception('An instance of the ArangoDB document of session is not initialized'); + + // Write data into buffwer of an instance of the ArangoDB document of session + $this->document->buffer = array_replace_recursive( + $this->document->buffer ?? [], + [$_SERVER['INTERFACE'] => array_replace_recursive($this->document->buffer[$_SERVER['INTERFACE']] ?? [], $data)] + ); + + // Write to ArangoDB and exit (success) + return document::update($this::$arangodb->session, $this->document) ? true : throw new exception('Не удалось записать данные в буфер сессии'); + } else throw new exception('Could not initialize the collection'); + } catch (exception $e) { + // Write to the registry of errors + $errors[] = [ + 'text' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'stack' => $e->getTrace() + ]; + } + + return false; + } + + /** + * Write + * + * Write a property into an instance of the ArangoDB document + * + * @param string $name Name of the property + * @param mixed $value Content of the property + * + * @return void + */ + public function __set(string $name, mixed $value = null): void + { + // Write to the property into an instance of the ArangoDB document and exit (success) + $this->document->{$name} = $value; + } + + /** + * Read + * + * Read a property from an instance of the ArangoDB docuemnt + * + * @param string $name Name of the property + * + * @return mixed Content of the property + */ + public function __get(string $name): mixed + { + // Read a property from an instance of the ArangoDB document and exit (success) + return match ($name) { + 'arangodb' => $this::$arangodb, + default => $this->document->{$name} + }; + } + + /** + * Delete + * + * Deinitialize the property in an instance of the ArangoDB document + * + * @param string $name Name of the property + * + * @return void + */ + public function __unset(string $name): void + { + // Delete the property in an instance of the ArangoDB document and exit (success) + unset($this->document->{$name}); + } + + /** + * Check of initialization + * + * Check of initialization of the property into an instance of the ArangoDB document + * + * @param string $name Name of the property + * + * @return bool The property is initialized? + */ + public function __isset(string $name): bool + { + // Check of initializatio nof the property and exit (success) + return isset($this->document->{$name}); + } + + /** + * Execute a method + * + * Execute a method from an instance of the ArangoDB document + * + * @param string $name Name of the method + * @param array $arguments Arguments for the method + * + * @return mixed Result of execution of the method + */ + public function __call(string $name, array $arguments = []): mixed + { + // Execute the method and exit (success) + if (method_exists($this->document, $name)) return $this->document->{$name}($arguments); + } +} diff --git a/${REPO_OWNER}/${REPO_NAME}/system/models/traits/status.php b/${REPO_OWNER}/${REPO_NAME}/system/models/traits/status.php new file mode 100644 index 0000000..00fcca4 --- /dev/null +++ b/${REPO_OWNER}/${REPO_NAME}/system/models/traits/status.php @@ -0,0 +1,45 @@ + + */ +trait status +{ + /** + * Initialize of a status + * + * @param array &$errors Registry of errors + * + * @return ?bool Status, if they are found + */ + public function status(array &$errors = []): ?bool + { + try { + // Read from ArangoDB and exit (success) + return $this->document->active ?? false; + } catch (exception $e) { + // Write to the registry of errors + $errors[] = [ + 'text' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'stack' => $e->getTrace() + ]; + } + + // Exit (fail) + return null; + } +} + diff --git a/${REPO_OWNER}/${REPO_NAME}/system/public/css/themes/default/main.css b/${REPO_OWNER}/${REPO_NAME}/system/public/css/themes/default/main.css new file mode 100644 index 0000000..ff80335 --- /dev/null +++ b/${REPO_OWNER}/${REPO_NAME}/system/public/css/themes/default/main.css @@ -0,0 +1,47 @@ +@charset "UTF-8"; + +* { + text-decoration: none; + outline: none; + border: none; + /* font-family: , system-ui, sans-serif; */ + font-family: "dejavu"; + transition: 0.1s ease-out; +} + +body { + margin: 0; + min-height: 100vh; + padding: 0; + display: flex; + flex-direction: column; + background-color: var(--background, #fafafa); +} + + +aside { +} + +header { +} + +main { + display: flex; + flex-direction: column; + align-items: center; + gap: 10px; + transition: 0s; +} + +footer { +} + +.unselectable { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + diff --git a/${REPO_OWNER}/${REPO_NAME}/system/public/index.php b/${REPO_OWNER}/${REPO_NAME}/system/public/index.php new file mode 100644 index 0000000..65975c6 --- /dev/null +++ b/${REPO_OWNER}/${REPO_NAME}/system/public/index.php @@ -0,0 +1,32 @@ +write('/', 'index', 'index'); + +// Инициализация ядра +$core = new core(namespace: __NAMESPACE__, router: $router); + +// Обработка запроса +echo $core->start(); + diff --git a/${REPO_OWNER}/${REPO_NAME}/system/settings/.gitignore b/${REPO_OWNER}/${REPO_NAME}/system/settings/.gitignore new file mode 100644 index 0000000..2027072 --- /dev/null +++ b/${REPO_OWNER}/${REPO_NAME}/system/settings/.gitignore @@ -0,0 +1,3 @@ +* +!.gitignore +!*.sample diff --git a/${REPO_OWNER}/${REPO_NAME}/system/settings/arangodb.php.sample b/${REPO_OWNER}/${REPO_NAME}/system/settings/arangodb.php.sample new file mode 100644 index 0000000..2839054 --- /dev/null +++ b/${REPO_OWNER}/${REPO_NAME}/system/settings/arangodb.php.sample @@ -0,0 +1,8 @@ + 'unix:///var/run/arangodb3/arango.sock', + 'database' => '{$REPO_NAME}', + 'name' => '{$REPO_NAME}', + 'password' => '' +]; diff --git a/${REPO_OWNER}/${REPO_NAME}/system/views/templater.php b/${REPO_OWNER}/${REPO_NAME}/system/views/templater.php new file mode 100644 index 0000000..5436e9a --- /dev/null +++ b/${REPO_OWNER}/${REPO_NAME}/system/views/templater.php @@ -0,0 +1,202 @@ + + */ +final class templater extends controller implements ArrayAccess +{ + /** + * Registry of global variables of view + */ + public array $variables = []; + + /** + * Instance of twig templater + */ + readonly public twig $twig; + + /** + * Constructor of an instance + * + * @param ?session $session Instance of the session of ArangoDB + * + * @return void + */ + public function __construct(?session &$session = null): void + { + // Initializing of an instance of twig + $this->twig = new twig(new FilesystemLoader(VIEWS)); + + // Initializing of global variables + $this->twig->addGlobal('theme', 'default'); + $this->twig->addGlobal('server', $_SERVER); + $this->twig->addGlobal('cookies', $_COOKIE); + if (!empty($session->status())) { + $this->twig->addGlobal('session', $session); + } + + // Initializing of twig extensions + $this->twig->addExtension(new intl()); + } + + /** + * Render a HTML-document + * + * @param string $file Related path to a HTML-document + * @param ?array $variables Registry of variables to push into registry of global variables + * + * @return ?string HTML-документ + */ + public function render(string $file, ?array $variables = null): ?string + { + // Generation and exit (success) + return $this->twig->render('themes' . DIRECTORY_SEPARATOR . $this->twig->getGlobal('theme') . DIRECTORY_SEPARATOR . $file, $variables + $this->variables); + } + + /** + * Write + * + * Write a variable into registry of global variables + * + * @param string $name Name of the variable + * @param mixed $value Value of the variable + * + * @return void + */ + public function __set(string $name, mixed $value = null): void + { + // Write the variable and exit (success) + $this->variables[$name] = $value; + } + + /** + * Read + * + * Read a variable from registry of global variables + * + * @param string $name Name of the variable + * + * @return mixed Content of the variable, if they are found + */ + public function __get(string $name): mixed + { + // Read the variable and exit (success) + return $this->variables[$name]; + } + + /** + * Delete + * + * Delete a variable from the registry of global variables + * + * @param string $name Name of the variable + * + * @return void + */ + public function __unset(string $name): void + { + // Delete the variable and exit (success) + unset($this->variables[$name]); + } + + /** + * Check of initialization + * + * Check of initialization in registry of global variables + * + * @param string $name Name of the variable + * + * @return bool The variable is initialized? + */ + public function __isset(string $name): bool + { + // Check of initialization of the variable and exit (success) + return isset($this->variables[$name]); + } + + /** + * Write + * + * Write a variable into registry of global variables + * + * @param mixed $name Name of an offset of the variable + * @param mixed $value Value of the variable + * + * @return void + */ + public function offsetSet(mixed $name, mixed $value): void + { + // Write the variable and exit (success) + $this->variables[$name] = $value; + } + + /** + * Read + * + * Read a variable from registry of global variables + * + * @param mixed $name Name of the variable + * + * @return mixed Content of the variable, if they are found + */ + public function offsetGet(mixed $name): mixed + { + // Read the variable and exit (success) + return $this->variables[$name]; + } + + /** + * Delete + * + * Delete a variable from the registry of global variables + * + * @param mixed $name Name of the variable + * + * @return void + */ + public function offsetUnset(mixed $name): void + { + // Delete the variable and exit (success) + unset($this->variables[$name]); + } + + /** + * Check of initialization + * + * Check of initialization in registry of global variables + * + * @param mixed $name Name of the variable + * + * @return bool The variable is initialized? + */ + public function offsetExists(mixed $name): bool + { + // Check of initialization of the variable and exit (success) + return isset($this->variables[$name]); + } +} + diff --git a/${REPO_OWNER}/${REPO_NAME}/system/views/themes/default/core.html b/${REPO_OWNER}/${REPO_NAME}/system/views/themes/default/core.html new file mode 100644 index 0000000..fd92488 --- /dev/null +++ b/${REPO_OWNER}/${REPO_NAME}/system/views/themes/default/core.html @@ -0,0 +1,31 @@ + + + + + + {% use 'head.html' with title as head_title, meta as head_meta, css as head_css %} + + {% block title %} + {{ block('head_title') }} + {% endblock %} + + {% block meta %} + {{ block('head_meta') }} + {% endblock %} + + {{ block('head_css') }} + {% block css %} + {% endblock %} + + + + {% block body %} + {% endblock %} + + {% include 'js.html' %} + {% block js %} + {% endblock %} + + + + diff --git a/${REPO_OWNER}/${REPO_NAME}/system/views/themes/default/footer.html b/${REPO_OWNER}/${REPO_NAME}/system/views/themes/default/footer.html new file mode 100644 index 0000000..8322f33 --- /dev/null +++ b/${REPO_OWNER}/${REPO_NAME}/system/views/themes/default/footer.html @@ -0,0 +1,10 @@ +{% block css %} +{% endblock %} + +{% block body %} +
+
+{% endblock %} + +{% block js %} +{% endblock %} diff --git a/${REPO_OWNER}/${REPO_NAME}/system/views/themes/default/head.html b/${REPO_OWNER}/${REPO_NAME}/system/views/themes/default/head.html new file mode 100644 index 0000000..24ce242 --- /dev/null +++ b/${REPO_OWNER}/${REPO_NAME}/system/views/themes/default/head.html @@ -0,0 +1,15 @@ +{% block title %} +{% if head.title != empty %}{{head.title}}{% else %}${REPO_NAME} by ${REPO_OWNER}{% endif %} +{% endblock %} + +{% block meta %} + + +{% for meta in head.metas %} + +{% endfor %} +{% endblock %} + +{% block css %} + +{% endblock %} diff --git a/${REPO_OWNER}/${REPO_NAME}/system/views/themes/default/header.html b/${REPO_OWNER}/${REPO_NAME}/system/views/themes/default/header.html new file mode 100644 index 0000000..430650a --- /dev/null +++ b/${REPO_OWNER}/${REPO_NAME}/system/views/themes/default/header.html @@ -0,0 +1,10 @@ +{% block css %} +{% endblock %} + +{% block body %} +
+
+{% endblock %} + +{% block js %} +{% endblock %} diff --git a/${REPO_OWNER}/${REPO_NAME}/system/views/themes/default/index.html b/${REPO_OWNER}/${REPO_NAME}/system/views/themes/default/index.html new file mode 100644 index 0000000..85ad9c7 --- /dev/null +++ b/${REPO_OWNER}/${REPO_NAME}/system/views/themes/default/index.html @@ -0,0 +1,27 @@ +{% extends "core.html" %} + +{% use "core.html" with css as core_css, body as core, js as core_js %} +{% use "header.html" with css as header_css, body as header, js as header_js %} +{% use "footer.html" with css as footer_css, body as footer, js as footer_js %} + +{% block css %} +{{ block('core_css') }} +{{ block('header_css') }} +{{ block('footer_css') }} +{% endblock %} + +{% block body %} +{{ block('header') }} +
+ {% block main %} + {{ main|raw }} + {% endblock %} +
+{{ block('footer') }} +{% endblock %} + +{% block js %} +{{ block('footer_js') }} +{{ block('header_js') }} +{{ block('core_js') }} +{% endblock %} diff --git a/${REPO_OWNER}/${REPO_NAME}/system/views/themes/default/js.html b/${REPO_OWNER}/${REPO_NAME}/system/views/themes/default/js.html new file mode 100644 index 0000000..47c8fd2 --- /dev/null +++ b/${REPO_OWNER}/${REPO_NAME}/system/views/themes/default/js.html @@ -0,0 +1,2 @@ +{% block js %} +{% endblock %} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c26c9a5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +!.gitignore +composer.phar +composer.lock +vendor diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..e007a57 --- /dev/null +++ b/LICENSE @@ -0,0 +1,11 @@ +DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +Version 2, December 2004 + +Copyright (C) 2004 Sam Hocevar + +Everyone is permitted to copy and distribute verbatim or modified copies of this license document, and changing it is allowed as long as the name is changed. + +DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. diff --git a/README.md b/README.md new file mode 100755 index 0000000..bf7c94a --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# ${REPO_NAME} +${REPO_DESCRIPTION} diff --git a/composer.json b/composer.json new file mode 100755 index 0000000..d306cac --- /dev/null +++ b/composer.json @@ -0,0 +1,43 @@ +{ + "name": "${REPO_OWNER}/${REPO_NAME}", + "description": "${REPO_DESCRIPTION}", + "readme": "README.md", + "keywords": [], + "type": "", + "homepage": "{$REPO_SSH_URL}", + "license": "WTFPL", + "authors": [ + { + "name": "${REPO_OWNER}", + "email": "", + "homepage": "", + "role": "Programmer" + } + ], + "support": { + "docs": "{$REPO_SSH_URL}/manual", + "issues": "{$REPO_SSH_URL}/issues" + }, + "require": { + "php": "~8.3", + "ext-sodium": "~8.3", + "mirzaev/minimal": "^2.2.0", + "mirzaev/accounts": "~1.2.x-dev", + "mirzaev/arangodb": "^1.0.0", + "triagens/arangodb": "~3.9.x-dev", + "twig/twig": "^3.4" + }, + "require-dev": { + "phpunit/phpunit": "~9.5" + }, + "autoload": { + "psr-4": { + "{$REPO_OWNER}\\{$REPO_NAME}\\": "{$REPO_OWNER}/{$REPO_NAME}/system" + } + }, + "autoload-dev": { + "psr-4": { + "{$REPO_OWNER}\\{$REPO_NAME}\\tests\\": "{$REPO_OWNER}/{$REPO_NAME}/tests" + } + } +}