Merge branch 'master' into dev

This commit is contained in:
Roman 2021-04-06 16:33:46 +02:00
commit dac3dc2331
92 changed files with 44709 additions and 12412 deletions

258
README.md

@ -11,9 +11,261 @@ Web-Base is a php framework which provides basic web functionalities and a moder
- REST API - REST API
- Account management - Account management
- Supporting MySQL + PostgreSQL - Supporting MySQL + PostgreSQL
- New: Page Routing - Dynamic Page Routing
- New: Admin Dashboard - Admin Dashboard
- New: Account & User functions - Account & User functions
- File Sharing Dashboard
### Upcoming: ### Upcoming:
I actually don't know what i want to implement here. There are quite to many CMS out there with alot of vulnerabilities. There also exist some frameworks already. This project is meant to provide a stable project base to implement what ever a developer wants to: Dynamic Homepages, Webshops, .. I actually don't know what i want to implement here. There are quite to many CMS out there with alot of vulnerabilities. There also exist some frameworks already. This project is meant to provide a stable project base to implement what ever a developer wants to: Dynamic Homepages, Webshops, ..
## Installation
1. `git clone https://git.romanh.de/Projekte/web-base` (or `https://github.com/rhergenreder/web-base`)
2. Create a [mysql](https://dev.mysql.com/doc/refman/5.7/en/creating-database.html) or [postgresql](https://www.postgresql.org/docs/9.0/sql-createdatabase.html) database
or use an existing empty database (e.g. test or public)
3. Open the webapp in your browser and follow the installation guide
For any changes made in [/adminPanel](/adminPanel) or [/fileControlPanel](/fileControlPanel), run:
1. once: `npm i`
2. build: `npm run build`
The compiled dist files will be automatically moved to `/js`.
## Extending the Base
### Adding API-Endpoints
Each API endpoint has usually one overlying category, for example all user and authorization endpoints belong to the [UserAPI](/core/Api/UserAPI.class.php).
These endpoints can be accessed by requesting URLs starting with `/api/user`, for example: `/api/user/login`. There are also endpoints, which don't have
a category, e.g. [PatchSQL](/core/Api/PatchSQL.class.php). These functions can be called directly, for example with `/api/patchSQL`. Both methods have one thing in common:
Each endpoint is represented by a class inheriting the [Request Class](/core/Api/Request.class.php). An example endpoint looks like this:
```php
namespace Api;
use Api\Parameter\Parameter;
use Objects\User;
class SingleEndpoint extends Request {
public function __construct(User $user, bool $externalCall = false) {
parent::__construct($user, $externalCall, array(
"someParameter" => new Parameter("someParameter", Parameter::TYPE_INT, true, 100)
));
$this->forbidMethod("POST");
}
public function execute($values = array()): bool {
if (!parent::execute($values)) {
return false;
}
$this->result['someAttribute'] = $this->getParam("someParameter") * 2;
return true;
}
}
```
An endpoint consists of two important functions:
1. the constructor defining the expected parameters as well as some restrictions and endpoint configuration.
2. the execute function, checking all requirements by calling the parent, and then executing the own method.
To create an API category containing multiple endpoints, a parent class inheriting from `Request`, e.g. `class MultipleAPI extends Request` is required.
All endpoints inside this category then inherit from the `MultipleAPI` class.
The classes must be present inside the [Api](/core/Api) directory according to the other endpoints.
### Access Control
By default, and if not further specified or restricted, all endpoints have the following access rules:
1. Allowed methods: GET and POST (`$this->allowedMethods`)
2. No login is required (`$this->loginRequired`)
3. CSRF-Token is required, if the user is logged in (`$this->csrfTokenRequired`)
4. The function can be called from outside (`$this->isPublic`)
5. An API-Key can be used to access this method (`$this->apiKeyAllowed`)
6. All user groups can access the method (Database, Table: `ApiPermission`)
The first five restrictions can be modified inside the constructor, while the group permissions are changed using
the [Admin Dashboard](/adminPanel). It's default values are set inside the [database script](/core/Configuration/CreateDatabase.class.php).
### Using the API internally
Some endpoints are set to private, which means, they can be only accessed inside the backend. These functions, as well as the public ones,
can be used by creating the desired request object, and calling the execute function with our parameters like shown below:
```php
$req = new \Api\Mail\Send($user);
$success = $req->execute(array(
"to" => "mail@example.org",
"subject" => "Example Mail",
"body" => "This is an example mail"
));
if (!$success) {
echo $req->getLastError();
}
```
The user object is usually obtained from the api (`$this->user`) or from the frontend document (`$document->getUser()`).
If any result is expected from the api call, the `$req->getResult()` method can be used, which returns an array of all field.
### Modifying the database
This step is not really required, as and changes made to the database must not be presented inside the code.
On the other hand, it is recommended to keep track of any modifications for later use or to deploy the application
to other systems. Therefore, either the [default installation script](/core/Configuration/CreateDatabase.class.php) or
an additional patch file, which can be executed using the API (`/api/PatchSQL`), can be created. The patch files are usually
located in [/core/Configuration/Patch](/core/Configuration/Patch) and have the following structure:
```php
namespace Configuration\Patch;
use Configuration\DatabaseScript;
use Driver\SQL\SQL;
class example_patch extends DatabaseScript {
public static function createQueries(SQL $sql): array {
$queries = [];
$queries[] = $sql->createTable("ExampleTable")
->addSerial("exampleCol")
->addString("someString", 32)
->primaryKey("exampleCol");
return $queries;
}
}
```
### Routing
To access and view any frontend pages, the internal router is used. Available routes can be customized on the admin dashboard. There are four types of routes:
1. Permanent redirect (http status code: 308)
2. Temporary redirect (http status code: 307)
3. Static Route
4. Dynamic Content
A static route targets a file, usually located in [/static](/static) and does nothing more, than returning its content. A dynamic route is usually the way to go:
It takes two parameters, firstly the target document and secondly, an optional view. For example, take the following routing table:
| Route | Action | Target | Extra |
| ----- | ------ | ------ | ----- |
| `/funnyCatImage` | `Serve Static` | `/static/cat.jpg` | |
| `/someRoute(/(.*))?` | `Redirect Dynamic` | `\Documents\MyDocument\` | `$2` |
The first route would return the cat image, if the case-insensitive path `/funnyCatImage` is requested.
The second route is more interesting, as it firstly contains regex, which means, any route starting with `/someRoute/` or just `/someRoute` is accepted.
Secondly, it passes the second group (`$2`), which is all the text after the last slash (or `null`) to the dynamically loaded document `MyDocument`.
### Creating and Modifying documents
A frontend page consists of a document, which again consists of a head and a body. Furthermore, a document can have various views, which have to be implemented
programmatically. Usually, all pages inside a document look somehow similar, for example share a common side- or navbar, a header or a footer. If we think of a web-shop,
we could have one document, when showing different articles and products, and a view for various pages, e.g. the dashboard with all the products, a single product view and so on.
To create a new document, a class inside [/core/Documents](/core/Documents) is created with the following scheme:
```php
namespace Documents {
use Elements\Document;
use Objects\User;
use Documents\Example\ExampleHead;
use Documents\Example\ExampleBody;
class ExampleDocument extends Document {
public function __construct(User $user, ?string $view = NULL) {
parent::__construct($user, ExampleHead::class, ExampleBody::class, $view);
}
}
}
namespace Documents\Example {
use Elements\Head;
use Elements\Body;
class ExampleHead extends Head {
public function __construct($document) {
parent::__construct($document);
}
protected function initSources() {
$this->loadJQuery();
$this->loadBootstrap();
$this->loadFontawesome();
}
protected function initMetas() : array {
return array(
array('charset' => 'utf-8'),
);
}
protected function initRawFields() : array {
return array();
}
protected function initTitle() : string {
return "Example Document";
}
}
class ExampleBody extends Body {
public function __construct($document) {
parent::__construct($document);
}
public function getCode(): string {
$view = $this->getDocument()->getRequestedView() ?? "<Empty>";
return "<b>Requested View:</b> " . htmlspecialchars($view);
}
}
}
```
Of course, the head and body classes can be placed in any file, as the code might get big and complex.
### Localization
Currently, there are two languages specified, which are stored in the database: `en_US` and `de_DE`.
A language is dynamically loaded according to the sent `Accept-Language`-Header, but can also be set using the `lang` parameter
or [/api/language/set](/core/Api/LanguageAPI.class.php) endpoint. Localization of strings can be achieved using the [LanguageModule](/core/Objects/lang/LanguageModule.php)-Class.
Let's look at this example:
```php
class ExampleLangModule extends \Objects\lang\LanguageModule {
public function getEntries(string $langCode) {
$entries = array();
switch ($langCode) {
case 'de_DE':
$entries["EXAMPLE_KEY"] = "Das ist eine Beispielübersetzung";
$entries["Welcome"] = "Willkommen";
break;
default:
$entries["EXAMPLE_KEY"] = "This is an example translation";
break;
}
return $entries;
}
}
```
If any translation key is not defined, the key is returned, which means, we don't need to specify the string `Welcome` again. To access the translations,
we firstly have to load the module. This is done by adding the class, or the object inside the constructor.
To translate the defined strings, we can use the global `L()` function. The following code snipped shows the use of
our sample language module:
```php
class SomeView extends \Elements\View {
public function __construct(\Elements\Document $document) {
parent::__construct($document);
$this->langModules[] = ExampleModule::class;
}
public function getCode() : string{
return L("Welcome") . "! " . L("EXAMPLE_KEY");
}
}
```
## Anything more?
Feel free to contact me regarding this project and any other questions.

1
adminPanel/.htaccess Normal file

@ -0,0 +1 @@
DENY FROM ALL

File diff suppressed because it is too large Load Diff

@ -16,13 +16,14 @@
"react-dom": "^16.13.1", "react-dom": "^16.13.1",
"react-draft-wysiwyg": "^1.14.5", "react-draft-wysiwyg": "^1.14.5",
"react-router-dom": "^5.2.0", "react-router-dom": "^5.2.0",
"react-scripts": "^3.4.1", "react-scripts": "^4.0.3",
"react-select": "^3.1.0", "react-select": "^3.1.0",
"react-tooltip": "^4.2.7", "react-tooltip": "^4.2.7",
"sanitize-html": "^1.27.0" "sanitize-html": "^1.27.0"
}, },
"scripts": { "scripts": {
"build": "webpack --mode production && mv dist/main.js ../js/admin.min.js" "build": "webpack --mode production && mv dist/main.js ../js/admin.min.js",
"debug": "react-scripts start"
}, },
"eslintConfig": { "eslintConfig": {
"extends": "react-app" "extends": "react-app"

@ -0,0 +1,9 @@
<html>
<head>
<link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/fontawesome.min.css">
</head>
<body>
<div id="root"></div>
</body>
</html>

@ -5,7 +5,7 @@ export default function Footer() {
return ( return (
<footer className={"main-footer"}> <footer className={"main-footer"}>
Theme: <strong>Copyright © 2014-2019 <a href={"http://adminlte.io"}>AdminLTE.io</a>. <b>Version</b> 3.0.3</strong>&nbsp; Theme: <strong>Copyright © 2014-2019 <a href={"http://adminlte.io"}>AdminLTE.io</a>. <b>Version</b> 3.0.3</strong>&nbsp;
CMS: <strong><a href={"https://git.romanh.de/Projekte/web-base"}>WebBase</a></strong>. <b>Version</b> 1.1.0 CMS: <strong><a href={"https://git.romanh.de/Projekte/web-base"}>WebBase</a></strong>. <b>Version</b> 1.2.0
</footer> </footer>
) )
} }

@ -75,7 +75,7 @@ export default function Sidebar(props) {
let filePath = parent.filesPath; let filePath = parent.filesPath;
if (filePath) { if (filePath) {
li.push(<li className={"nav-item"} key={"files"}> li.push(<li className={"nav-item"} key={"files"}>
<a href={filePath} className={"nav-link"} target={"_blank"} rel={"noopener noreferrer"}> <a href={filePath} className={"nav-link"} target={"_blank"} rel={"noopener"}>
<Icon icon={"folder"} className={"nav-icon"} /> <Icon icon={"folder"} className={"nav-icon"} />
<p>Files</p> <p>Files</p>
</a> </a>

@ -121,7 +121,13 @@ export default function HelpPage() {
<b>Project Lead & Main Developer</b> <b>Project Lead & Main Developer</b>
<ul className={"list-unstyled"}> <ul className={"list-unstyled"}>
<li><small><Icon icon={"address-card"} className={"mr-1"}/>Roman Hergenreder</small></li> <li><small><Icon icon={"address-card"} className={"mr-1"}/>Roman Hergenreder</small></li>
<li><small><Icon icon={"globe"} className={"mr-1"}/><a href={"https://romanh.de/"} target={"_blank"}>https://romanh.de/</a></small></li> <li>
<small><Icon icon={"globe"} className={"mr-1"}/>
<a href={"https://romanh.de/"} target={"_blank"} rel={"noopener"}>
https://romanh.de/
</a>
</small>
</li>
<li><small><Icon icon={"envelope"} className={"mr-1"}/><a href={"mailto:webmaster@romanh.de"}>webmaster@romanh.de</a></small></li> <li><small><Icon icon={"envelope"} className={"mr-1"}/><a href={"mailto:webmaster@romanh.de"}>webmaster@romanh.de</a></small></li>
</ul> </ul>

@ -422,7 +422,7 @@ export default class Settings extends React.Component {
<label className={"form-check-label"} htmlFor={"recaptcha_enabled"}> <label className={"form-check-label"} htmlFor={"recaptcha_enabled"}>
Enable Google's reCaptcha Enable Google's reCaptcha
<span className={"ml-2"}> <span className={"ml-2"}>
(<a href={"https://www.google.com/recaptcha/intro/v3.html"} target={"_blank"}> (<a href={"https://www.google.com/recaptcha/intro/v3.html"} target={"_blank"} rel={"noopener noreferrer"}>
More Info More Info
<sup><small><Icon icon={"external-link-alt"} className={"ml-1"}/></small></sup> <sup><small><Icon icon={"external-link-alt"} className={"ml-1"}/></small></sup>
</a>) </a>)

@ -44,7 +44,7 @@ namespace Api\ApiKey {
$this->loginRequired = true; $this->loginRequired = true;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if(!parent::execute($values)) { if(!parent::execute($values)) {
return false; return false;
@ -81,7 +81,7 @@ namespace Api\ApiKey {
$this->loginRequired = true; $this->loginRequired = true;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if(!parent::execute($values)) { if(!parent::execute($values)) {
return false; return false;
} }
@ -127,7 +127,7 @@ namespace Api\ApiKey {
$this->loginRequired = true; $this->loginRequired = true;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if(!parent::execute($values)) { if(!parent::execute($values)) {
return false; return false;
} }
@ -162,7 +162,7 @@ namespace Api\ApiKey {
$this->loginRequired = true; $this->loginRequired = true;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if(!parent::execute($values)) { if(!parent::execute($values)) {
return false; return false;
} }

@ -34,7 +34,7 @@ namespace Api\Contact {
parent::__construct($user, $externalCall, $parameters); parent::__construct($user, $externalCall, $parameters);
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }

@ -238,7 +238,7 @@ namespace Api\File {
$this->csrfTokenRequired = false; $this->csrfTokenRequired = false;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }
@ -292,7 +292,7 @@ namespace Api\File {
$this->loginRequired = true; $this->loginRequired = true;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }
@ -316,7 +316,7 @@ namespace Api\File {
$this->csrfTokenRequired = true; $this->csrfTokenRequired = true;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }
@ -360,7 +360,7 @@ namespace Api\File {
$this->csrfTokenRequired = false; $this->csrfTokenRequired = false;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }
@ -407,7 +407,7 @@ namespace Api\File {
$this->csrfTokenRequired = false; $this->csrfTokenRequired = false;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }
@ -450,7 +450,7 @@ namespace Api\File {
$this->loginRequired = true; $this->loginRequired = true;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }
@ -482,7 +482,7 @@ namespace Api\File {
)); ));
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }
@ -551,7 +551,7 @@ namespace Api\File {
$this->loginRequired = true; $this->loginRequired = true;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }
@ -653,7 +653,7 @@ namespace Api\File {
$this->csrfTokenRequired = false; $this->csrfTokenRequired = false;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }
@ -826,7 +826,7 @@ namespace Api\File {
$this->csrfTokenRequired = false; $this->csrfTokenRequired = false;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }
@ -891,7 +891,7 @@ namespace Api\File {
$this->csrfTokenRequired = true; $this->csrfTokenRequired = true;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }
@ -946,7 +946,7 @@ namespace Api\File {
$this->csrfTokenRequired = false; $this->csrfTokenRequired = false;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }
@ -1017,7 +1017,7 @@ namespace Api\File {
$this->csrfTokenRequired = false; $this->csrfTokenRequired = false;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }

@ -54,7 +54,7 @@ namespace Api\Groups {
return $this->success; return $this->success;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if(!parent::execute($values)) { if(!parent::execute($values)) {
return false; return false;
} }
@ -116,7 +116,7 @@ namespace Api\Groups {
)); ));
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }
@ -162,7 +162,7 @@ namespace Api\Groups {
)); ));
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }

@ -23,7 +23,7 @@ namespace Api\Language {
parent::__construct($user, $externalCall, array()); parent::__construct($user, $externalCall, array());
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if(!parent::execute($values)) { if(!parent::execute($values)) {
return false; return false;
} }
@ -108,7 +108,7 @@ namespace Api\Language {
return $this->success; return $this->success;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if(!parent::execute($values)) { if(!parent::execute($values)) {
return false; return false;
} }

@ -24,7 +24,7 @@ namespace Api\Mail {
)); ));
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }
@ -77,7 +77,7 @@ namespace Api\Mail {
return null; return null;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if(!parent::execute($values)) { if(!parent::execute($values)) {
return false; return false;
} }

@ -107,7 +107,7 @@ namespace Api\Notifications {
return $this->success; return $this->success;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if(!parent::execute($values)) { if(!parent::execute($values)) {
return false; return false;
} }
@ -213,7 +213,7 @@ namespace Api\Notifications {
return $this->success; return $this->success;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if(!parent::execute($values)) { if(!parent::execute($values)) {
return false; return false;
} }
@ -235,7 +235,7 @@ namespace Api\Notifications {
$this->loginRequired = true; $this->loginRequired = true;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }

@ -8,14 +8,22 @@ class ArrayType extends Parameter {
public int $elementType; public int $elementType;
public int $canBeOne; public int $canBeOne;
public function __construct($name, $elementType = Parameter::TYPE_MIXED, $canBeOne=false, $optional = FALSE, $defaultValue = NULL) { /**
* ArrayType constructor.
* @param string $name the name of the parameter
* @param int $elementType element type inside the array, for example, allow only integer values (Parameter::TYPE_INT)
* @param bool $canBeOne true, if a single element can be passed inside the request (e.g. array=1 instead of array[]=1). Will be automatically casted to an array
* @param bool $optional true if the parameter is optional
* @param array|null $defaultValue the default value to use, if the parameter is not given
*/
public function __construct(string $name, int $elementType = Parameter::TYPE_MIXED, bool $canBeOne = false, bool $optional = FALSE, ?array $defaultValue = NULL) {
$this->elementType = $elementType; $this->elementType = $elementType;
$this->elementParameter = new Parameter('', $elementType); $this->elementParameter = new Parameter('', $elementType);
$this->canBeOne = $canBeOne; $this->canBeOne = $canBeOne;
parent::__construct($name, Parameter::TYPE_ARRAY, $optional, $defaultValue); parent::__construct($name, Parameter::TYPE_ARRAY, $optional, $defaultValue);
} }
public function parseParam($value) { public function parseParam($value): bool {
if(!is_array($value)) { if(!is_array($value)) {
if (!$this->canBeOne) { if (!$this->canBeOne) {
return false; return false;
@ -38,12 +46,12 @@ class ArrayType extends Parameter {
return true; return true;
} }
public function getTypeName() { public function getTypeName(): string {
$elementType = $this->elementParameter->getTypeName(); $elementType = $this->elementParameter->getTypeName();
return parent::getTypeName() . "($elementType)"; return parent::getTypeName() . "($elementType)";
} }
public function toString() { public function toString(): string {
$typeName = $this->getTypeName(); $typeName = $this->getTypeName();
$str = "$typeName $this->name"; $str = "$typeName $this->name";
$defaultValue = (is_null($this->value) ? 'NULL' : (is_array($this->value) ? '[' . implode(",", $this->value) . ']' : $this->value)); $defaultValue = (is_null($this->value) ? 'NULL' : (is_array($this->value) ? '[' . implode(",", $this->value) . ']' : $this->value));

@ -26,11 +26,11 @@ class Parameter {
public string $name; public string $name;
public $value; public $value;
public $optional; public bool $optional;
public int $type; public int $type;
public string $typeName; public string $typeName;
public function __construct($name, $type, $optional = FALSE, $defaultValue = NULL) { public function __construct(string $name, int $type, bool $optional = FALSE, $defaultValue = NULL) {
$this->name = $name; $this->name = $name;
$this->optional = $optional; $this->optional = $optional;
$this->value = $defaultValue; $this->value = $defaultValue;
@ -38,11 +38,11 @@ class Parameter {
$this->typeName = $this->getTypeName(); $this->typeName = $this->getTypeName();
} }
public function getTypeName() { public function getTypeName(): string {
return ($this->type >= 0 && $this->type < count(Parameter::names)) ? Parameter::names[$this->type] : "INVALID"; return ($this->type >= 0 && $this->type < count(Parameter::names)) ? Parameter::names[$this->type] : "INVALID";
} }
public function toString() { public function toString(): string {
$typeName = Parameter::names[$this->type]; $typeName = Parameter::names[$this->type];
$str = "$typeName $this->name"; $str = "$typeName $this->name";
@ -54,7 +54,7 @@ class Parameter {
return $str; return $str;
} }
public static function parseType($value) { public static function parseType($value): int {
if(is_array($value)) if(is_array($value))
return Parameter::TYPE_ARRAY; return Parameter::TYPE_ARRAY;
else if(is_numeric($value) && intval($value) == $value) else if(is_numeric($value) && intval($value) == $value)
@ -77,7 +77,7 @@ class Parameter {
return Parameter::TYPE_STRING; return Parameter::TYPE_STRING;
} }
public function parseParam($value) { public function parseParam($value): bool {
switch($this->type) { switch($this->type) {
case Parameter::TYPE_INT: case Parameter::TYPE_INT:
if(is_numeric($value) && intval($value) == $value) { if(is_numeric($value) && intval($value) == $value) {

@ -5,12 +5,12 @@ namespace Api\Parameter;
class StringType extends Parameter { class StringType extends Parameter {
public int $maxLength; public int $maxLength;
public function __construct($name, $maxLength = -1, $optional = FALSE, $defaultValue = NULL) { public function __construct(string $name, int $maxLength = -1, bool $optional = FALSE, ?string $defaultValue = NULL) {
$this->maxLength = $maxLength; $this->maxLength = $maxLength;
parent::__construct($name, Parameter::TYPE_STRING, $optional, $defaultValue); parent::__construct($name, Parameter::TYPE_STRING, $optional, $defaultValue);
} }
public function parseParam($value) { public function parseParam($value): bool {
if(!is_string($value)) { if(!is_string($value)) {
return false; return false;
} }
@ -23,12 +23,12 @@ class StringType extends Parameter {
return true; return true;
} }
public function getTypeName() { public function getTypeName(): string {
$maxLength = ($this->maxLength > 0 ? "($this->maxLength)" : ""); $maxLength = ($this->maxLength > 0 ? "($this->maxLength)" : "");
return parent::getTypeName() . $maxLength; return parent::getTypeName() . $maxLength;
} }
public function toString() { public function toString(): string {
$typeName = $this->getTypeName(); $typeName = $this->getTypeName();
$str = "$typeName $this->name"; $str = "$typeName $this->name";
$defaultValue = (is_null($this->value) ? 'NULL' : $this->value); $defaultValue = (is_null($this->value) ? 'NULL' : $this->value);

@ -16,7 +16,7 @@ class PatchSQL extends Request {
$this->csrfTokenRequired = false; $this->csrfTokenRequired = false;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }

@ -35,7 +35,7 @@ namespace Api\Permission {
$this->isPublic = false; $this->isPublic = false;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }
@ -102,7 +102,7 @@ namespace Api\Permission {
return $this->success; return $this->success;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }
@ -147,7 +147,7 @@ namespace Api\Permission {
)); ));
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }

@ -18,13 +18,13 @@ class Request {
protected bool $apiKeyAllowed; protected bool $apiKeyAllowed;
protected bool $csrfTokenRequired; protected bool $csrfTokenRequired;
private array $aDefaultParams; private array $defaultParams;
private array $allowedMethods; private array $allowedMethods;
private bool $externalCall; private bool $externalCall;
public function __construct(User $user, bool $externalCall = false, array $params = array()) { public function __construct(User $user, bool $externalCall = false, array $params = array()) {
$this->user = $user; $this->user = $user;
$this->aDefaultParams = $params; $this->defaultParams = $params;
$this->success = false; $this->success = false;
$this->result = array(); $this->result = array();
@ -45,29 +45,30 @@ class Request {
} }
} }
public function parseParams($values) { public function parseParams($values): bool {
foreach($this->params as $name => $param) { foreach ($this->params as $name => $param) {
$value = $values[$name] ?? NULL; $value = $values[$name] ?? NULL;
$isEmpty = (is_string($value) && strlen($value) === 0) || (is_array($value) && empty($value)); $isEmpty = (is_string($value) && strlen($value) === 0) || (is_array($value) && empty($value));
if(!$param->optional && (is_null($value) || $isEmpty)) { if (!$param->optional && (is_null($value) || $isEmpty)) {
return $this->createError("Missing parameter: $name"); return $this->createError("Missing parameter: $name");
} }
if(!is_null($value) && !$isEmpty) { if (!is_null($value) && !$isEmpty) {
if(!$param->parseParam($value)) { if (!$param->parseParam($value)) {
$value = print_r($value, true); $value = print_r($value, true);
return $this->createError("Invalid Type for parameter: $name '$value' (Required: " . $param->getTypeName() . ")"); return $this->createError("Invalid Type for parameter: $name '$value' (Required: " . $param->getTypeName() . ")");
} }
} }
} }
return true; return true;
} }
public function parseVariableParams($values) { public function parseVariableParams($values) {
foreach($values as $name => $value) { foreach ($values as $name => $value) {
if(isset($this->params[$name])) continue; if (isset($this->params[$name])) continue;
$type = Parameter\Parameter::parseType($value); $type = Parameter\Parameter::parseType($value);
$param = new Parameter\Parameter($name, $type, true); $param = new Parameter\Parameter($name, $type, true);
$param->parseParam($value); $param->parseParam($value);
@ -75,19 +76,19 @@ class Request {
} }
} }
public function execute($values = array()) { public function execute($values = array()): bool {
$this->params = $this->aDefaultParams; $this->params = array_merge([], $this->defaultParams);
$this->success = false; $this->success = false;
$this->result = array(); $this->result = array();
$this->lastError = ''; $this->lastError = '';
if($this->user->isLoggedIn()) { if ($this->user->isLoggedIn()) {
$this->result['logoutIn'] = $this->user->getSession()->getExpiresSeconds(); $this->result['logoutIn'] = $this->user->getSession()->getExpiresSeconds();
} }
if($this->externalCall) { if ($this->externalCall) {
$values = $_REQUEST; $values = $_REQUEST;
if($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_SERVER["CONTENT_TYPE"]) && in_array("application/json", explode(";", $_SERVER["CONTENT_TYPE"]))) { if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_SERVER["CONTENT_TYPE"]) && in_array("application/json", explode(";", $_SERVER["CONTENT_TYPE"]))) {
$jsonData = json_decode(file_get_contents('php://input'), true); $jsonData = json_decode(file_get_contents('php://input'), true);
if ($jsonData) { if ($jsonData) {
$values = array_merge($values, $jsonData); $values = array_merge($values, $jsonData);
@ -99,34 +100,34 @@ class Request {
} }
} }
if($this->isDisabled) { if ($this->isDisabled) {
$this->lastError = "This function is currently disabled."; $this->lastError = "This function is currently disabled.";
return false; return false;
} }
if($this->externalCall && !$this->isPublic) { if ($this->externalCall && !$this->isPublic) {
$this->lastError = 'This function is private.'; $this->lastError = 'This function is private.';
header('HTTP 1.1 403 Forbidden'); header('HTTP 1.1 403 Forbidden');
return false; return false;
} }
if(!in_array($_SERVER['REQUEST_METHOD'], $this->allowedMethods)) { if (!in_array($_SERVER['REQUEST_METHOD'], $this->allowedMethods)) {
$this->lastError = 'This method is not allowed'; $this->lastError = 'This method is not allowed';
header('HTTP 1.1 405 Method Not Allowed'); header('HTTP 1.1 405 Method Not Allowed');
return false; return false;
} }
if($this->externalCall) { if ($this->externalCall) {
$apiKeyAuthorized = false; $apiKeyAuthorized = false;
// Logged in or api key authorized? // Logged in or api key authorized?
if ($this->loginRequired) { if ($this->loginRequired) {
if(isset($values['api_key']) && $this->apiKeyAllowed) { if (isset($values['api_key']) && $this->apiKeyAllowed) {
$apiKey = $values['api_key']; $apiKey = $values['api_key'];
$apiKeyAuthorized = $this->user->authorize($apiKey); $apiKeyAuthorized = $this->user->authorize($apiKey);
} }
if(!$this->user->isLoggedIn() && !$apiKeyAuthorized) { if (!$this->user->isLoggedIn() && !$apiKeyAuthorized) {
$this->lastError = 'You are not logged in.'; $this->lastError = 'You are not logged in.';
header('HTTP 1.1 401 Unauthorized'); header('HTTP 1.1 401 Unauthorized');
return false; return false;
@ -134,7 +135,7 @@ class Request {
} }
// CSRF Token // CSRF Token
if($this->csrfTokenRequired && $this->user->isLoggedIn()) { if ($this->csrfTokenRequired && $this->user->isLoggedIn()) {
// csrf token required + external call // csrf token required + external call
// if it's not a call with API_KEY, check for csrf_token // if it's not a call with API_KEY, check for csrf_token
if (!isset($values["csrf_token"]) || strcmp($values["csrf_token"], $this->user->getSession()->getCsrfToken()) !== 0) { if (!isset($values["csrf_token"]) || strcmp($values["csrf_token"], $this->user->getSession()->getCsrfToken()) !== 0) {
@ -155,13 +156,15 @@ class Request {
} }
} }
if(!$this->parseParams($values)) if (!$this->parseParams($values)) {
return false; return false;
}
if($this->variableParamCount) if ($this->variableParamCount) {
$this->parseVariableParams($values); $this->parseVariableParams($values);
}
if(!$this->user->getSQL()->isConnected()) { if (!$this->user->getSQL()->isConnected()) {
$this->lastError = $this->user->getSQL()->getLastError(); $this->lastError = $this->user->getSQL()->getLastError();
return false; return false;
} }
@ -171,23 +174,40 @@ class Request {
return true; return true;
} }
protected function createError($err) { protected function createError($err): bool {
$this->success = false; $this->success = false;
$this->lastError = $err; $this->lastError = $err;
return false; return false;
} }
protected function getParam($name) { protected function getParam($name) {
return isset($this->params[$name]) ? $this->params[$name]->value : NULL; // i don't know why phpstorm
return (isset($this->params[$name]) ? $this->params[$name]->value : NULL);
} }
public function isPublic() { return $this->isPublic; } public function isPublic(): bool {
public function getLastError() { return $this->lastError; } return $this->isPublic;
public function getResult() { return $this->result; } }
public function success() { return $this->success; }
public function loginRequired() { return $this->loginRequired; } public function getLastError(): string {
public function isExternalCall() { return $this->externalCall; } return $this->lastError;
public function clearError() { $this->success = true; $this->lastError = ""; } }
public function getResult(): array {
return $this->result;
}
public function success(): bool {
return $this->success;
}
public function loginRequired(): bool {
return $this->loginRequired;
}
public function isExternalCall(): bool {
return $this->externalCall;
}
private function getMethod() { private function getMethod() {
$class = str_replace("\\", "/", get_class($this)); $class = str_replace("\\", "/", get_class($this));
@ -195,7 +215,7 @@ class Request {
return $class; return $class;
} }
public function getJsonResult() { public function getJsonResult(): string {
$this->result['success'] = $this->success; $this->result['success'] = $this->success;
$this->result['msg'] = $this->lastError; $this->result['msg'] = $this->lastError;
return json_encode($this->result); return json_encode($this->result);

@ -34,7 +34,7 @@ namespace Api\Routes {
parent::__construct($user, $externalCall, array()); parent::__construct($user, $externalCall, array());
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if(!parent::execute($values)) { if(!parent::execute($values)) {
return false; return false;
} }
@ -81,7 +81,7 @@ namespace Api\Routes {
$this->isPublic = false; $this->isPublic = false;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if(!parent::execute($values)) { if(!parent::execute($values)) {
return false; return false;
} }
@ -133,7 +133,7 @@ namespace Api\Routes {
)); ));
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if(!parent::execute($values)) { if(!parent::execute($values)) {
return false; return false;
} }

@ -29,7 +29,7 @@ namespace Api\Settings {
)); ));
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if(!parent::execute($values)) { if(!parent::execute($values)) {
return false; return false;
} }
@ -72,7 +72,7 @@ namespace Api\Settings {
)); ));
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }

@ -66,7 +66,7 @@ class Stats extends Request {
return ($this->success ? $res[0]["count"] : $this->success); return ($this->success ? $res[0]["count"] : $this->success);
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if(!parent::execute($values)) { if(!parent::execute($values)) {
return false; return false;
} }

@ -9,11 +9,11 @@ namespace Api {
protected function userExists(?string $username, ?string $email) { protected function userExists(?string $username, ?string $email) {
$conditions = array(); $conditions = array();
if (!is_null($username) && !empty($username)) { if ($username) {
$conditions[] = new Compare("User.name", $username); $conditions[] = new Compare("User.name", $username);
} }
if (!is_null($email) && !empty($email)) { if ($email) {
$conditions[] = new Compare("User.email", $email); $conditions[] = new Compare("User.email", $email);
} }
@ -152,7 +152,7 @@ namespace Api\User {
$this->loginRequired = true; $this->loginRequired = true;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }
@ -229,7 +229,7 @@ namespace Api\User {
return false; return false;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }
@ -306,7 +306,7 @@ namespace Api\User {
)); ));
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }
@ -350,7 +350,7 @@ namespace Api\User {
$this->csrfTokenRequired = false; $this->csrfTokenRequired = false;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }
@ -377,7 +377,7 @@ namespace Api\User {
$this->loginRequired = true; $this->loginRequired = true;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }
@ -469,7 +469,7 @@ namespace Api\User {
return $this->success; return $this->success;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }
@ -533,7 +533,7 @@ namespace Api\User {
return $this->success; return $this->success;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }
@ -586,7 +586,7 @@ namespace Api\User {
return $this->createError(L('Wrong username or password')); return $this->createError(L('Wrong username or password'));
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }
@ -648,7 +648,7 @@ namespace Api\User {
$this->apiKeyAllowed = false; $this->apiKeyAllowed = false;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }
@ -693,7 +693,7 @@ namespace Api\User {
return $this->success; return $this->success;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }
@ -804,7 +804,7 @@ namespace Api\User {
return array(); return array();
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }
@ -847,7 +847,7 @@ namespace Api\User {
$this->loginRequired = true; $this->loginRequired = true;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }
@ -945,7 +945,7 @@ namespace Api\User {
$this->loginRequired = true; $this->loginRequired = true;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }
@ -986,7 +986,7 @@ namespace Api\User {
$this->csrfTokenRequired = false; $this->csrfTokenRequired = false;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }
@ -1105,7 +1105,7 @@ namespace Api\User {
return $this->success; return $this->success;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }

@ -16,7 +16,7 @@ class VerifyCaptcha extends Request {
$this->isPublic = false; $this->isPublic = false;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if(!parent::execute($values)) { if(!parent::execute($values)) {
return false; return false;
} }

@ -27,7 +27,7 @@ namespace Api\Visitors {
$this->isPublic = false; $this->isPublic = false;
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }
@ -75,7 +75,7 @@ namespace Api\Visitors {
} }
} }
public function execute($values = array()) { public function execute($values = array()): bool {
if (!parent::execute($values)) { if (!parent::execute($values)) {
return false; return false;
} }

@ -15,27 +15,27 @@ class Configuration {
$class = \Configuration\Database::class; $class = \Configuration\Database::class;
$path = getClassPath($class, true); $path = getClassPath($class, true);
if(file_exists($path) && is_readable($path)) { if (file_exists($path) && is_readable($path)) {
include_once $path; include_once $path;
if(class_exists($class)) { if (class_exists($class)) {
$this->database = new \Configuration\Database(); $this->database = new \Configuration\Database();
} }
} }
} }
public function getDatabase() : ?ConnectionData { public function getDatabase(): ?ConnectionData {
return $this->database; return $this->database;
} }
public function getSettings() : Settings { public function getSettings(): Settings {
return $this->settings; return $this->settings;
} }
public function create(string $className, $data) { public function create(string $className, $data) {
$path = getClassPath("\\Configuration\\$className"); $path = getClassPath("\\Configuration\\$className");
if($data) { if ($data) {
if(is_string($data)) { if (is_string($data)) {
$key = addslashes($data); $key = addslashes($data);
$code = intendCode( $code = intendCode(
"<?php "<?php
@ -50,7 +50,7 @@ class Configuration {
}", false }", false
); );
} else if($data instanceof ConnectionData) { } else if ($data instanceof ConnectionData) {
$superClass = get_class($data); $superClass = get_class($data);
$host = addslashes($data->getHost()); $host = addslashes($data->getHost());
$port = intval($data->getPort()); $port = intval($data->getPort());
@ -58,7 +58,7 @@ class Configuration {
$password = addslashes($data->getPassword()); $password = addslashes($data->getPassword());
$properties = ""; $properties = "";
foreach($data->getProperties() as $key => $val) { foreach ($data->getProperties() as $key => $val) {
$key = addslashes($key); $key = addslashes($key);
$val = is_string($val) ? "'" . addslashes($val) . "'" : $val; $val = is_string($val) ? "'" . addslashes($val) . "'" : $val;
$properties .= "\n\$this->setProperty('$key', $val);"; $properties .= "\n\$this->setProperty('$key', $val);";
@ -86,9 +86,9 @@ class Configuration {
return @file_put_contents($path, $code); return @file_put_contents($path, $code);
} }
public function delete(string $className) { public function delete(string $className): bool {
$path = getClassPath("\\Configuration\\$className"); $path = getClassPath("\\Configuration\\$className");
if(file_exists($path)) { if (file_exists($path)) {
return unlink($path); return unlink($path);
} }

@ -11,7 +11,7 @@ class CreateDatabase extends DatabaseScript {
// NOTE: // NOTE:
// explicit serial ids removed due to postgres' serial implementation // explicit serial ids removed due to postgres' serial implementation
public static function createQueries(SQL $sql) { public static function createQueries(SQL $sql): array {
$queries = array(); $queries = array();
// Language // Language
@ -24,8 +24,8 @@ class CreateDatabase extends DatabaseScript {
->unique("name"); ->unique("name");
$queries[] = $sql->insert("Language", array("code", "name")) $queries[] = $sql->insert("Language", array("code", "name"))
->addRow( "en_US", 'American English') ->addRow("en_US", 'American English')
->addRow( "de_DE", 'Deutsch Standard'); ->addRow("de_DE", 'Deutsch Standard');
$queries[] = $sql->createTable("User") $queries[] = $sql->createTable("User")
->addSerial("uid") ->addSerial("uid")
@ -50,7 +50,7 @@ class CreateDatabase extends DatabaseScript {
->addString("browser", 64) ->addString("browser", 64)
->addJson("data", false, '{}') ->addJson("data", false, '{}')
->addBool("stay_logged_in", true) ->addBool("stay_logged_in", true)
->addString("csrf_token", 16 ) ->addString("csrf_token", 16)
->primaryKey("uid", "user_id") ->primaryKey("uid", "user_id")
->foreignKey("user_id", "User", "uid", new CascadeStrategy()); ->foreignKey("user_id", "User", "uid", new CascadeStrategy());
@ -82,7 +82,7 @@ class CreateDatabase extends DatabaseScript {
$queries[] = $sql->createTable("Notification") $queries[] = $sql->createTable("Notification")
->addSerial("uid") ->addSerial("uid")
->addEnum("type", array("default","message","warning"), false, "default") ->addEnum("type", array("default", "message", "warning"), false, "default")
->addDateTime("created_at", false, $sql->currentTimestamp()) ->addDateTime("created_at", false, $sql->currentTimestamp())
->addString("title", 32) ->addString("title", 32)
->addString("message", 256) ->addString("message", 256)
@ -200,7 +200,7 @@ class CreateDatabase extends DatabaseScript {
return $queries; return $queries;
} }
private static function MessageConfirmEmail() : string { private static function MessageConfirmEmail(): string {
return "Hello {{username}},<br>" . return "Hello {{username}},<br>" .
"You recently created an account on {{site_name}}. Please click on the following link to " . "You recently created an account on {{site_name}}. Please click on the following link to " .
"confirm your email address and complete your registration. If you haven't registered an " . "confirm your email address and complete your registration. If you haven't registered an " .
@ -210,7 +210,7 @@ class CreateDatabase extends DatabaseScript {
"{{site_name}} Administration"; "{{site_name}} Administration";
} }
private static function MessageAcceptInvite() : string { private static function MessageAcceptInvite(): string {
return "Hello {{username}},<br>" . return "Hello {{username}},<br>" .
"You were invited to create an account on {{site_name}}. Please click on the following link to " . "You were invited to create an account on {{site_name}}. Please click on the following link to " .
"confirm your email address and complete your registration by choosing a new password. " . "confirm your email address and complete your registration by choosing a new password. " .
@ -220,7 +220,7 @@ class CreateDatabase extends DatabaseScript {
"{{site_name}} Administration"; "{{site_name}} Administration";
} }
private static function MessageResetPassword() : string { private static function MessageResetPassword(): string {
return "Hello {{username}},<br>" . return "Hello {{username}},<br>" .
"you requested a password reset on {{site_name}}. Please click on the following link to " . "you requested a password reset on {{site_name}}. Please click on the following link to " .
"choose a new password. If this request was not intended, you can simply ignore the email. The Link is valid for one hour:<br><br>" . "choose a new password. If this request was not intended, you can simply ignore the email. The Link is valid for one hour:<br><br>" .
@ -233,7 +233,7 @@ class CreateDatabase extends DatabaseScript {
$patchDirectory = './core/Configuration/Patch/'; $patchDirectory = './core/Configuration/Patch/';
if (file_exists($patchDirectory) && is_dir($patchDirectory)) { if (file_exists($patchDirectory) && is_dir($patchDirectory)) {
$scan_arr = scandir($patchDirectory); $scan_arr = scandir($patchDirectory);
$files_arr = array_diff($scan_arr, array('.','..')); $files_arr = array_diff($scan_arr, array('.', '..'));
foreach ($files_arr as $file) { foreach ($files_arr as $file) {
$suffix = ".class.php"; $suffix = ".class.php";
if (endsWith($file, $suffix)) { if (endsWith($file, $suffix)) {
@ -241,7 +241,7 @@ class CreateDatabase extends DatabaseScript {
$className = "\\Configuration\\Patch\\$className"; $className = "\\Configuration\\Patch\\$className";
$method = "$className::createQueries"; $method = "$className::createQueries";
$patchQueries = call_user_func($method, $sql); $patchQueries = call_user_func($method, $sql);
foreach($patchQueries as $query) $queries[] = $query; foreach ($patchQueries as $query) $queries[] = $query;
} }
} }
} }

@ -10,7 +10,7 @@ use Driver\SQL\Strategy\UpdateStrategy;
class file_api extends DatabaseScript { class file_api extends DatabaseScript {
public static function createQueries(SQL $sql) { public static function createQueries(SQL $sql): array {
$queries = array(); $queries = array();

@ -11,12 +11,16 @@ use Objects\User;
class Settings { class Settings {
//
private bool $installationComplete;
// settings
private string $siteName; private string $siteName;
private string $baseUrl; private string $baseUrl;
private string $jwtSecret; private string $jwtSecret;
private bool $installationComplete;
private bool $registrationAllowed; private bool $registrationAllowed;
private bool $recaptchaEnabled; private bool $recaptchaEnabled;
private bool $mailEnabled;
private string $recaptchaPublicKey; private string $recaptchaPublicKey;
private string $recaptchaPrivateKey; private string $recaptchaPrivateKey;
@ -24,11 +28,11 @@ class Settings {
return $this->jwtSecret; return $this->jwtSecret;
} }
public function isInstalled() { public function isInstalled(): bool {
return $this->installationComplete; return $this->installationComplete;
} }
public static function loadDefaults() : Settings { public static function loadDefaults(): Settings {
$hostname = $_SERVER["SERVER_NAME"]; $hostname = $_SERVER["SERVER_NAME"];
$protocol = getProtocol(); $protocol = getProtocol();
$jwt = generateRandomString(32); $jwt = generateRandomString(32);
@ -42,10 +46,11 @@ class Settings {
$settings->recaptchaPublicKey = ""; $settings->recaptchaPublicKey = "";
$settings->recaptchaPrivateKey = ""; $settings->recaptchaPrivateKey = "";
$settings->recaptchaEnabled = false; $settings->recaptchaEnabled = false;
$settings->mailEnabled = false;
return $settings; return $settings;
} }
public function loadFromDatabase(User $user) { public function loadFromDatabase(User $user): bool {
$req = new \Api\Settings\Get($user); $req = new \Api\Settings\Get($user);
$success = $req->execute(); $success = $req->execute();
@ -58,6 +63,7 @@ class Settings {
$this->recaptchaEnabled = $result["recaptcha_enabled"] ?? $this->recaptchaEnabled; $this->recaptchaEnabled = $result["recaptcha_enabled"] ?? $this->recaptchaEnabled;
$this->recaptchaPublicKey = $result["recaptcha_public_key"] ?? $this->recaptchaPublicKey; $this->recaptchaPublicKey = $result["recaptcha_public_key"] ?? $this->recaptchaPublicKey;
$this->recaptchaPrivateKey = $result["recaptcha_private_key"] ?? $this->recaptchaPrivateKey; $this->recaptchaPrivateKey = $result["recaptcha_private_key"] ?? $this->recaptchaPrivateKey;
$this->mailEnabled = $result["mail_enabled"] ?? $this->mailEnabled;
if (!isset($result["jwt_secret"])) { if (!isset($result["jwt_secret"])) {
$req = new \Api\Settings\Set($user); $req = new \Api\Settings\Set($user);
@ -81,27 +87,27 @@ class Settings {
->addRow("recaptcha_private_key", $this->recaptchaPrivateKey, true, false); ->addRow("recaptcha_private_key", $this->recaptchaPrivateKey, true, false);
} }
public function getSiteName() : string { public function getSiteName(): string {
return $this->siteName; return $this->siteName;
} }
public function getBaseUrl() : string { public function getBaseUrl(): string {
return $this->baseUrl; return $this->baseUrl;
} }
public function isRecaptchaEnabled() : bool { public function isRecaptchaEnabled(): bool {
return $this->recaptchaEnabled; return $this->recaptchaEnabled;
} }
public function getRecaptchaSiteKey() : string { public function getRecaptchaSiteKey(): string {
return $this->recaptchaPublicKey; return $this->recaptchaPublicKey;
} }
public function getRecaptchaSecretKey() : string { public function getRecaptchaSecretKey(): string {
return $this->recaptchaPrivateKey; return $this->recaptchaPrivateKey;
} }
public function isRegistrationAllowed() : bool { public function isRegistrationAllowed(): bool {
return $this->registrationAllowed; return $this->registrationAllowed;
} }
} }

@ -34,7 +34,7 @@ namespace Documents\Account {
$this->loadFontawesome(); $this->loadFontawesome();
} }
protected function initMetas() { protected function initMetas(): array {
return array( return array(
array('name' => 'viewport', 'content' => 'width=device-width, initial-scale=1.0'), array('name' => 'viewport', 'content' => 'width=device-width, initial-scale=1.0'),
array('name' => 'format-detection', 'content' => 'telephone=yes'), array('name' => 'format-detection', 'content' => 'telephone=yes'),
@ -44,11 +44,11 @@ namespace Documents\Account {
); );
} }
protected function initRawFields() { protected function initRawFields(): array {
return array(); return array();
} }
protected function initTitle() { protected function initTitle(): string {
return "Account"; return "Account";
} }
} }
@ -59,7 +59,7 @@ namespace Documents\Account {
parent::__construct($document); parent::__construct($document);
} }
protected function getContent() { protected function getContent(): string {
$view = $this->getDocument()->getView(); $view = $this->getDocument()->getView();
if ($view === null) { if ($view === null) {

@ -19,8 +19,6 @@ namespace Documents {
namespace Documents\Admin { namespace Documents\Admin {
use Elements\Head; use Elements\Head;
use Elements\Link;
use Elements\Script;
class AdminHead extends Head { class AdminHead extends Head {
@ -32,7 +30,7 @@ namespace Documents\Admin {
$this->loadFontawesome(); $this->loadFontawesome();
} }
protected function initMetas() { protected function initMetas(): array {
return array( return array(
array('name' => 'viewport', 'content' => 'width=device-width, initial-scale=1.0'), array('name' => 'viewport', 'content' => 'width=device-width, initial-scale=1.0'),
array('name' => 'format-detection', 'content' => 'telephone=yes'), array('name' => 'format-detection', 'content' => 'telephone=yes'),
@ -42,12 +40,12 @@ namespace Documents\Admin {
); );
} }
protected function initRawFields() { protected function initRawFields(): array {
return array(); return array();
} }
protected function initTitle() { protected function initTitle(): string {
return "WebBase - Administration"; return $this->getSiteName() . " - Administration";
} }
} }
} }

@ -15,7 +15,6 @@ namespace Documents {
namespace Documents\Document404 { namespace Documents\Document404 {
use Elements\Body;
use Elements\Head; use Elements\Head;
use Elements\SimpleBody; use Elements\SimpleBody;
use Views\View404; use Views\View404;
@ -29,7 +28,7 @@ namespace Documents\Document404 {
protected function initSources() { protected function initSources() {
} }
protected function initMetas() { protected function initMetas(): array {
return array( return array(
array('name' => 'viewport', 'content' => 'width=device-width, initial-scale=1.0'), array('name' => 'viewport', 'content' => 'width=device-width, initial-scale=1.0'),
array('name' => 'format-detection', 'content' => 'telephone=yes'), array('name' => 'format-detection', 'content' => 'telephone=yes'),
@ -39,11 +38,11 @@ namespace Documents\Document404 {
); );
} }
protected function initRawFields() { protected function initRawFields(): array {
return array(); return array();
} }
protected function initTitle() { protected function initTitle(): string {
return "WebBase - Not Found"; return "WebBase - Not Found";
} }
} }
@ -58,7 +57,7 @@ namespace Documents\Document404 {
http_response_code(404); http_response_code(404);
} }
protected function getContent() { protected function getContent(): string {
return $this->load(View404::class); return $this->load(View404::class);
} }
} }

@ -17,17 +17,18 @@ namespace Documents {
namespace Documents\Files { namespace Documents\Files {
use Elements\Head; use Elements\Head;
use Elements\Link;
use Elements\Script; use Elements\Script;
use Elements\SimpleBody; use Elements\SimpleBody;
class FilesHead extends Head { class FilesHead extends Head {
protected function initSources() { protected function initSources() {
$this->loadBootstrap(); $this->addCSS(Link::BOOTSTRAP);
$this->loadFontawesome(); $this->loadFontawesome();
} }
protected function initMetas() { protected function initMetas(): array {
return array( return array(
array('name' => 'viewport', 'content' => 'width=device-width, initial-scale=1.0'), array('name' => 'viewport', 'content' => 'width=device-width, initial-scale=1.0'),
array('name' => 'format-detection', 'content' => 'telephone=yes'), array('name' => 'format-detection', 'content' => 'telephone=yes'),
@ -38,11 +39,11 @@ namespace Documents\Files {
); );
} }
protected function initRawFields() { protected function initRawFields(): array {
return array(); return array();
} }
protected function initTitle() { protected function initTitle(): string {
return "File Control Panel"; return "File Control Panel";
} }
} }
@ -53,7 +54,7 @@ namespace Documents\Files {
parent::__construct($document); parent::__construct($document);
} }
protected function getContent() { protected function getContent(): string {
$html = "<noscript>" . $this->createErrorText("Javascript is required for this site to render.") . "</noscript>"; $html = "<noscript>" . $this->createErrorText("Javascript is required for this site to render.") . "</noscript>";
$html .= "<div id=\"root\"></div>"; $html .= "<div id=\"root\"></div>";
$html .= new Script(Script::MIME_TEXT_JAVASCRIPT, Script::FILES); $html .= new Script(Script::MIME_TEXT_JAVASCRIPT, Script::FILES);

@ -41,7 +41,7 @@ namespace Documents\Install {
$this->addJS(Script::INSTALL); $this->addJS(Script::INSTALL);
} }
protected function initMetas() { protected function initMetas(): array {
return array( return array(
array('name' => 'viewport', 'content' => 'width=device-width, initial-scale=1.0'), array('name' => 'viewport', 'content' => 'width=device-width, initial-scale=1.0'),
array('name' => 'format-detection', 'content' => 'telephone=yes'), array('name' => 'format-detection', 'content' => 'telephone=yes'),
@ -51,11 +51,11 @@ namespace Documents\Install {
); );
} }
protected function initRawFields() { protected function initRawFields(): array {
return array(); return array();
} }
protected function initTitle() { protected function initTitle(): string {
return "WebBase - Installation"; return "WebBase - Installation";
} }
@ -88,17 +88,17 @@ namespace Documents\Install {
$this->steps = array(); $this->steps = array();
} }
private function getParameter($name) { private function getParameter($name): ?string {
if(isset($_REQUEST[$name]) && is_string($_REQUEST[$name])) { if (isset($_REQUEST[$name]) && is_string($_REQUEST[$name])) {
return trim($_REQUEST[$name]); return trim($_REQUEST[$name]);
} }
return NULL; return NULL;
} }
private function getCurrentStep() { private function getCurrentStep(): int {
if(!$this->checkRequirements()["success"]) { if (!$this->checkRequirements()["success"]) {
return self::CHECKING_REQUIREMENTS; return self::CHECKING_REQUIREMENTS;
} }
@ -106,12 +106,12 @@ namespace Documents\Install {
$config = $user->getConfiguration(); $config = $user->getConfiguration();
// Check if database configuration exists // Check if database configuration exists
if(!$config->getDatabase()) { if (!$config->getDatabase()) {
return self::DATABASE_CONFIGURATION; return self::DATABASE_CONFIGURATION;
} }
$sql = $user->getSQL(); $sql = $user->getSQL();
if(!$sql || !$sql->isConnected()) { if (!$sql || !$sql->isConnected()) {
return self::DATABASE_CONFIGURATION; return self::DATABASE_CONFIGURATION;
} }
@ -156,33 +156,33 @@ namespace Documents\Install {
return $step; return $step;
} }
private function checkRequirements() { private function checkRequirements(): array {
$msg = $this->errorString; $msg = $this->errorString;
$success = true; $success = true;
$failedRequirements = array(); $failedRequirements = array();
$configDir = "core/Configuration/"; $configDir = "core/Configuration/";
if(!is_writeable($configDir)) { if (!is_writeable($configDir)) {
$failedRequirements[] = "<b>$configDir</b> is not writeable. Try running <b>chmod 700 $configDir</b>"; $failedRequirements[] = "<b>$configDir</b> is not writeable. Try running <b>chmod 700 $configDir</b>";
$success = false; $success = false;
} }
if (function_exists("posix_getuid")) { if (function_exists("posix_getuid")) {
$userId = posix_getuid(); $userId = posix_getuid();
if(fileowner($configDir) !== $userId) { if (fileowner($configDir) !== $userId) {
$username = posix_getpwuid($userId)['name']; $username = posix_getpwuid($userId)['name'];
$failedRequirements[] = "<b>$configDir</b> is not owned by current user: $username ($userId). Try running <b>chown -R $username $configDir</b>"; $failedRequirements[] = "<b>$configDir</b> is not owned by current user: $username ($userId). Try running <b>chown -R $username $configDir</b>";
$success = false; $success = false;
} }
} }
if(version_compare(PHP_VERSION, '7.4', '<')) { if (version_compare(PHP_VERSION, '7.4', '<')) {
$failedRequirements[] = "PHP Version <b>>= 7.4</b> is required. Got: <b>" . PHP_VERSION . "</b>"; $failedRequirements[] = "PHP Version <b>>= 7.4</b> is required. Got: <b>" . PHP_VERSION . "</b>";
$success = false; $success = false;
} }
if(!$success) { if (!$success) {
$msg = "The following requirements failed the check:<br>" . $msg = "The following requirements failed the check:<br>" .
$this->createUnorderedList($failedRequirements); $this->createUnorderedList($failedRequirements);
$this->errorString = $msg; $this->errorString = $msg;
@ -191,7 +191,7 @@ namespace Documents\Install {
return array("success" => $success, "msg" => $msg); return array("success" => $success, "msg" => $msg);
} }
private function databaseConfiguration() { private function databaseConfiguration(): array {
$host = $this->getParameter("host"); $host = $this->getParameter("host");
$port = $this->getParameter("port"); $port = $this->getParameter("port");
@ -204,44 +204,44 @@ namespace Documents\Install {
$success = true; $success = true;
$missingInputs = array(); $missingInputs = array();
if(is_null($host) || empty($host)) { if (is_null($host) || empty($host)) {
$success = false; $success = false;
$missingInputs[] = "Host"; $missingInputs[] = "Host";
} }
if(is_null($port) || empty($port)) { if (is_null($port) || empty($port)) {
$success = false; $success = false;
$missingInputs[] = "Port"; $missingInputs[] = "Port";
} }
if(is_null($username) || empty($username)) { if (is_null($username) || empty($username)) {
$success = false; $success = false;
$missingInputs[] = "Username"; $missingInputs[] = "Username";
} }
if(is_null($password)) { if (is_null($password)) {
$success = false; $success = false;
$missingInputs[] = "Password"; $missingInputs[] = "Password";
} }
if(is_null($database) || empty($database)) { if (is_null($database) || empty($database)) {
$success = false; $success = false;
$missingInputs[] = "Database"; $missingInputs[] = "Database";
} }
if(is_null($type) || empty($type)) { if (is_null($type) || empty($type)) {
$success = false; $success = false;
$missingInputs[] = "Type"; $missingInputs[] = "Type";
} }
$supportedTypes = array("mysql", "postgres"); $supportedTypes = array("mysql", "postgres");
if(!$success) { if (!$success) {
$msg = "Please fill out the following inputs:<br>" . $msg = "Please fill out the following inputs:<br>" .
$this->createUnorderedList($missingInputs); $this->createUnorderedList($missingInputs);
} else if(!is_numeric($port) || ($port = intval($port)) < 1 || $port > 65535) { } else if (!is_numeric($port) || ($port = intval($port)) < 1 || $port > 65535) {
$msg = "Port must be in range of 1-65535."; $msg = "Port must be in range of 1-65535.";
$success = false; $success = false;
} else if(!in_array($type, $supportedTypes)) { } else if (!in_array($type, $supportedTypes)) {
$msg = "Unsupported database type. Must be one of: " . implode(", ", $supportedTypes); $msg = "Unsupported database type. Must be one of: " . implode(", ", $supportedTypes);
$success = false; $success = false;
} else { } else {
@ -251,9 +251,9 @@ namespace Documents\Install {
$connectionData->setProperty('type', $type); $connectionData->setProperty('type', $type);
$sql = SQL::createConnection($connectionData); $sql = SQL::createConnection($connectionData);
$success = false; $success = false;
if(is_string($sql)) { if (is_string($sql)) {
$msg = "Error connecting to database: $sql"; $msg = "Error connecting to database: $sql";
} else if(!$sql->isConnected()) { } else if (!$sql->isConnected()) {
if (!$sql->checkRequirements()) { if (!$sql->checkRequirements()) {
$driverName = $sql->getDriverName(); $driverName = $sql->getDriverName();
$installLink = "https://www.php.net/manual/en/$driverName.setup.php"; $installLink = "https://www.php.net/manual/en/$driverName.setup.php";
@ -267,7 +267,7 @@ namespace Documents\Install {
$msg = ""; $msg = "";
$success = true; $success = true;
$queries = CreateDatabase::createQueries($sql); $queries = CreateDatabase::createQueries($sql);
foreach($queries as $query) { foreach ($queries as $query) {
if (!($res = $query->execute())) { if (!($res = $query->execute())) {
$msg = "Error creating tables: " . $sql->getLastError(); $msg = "Error creating tables: " . $sql->getLastError();
$success = false; $success = false;
@ -276,13 +276,13 @@ namespace Documents\Install {
} }
$config = $this->getDocument()->getUser()->getConfiguration(); $config = $this->getDocument()->getUser()->getConfiguration();
if(!$config->create("Database", $connectionData)) { if (!$config->create("Database", $connectionData)) {
$success = false; $success = false;
$msg = "Unable to write file"; $msg = "Unable to write file";
} }
} }
if($sql) { if ($sql) {
$sql->close(); $sql->close();
} }
} }
@ -290,10 +290,10 @@ namespace Documents\Install {
return array("success" => $success, "msg" => $msg); return array("success" => $success, "msg" => $msg);
} }
private function createUser() { private function createUser(): array {
$user = $this->getDocument()->getUser(); $user = $this->getDocument()->getUser();
if($this->getParameter("prev") === "true") { if ($this->getParameter("prev") === "true") {
$success = $user->getConfiguration()->delete("Database"); $success = $user->getConfiguration()->delete("Database");
$msg = $success ? "" : error_get_last(); $msg = $success ? "" : error_get_last();
return array("success" => $success, "msg" => $msg); return array("success" => $success, "msg" => $msg);
@ -307,22 +307,22 @@ namespace Documents\Install {
$success = true; $success = true;
$missingInputs = array(); $missingInputs = array();
if(is_null($username) || empty($username)) { if (is_null($username) || empty($username)) {
$success = false; $success = false;
$missingInputs[] = "Username"; $missingInputs[] = "Username";
} }
if(is_null($password) || empty($password)) { if (is_null($password) || empty($password)) {
$success = false; $success = false;
$missingInputs[] = "Password"; $missingInputs[] = "Password";
} }
if(is_null($confirmPassword) || empty($confirmPassword)) { if (is_null($confirmPassword) || empty($confirmPassword)) {
$success = false; $success = false;
$missingInputs[] = "Confirm Password"; $missingInputs[] = "Confirm Password";
} }
if(!$success) { if (!$success) {
$msg = "Please fill out the following inputs:<br>" . $msg = "Please fill out the following inputs:<br>" .
$this->createUnorderedList($missingInputs); $this->createUnorderedList($missingInputs);
} else { } else {
@ -347,10 +347,10 @@ namespace Documents\Install {
return array("msg" => $msg, "success" => $success); return array("msg" => $msg, "success" => $success);
} }
private function addMailService() { private function addMailService(): array {
$user = $this->getDocument()->getUser(); $user = $this->getDocument()->getUser();
if($this->getParameter("prev") === "true") { if ($this->getParameter("prev") === "true") {
$sql = $user->getSQL(); $sql = $user->getSQL();
$success = $sql->delete("User")->execute(); $success = $sql->delete("User")->execute();
$msg = $sql->getLastError(); $msg = $sql->getLastError();
@ -359,9 +359,9 @@ namespace Documents\Install {
$success = true; $success = true;
$msg = $this->errorString; $msg = $this->errorString;
if($this->getParameter("skip") === "true") { if ($this->getParameter("skip") === "true") {
$req = new \Api\Settings\Set($user); $req = new \Api\Settings\Set($user);
$success = $req->execute(array("settings" => array( "mail_enabled" => "0" ))); $success = $req->execute(array("settings" => array("mail_enabled" => "0")));
$msg = $req->getLastError(); $msg = $req->getLastError();
} else { } else {
@ -372,30 +372,30 @@ namespace Documents\Install {
$success = true; $success = true;
$missingInputs = array(); $missingInputs = array();
if(is_null($address) || empty($address)) { if (is_null($address) || empty($address)) {
$success = false; $success = false;
$missingInputs[] = "SMTP Address"; $missingInputs[] = "SMTP Address";
} }
if(is_null($port) || empty($port)) { if (is_null($port) || empty($port)) {
$success = false; $success = false;
$missingInputs[] = "Port"; $missingInputs[] = "Port";
} }
if(is_null($username) || empty($username)) { if (is_null($username) || empty($username)) {
$success = false; $success = false;
$missingInputs[] = "Username"; $missingInputs[] = "Username";
} }
if(is_null($password)) { if (is_null($password)) {
$success = false; $success = false;
$missingInputs[] = "Password"; $missingInputs[] = "Password";
} }
if(!$success) { if (!$success) {
$msg = "Please fill out the following inputs:<br>" . $msg = "Please fill out the following inputs:<br>" .
$this->createUnorderedList($missingInputs); $this->createUnorderedList($missingInputs);
} else if(!is_numeric($port) || ($port = intval($port)) < 1 || $port > 65535) { } else if (!is_numeric($port) || ($port = intval($port)) < 1 || $port > 65535) {
$msg = "Port must be in range of 1-65535."; $msg = "Port must be in range of 1-65535.";
$success = false; $success = false;
} else { } else {
@ -413,7 +413,7 @@ namespace Documents\Install {
try { try {
$success = $mail->SmtpConnect(); $success = $mail->SmtpConnect();
if(!$success) { if (!$success) {
$error = empty($mail->ErrorInfo) ? "Unknown Error" : $mail->ErrorInfo; $error = empty($mail->ErrorInfo) ? "Unknown Error" : $mail->ErrorInfo;
$msg = "Could not connect to SMTP Server: $error"; $msg = "Could not connect to SMTP Server: $error";
} else { } else {
@ -421,11 +421,11 @@ namespace Documents\Install {
$msg = ""; $msg = "";
$mail->smtpClose(); $mail->smtpClose();
} }
} catch(Exception $error) { } catch (Exception $error) {
$msg = "Could not connect to SMTP Server: " . $error->errorMessage(); $msg = "Could not connect to SMTP Server: " . $error->errorMessage();
} }
if($success) { if ($success) {
$req = new \Api\Settings\Set($user); $req = new \Api\Settings\Set($user);
$success = $req->execute(array("settings" => array( $success = $req->execute(array("settings" => array(
"mail_enabled" => "1", "mail_enabled" => "1",
@ -442,9 +442,9 @@ namespace Documents\Install {
return array("success" => $success, "msg" => $msg); return array("success" => $success, "msg" => $msg);
} }
private function performStep() { private function performStep(): array {
switch($this->currentStep) { switch ($this->currentStep) {
case self::CHECKING_REQUIREMENTS: case self::CHECKING_REQUIREMENTS:
return $this->checkRequirements(); return $this->checkRequirements();
@ -466,15 +466,15 @@ namespace Documents\Install {
} }
} }
private function createProgressSidebar() { private function createProgressSidebar(): string {
$items = array(); $items = array();
foreach($this->steps as $num => $step) { foreach ($this->steps as $num => $step) {
$title = $step["title"]; $title = $step["title"];
$status = $step["status"]; $status = $step["status"];
$currentStep = ($num == $this->currentStep) ? " id=\"currentStep\"" : ""; $currentStep = ($num == $this->currentStep) ? " id=\"currentStep\"" : "";
switch($status) { switch ($status) {
case self::PENDING: case self::PENDING:
$statusIcon = $this->createIcon("spinner"); $statusIcon = $this->createIcon("spinner");
$statusText = "Loading…"; $statusText = "Loading…";
@ -514,7 +514,7 @@ namespace Documents\Install {
return implode("", $items); return implode("", $items);
} }
private function createFormItem($formItem, $inline=false) { private function createFormItem($formItem, $inline = false): string {
$title = $formItem["title"]; $title = $formItem["title"];
$name = $formItem["name"]; $name = $formItem["name"];
@ -526,37 +526,37 @@ namespace Documents\Install {
"class" => "form-control" "class" => "form-control"
); );
if(isset($formItem["required"]) && $formItem["required"]) { if (isset($formItem["required"]) && $formItem["required"]) {
$attributes["required"] = ""; $attributes["required"] = "";
} }
if ($type !== "select") { if ($type !== "select") {
$attributes["type"] = $type; $attributes["type"] = $type;
if(isset($formItem["value"]) && $formItem["value"]) { if (isset($formItem["value"]) && $formItem["value"]) {
$attributes["value"] = $formItem["value"]; $attributes["value"] = $formItem["value"];
} }
if($type === "number") { if ($type === "number") {
if(isset($formItem["min"]) && is_numeric($formItem["min"])) if (isset($formItem["min"]) && is_numeric($formItem["min"]))
$attributes["min"] = $formItem["min"]; $attributes["min"] = $formItem["min"];
if(isset($formItem["max"]) && is_numeric($formItem["max"])) if (isset($formItem["max"]) && is_numeric($formItem["max"]))
$attributes["max"] = $formItem["max"]; $attributes["max"] = $formItem["max"];
if(isset($formItem["step"]) && is_numeric($formItem["step"])) if (isset($formItem["step"]) && is_numeric($formItem["step"]))
$attributes["step"] = $formItem["step"]; $attributes["step"] = $formItem["step"];
} }
} }
$replacements = array("+" => " ", "&" => "\" ", "=" => "=\""); $replacements = array("+" => " ", "&" => "\" ", "=" => "=\"");
$attributes = http_build_query($attributes) . "\""; $attributes = http_build_query($attributes) . "\"";
foreach($replacements as $key => $val) { foreach ($replacements as $key => $val) {
$attributes = str_replace($key, $val, $attributes); $attributes = str_replace($key, $val, $attributes);
} }
if ($type === "select") { if ($type === "select") {
$items = $formItem["items"] ?? array(); $items = $formItem["items"] ?? array();
$element = "<select $attributes>"; $element = "<select $attributes>";
foreach($items as $key => $val) { foreach ($items as $key => $val) {
$element .= "<option value=\"$key\">$val</option>"; $element .= "<option value=\"$key\">$val</option>";
} }
$element .= "</select>"; $element .= "</select>";
@ -564,7 +564,7 @@ namespace Documents\Install {
$element = "<input $attributes>"; $element = "<input $attributes>";
} }
if(!$inline) { if (!$inline) {
return return
"<div class=\"d-block my-3\"> "<div class=\"d-block my-3\">
<label for=\"$name\">$title</label> <label for=\"$name\">$title</label>
@ -579,7 +579,7 @@ namespace Documents\Install {
} }
} }
private function createProgessMainview() { private function createProgessMainview(): string {
$views = array( $views = array(
self::CHECKING_REQUIREMENTS => array( self::CHECKING_REQUIREMENTS => array(
@ -646,7 +646,7 @@ namespace Documents\Install {
) )
); );
if(!isset($views[$this->currentStep])) { if (!isset($views[$this->currentStep])) {
return ""; return "";
} }
@ -657,24 +657,24 @@ namespace Documents\Install {
$html = "<h4 class=\"mb-3\">$title</h4><hr class=\"mb-4\">"; $html = "<h4 class=\"mb-3\">$title</h4><hr class=\"mb-4\">";
if(isset($currentView["text"])) { if (isset($currentView["text"])) {
$text = $currentView["text"]; $text = $currentView["text"];
$html .= "<div class=\"my-3\">$text</i></div>"; $html .= "<div class=\"my-3\">$text</i></div>";
} }
if(isset($currentView["progressText"])) { if (isset($currentView["progressText"])) {
$progressText = $currentView["progressText"]; $progressText = $currentView["progressText"];
$html .= "<div id=\"progressText\" style=\"display:none\" class=\"my-3\">$progressText$spinnerIcon</i></div>"; $html .= "<div id=\"progressText\" style=\"display:none\" class=\"my-3\">$progressText$spinnerIcon</i></div>";
} }
if(isset($currentView["form"])) { if (isset($currentView["form"])) {
$html .= "<form id=\"installForm\">"; $html .= "<form id=\"installForm\">";
foreach($currentView["form"] as $formItem) { foreach ($currentView["form"] as $formItem) {
if($formItem["type"] === "row") { if ($formItem["type"] === "row") {
$html .= "<div class=\"row\">"; $html .= "<div class=\"row\">";
foreach($formItem["items"] as $item) { foreach ($formItem["items"] as $item) {
$html .= $this->createFormItem($item, true); $html .= $this->createFormItem($item, true);
} }
$html .= "</div>"; $html .= "</div>";
@ -691,7 +691,7 @@ namespace Documents\Install {
array("title" => "Go Back", "type" => "info", "id" => "btnPrev", "float" => "left", "disabled" => $prevDisabled) array("title" => "Go Back", "type" => "info", "id" => "btnPrev", "float" => "left", "disabled" => $prevDisabled)
); );
if($this->currentStep != self::FINISH_INSTALLATION) { if ($this->currentStep != self::FINISH_INSTALLATION) {
if ($this->currentStep == self::CHECKING_REQUIREMENTS) { if ($this->currentStep == self::CHECKING_REQUIREMENTS) {
$buttons[] = array("title" => "Retry", "type" => "success", "id" => "btnRetry", "float" => "right"); $buttons[] = array("title" => "Retry", "type" => "success", "id" => "btnRetry", "float" => "right");
} else { } else {
@ -701,14 +701,14 @@ namespace Documents\Install {
$buttons[] = array("title" => "Finish", "type" => "success", "id" => "btnFinish", "float" => "right"); $buttons[] = array("title" => "Finish", "type" => "success", "id" => "btnFinish", "float" => "right");
} }
if(isset($currentView["skip"])) { if (isset($currentView["skip"])) {
$buttons[] = array("title" => "Skip", "type" => "secondary", "id" => "btnSkip", "float" => "right"); $buttons[] = array("title" => "Skip", "type" => "secondary", "id" => "btnSkip", "float" => "right");
} }
$buttonsLeft = ""; $buttonsLeft = "";
$buttonsRight = ""; $buttonsRight = "";
foreach($buttons as $button) { foreach ($buttons as $button) {
$title = $button["title"]; $title = $button["title"];
$type = $button["type"]; $type = $button["type"];
$id = $button["id"]; $id = $button["id"];
@ -716,7 +716,7 @@ namespace Documents\Install {
$disabled = (isset($button["disabled"]) && $button["disabled"]) ? " disabled" : ""; $disabled = (isset($button["disabled"]) && $button["disabled"]) ? " disabled" : "";
$button = "<button type=\"button\" id=\"$id\" class=\"btn btn-$type m-1\"$disabled>$title</button>"; $button = "<button type=\"button\" id=\"$id\" class=\"btn btn-$type m-1\"$disabled>$title</button>";
if($float === "left") { if ($float === "left") {
$buttonsLeft .= $button; $buttonsLeft .= $button;
} else { } else {
$buttonsRight .= $button; $buttonsRight .= $button;
@ -732,7 +732,7 @@ namespace Documents\Install {
return $html; return $html;
} }
function getCode() { function getCode(): string {
$html = parent::getCode(); $html = parent::getCode();
$this->steps = array( $this->steps = array(
@ -761,16 +761,16 @@ namespace Documents\Install {
$this->currentStep = $this->getCurrentStep(); $this->currentStep = $this->getCurrentStep();
// set status // set status
for($step = self::CHECKING_REQUIREMENTS; $step < $this->currentStep; $step++) { for ($step = self::CHECKING_REQUIREMENTS; $step < $this->currentStep; $step++) {
$this->steps[$step]["status"] = self::SUCCESSFUL; $this->steps[$step]["status"] = self::SUCCESSFUL;
} }
if($this->currentStep == self::FINISH_INSTALLATION) { if ($this->currentStep == self::FINISH_INSTALLATION) {
$this->steps[$this->currentStep]["status"] = self::SUCCESSFUL; $this->steps[$this->currentStep]["status"] = self::SUCCESSFUL;
} }
// POST // POST
if($_SERVER['REQUEST_METHOD'] == 'POST') { if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$response = $this->performStep(); $response = $this->performStep();
$response["step"] = $this->currentStep; $response["step"] = $this->currentStep;
die(json_encode($response)); die(json_encode($response));

@ -4,7 +4,7 @@ namespace Driver\SQL\Column;
class BoolColumn extends Column { class BoolColumn extends Column {
public function __construct($name, $defaultValue=false) { public function __construct(string $name, bool $defaultValue = false) {
parent::__construct($name, false, $defaultValue); parent::__construct($name, false, $defaultValue);
} }

@ -8,14 +8,14 @@ class Column {
private bool $nullable; private bool $nullable;
private $defaultValue; private $defaultValue;
public function __construct($name, $nullable = false, $defaultValue = NULL) { public function __construct(string $name, bool $nullable = false, $defaultValue = NULL) {
$this->name = $name; $this->name = $name;
$this->nullable = $nullable; $this->nullable = $nullable;
$this->defaultValue = $defaultValue; $this->defaultValue = $defaultValue;
} }
public function getName() { return $this->name; } public function getName(): string { return $this->name; }
public function notNull() { return !$this->nullable; } public function notNull(): bool { return !$this->nullable; }
public function getDefaultValue() { return $this->defaultValue; } public function getDefaultValue() { return $this->defaultValue; }
} }

@ -4,7 +4,7 @@ namespace Driver\SQL\Column;
class DateTimeColumn extends Column { class DateTimeColumn extends Column {
public function __construct($name, $nullable=false, $defaultValue=NULL) { public function __construct(string $name, bool $nullable = false, $defaultValue = NULL) {
parent::__construct($name, $nullable, $defaultValue); parent::__construct($name, $nullable, $defaultValue);
} }
} }

@ -6,10 +6,10 @@ class EnumColumn extends Column {
private array $values; private array $values;
public function __construct($name, $values, $nullable=false, $defaultValue=NULL) { public function __construct(string $name, array $values, bool $nullable = false, $defaultValue = NULL) {
parent::__construct($name, $nullable, $defaultValue); parent::__construct($name, $nullable, $defaultValue);
$this->values = $values; $this->values = $values;
} }
public function getValues() { return $this->values; } public function getValues(): array { return $this->values; }
} }

@ -4,7 +4,7 @@ namespace Driver\SQL\Column;
class IntColumn extends Column { class IntColumn extends Column {
public function __construct($name, $nullable=false, $defaultValue=NULL) { public function __construct(string $name, bool $nullable = false, $defaultValue = NULL) {
parent::__construct($name, $nullable, $defaultValue); parent::__construct($name, $nullable, $defaultValue);
} }

@ -4,7 +4,7 @@ namespace Driver\SQL\Column;
class JsonColumn extends Column { class JsonColumn extends Column {
public function __construct($name, $nullable=false, $defaultValue=null) { public function __construct(string $name, bool $nullable = false, $defaultValue = null) {
parent::__construct($name, $nullable, $defaultValue); parent::__construct($name, $nullable, $defaultValue);
} }

@ -4,7 +4,7 @@ namespace Driver\SQL\Column;
class SerialColumn extends Column { class SerialColumn extends Column {
public function __construct($name, $defaultValue=NULL) { public function __construct(string $name, $defaultValue = NULL) {
parent::__construct($name, false, $defaultValue); # not nullable parent::__construct($name, false, $defaultValue); # not nullable
} }

@ -6,10 +6,10 @@ class StringColumn extends Column {
private ?int $maxSize; private ?int $maxSize;
public function __construct($name, $maxSize=null, $nullable=false, $defaultValue=null) { public function __construct(string $name, ?int $maxSize = null, bool $nullable = false, $defaultValue = null) {
parent::__construct($name, $nullable, $defaultValue); parent::__construct($name, $nullable, $defaultValue);
$this->maxSize = $maxSize; $this->maxSize = $maxSize;
} }
public function getMaxSize() { return $this->maxSize; } public function getMaxSize(): ?int { return $this->maxSize; }
} }

@ -8,14 +8,14 @@ class Compare extends Condition {
private string $column; private string $column;
private $value; private $value;
public function __construct($col, $val, $operator='=') { public function __construct(string $col, $val, string $operator = '=') {
$this->operator = $operator; $this->operator = $operator;
$this->column = $col; $this->column = $col;
$this->value = $val; $this->value = $val;
} }
public function getColumn() { return $this->column; } public function getColumn(): string { return $this->column; }
public function getValue() { return $this->value; } public function getValue() { return $this->value; }
public function getOperator() { return $this->operator; } public function getOperator(): string { return $this->operator; }
} }

@ -10,5 +10,5 @@ class CondAnd extends Condition {
$this->conditions = $conditions; $this->conditions = $conditions;
} }
public function getConditions() { return $this->conditions; } public function getConditions(): array { return $this->conditions; }
} }

@ -12,6 +12,6 @@ class CondIn extends Condition {
$this->expression = $expression; $this->expression = $expression;
} }
public function getColumn() { return $this->column; } public function getColumn(): string { return $this->column; }
public function getExpression() { return $this->expression; } public function getExpression() { return $this->expression; }
} }

@ -8,7 +8,7 @@ abstract class CondKeyword extends Condition {
private $rightExpression; private $rightExpression;
private string $keyword; private string $keyword;
public function __construct($keyword, $leftExpression, $rightExpression) { public function __construct(string $keyword, $leftExpression, $rightExpression) {
$this->leftExpression = $leftExpression; $this->leftExpression = $leftExpression;
$this->rightExpression = $rightExpression; $this->rightExpression = $rightExpression;
$this->keyword = $keyword; $this->keyword = $keyword;
@ -16,5 +16,5 @@ abstract class CondKeyword extends Condition {
public function getLeftExp() { return $this->leftExpression; } public function getLeftExp() { return $this->leftExpression; }
public function getRightExp() { return $this->rightExpression; } public function getRightExp() { return $this->rightExpression; }
public function getKeyword() { return $this->keyword; } public function getKeyword(): string { return $this->keyword; }
} }

@ -10,5 +10,5 @@ class CondNull extends Condition {
$this->column = $col; $this->column = $col;
} }
public function getColumn() { return $this->column; } public function getColumn(): string { return $this->column; }
} }

@ -10,5 +10,5 @@ class CondOr extends Condition {
$this->conditions = (!empty($conditions) && is_array($conditions[0])) ? $conditions[0] : $conditions; $this->conditions = (!empty($conditions) && is_array($conditions[0])) ? $conditions[0] : $conditions;
} }
public function getConditions() { return $this->conditions; } public function getConditions(): array { return $this->conditions; }
} }

@ -10,5 +10,5 @@ abstract class Constraint {
$this->columnNames = (!is_array($columnNames) ? array($columnNames) : $columnNames); $this->columnNames = (!is_array($columnNames) ? array($columnNames) : $columnNames);
} }
public function getColumnNames() { return $this->columnNames; } public function getColumnNames(): array { return $this->columnNames; }
} }

@ -10,14 +10,14 @@ class ForeignKey extends Constraint {
private string $referencedColumn; private string $referencedColumn;
private ?Strategy $strategy; private ?Strategy $strategy;
public function __construct($name, $refTable, $refColumn, $strategy = NULL) { public function __construct(string $name, string $refTable, string $refColumn, ?Strategy $strategy = NULL) {
parent::__construct($name); parent::__construct($name);
$this->referencedTable = $refTable; $this->referencedTable = $refTable;
$this->referencedColumn = $refColumn; $this->referencedColumn = $refColumn;
$this->strategy = $strategy; $this->strategy = $strategy;
} }
public function getReferencedTable() { return $this->referencedTable; } public function getReferencedTable(): string { return $this->referencedTable; }
public function getReferencedColumn() { return $this->referencedColumn; } public function getReferencedColumn(): string { return $this->referencedColumn; }
public function onDelete() { return $this->strategy; } public function onDelete(): ?Strategy { return $this->strategy; }
} }

@ -4,9 +4,10 @@ namespace Driver\SQL\Expression;
use Driver\SQL\Condition\Compare; use Driver\SQL\Condition\Compare;
# TODO: change confusing class inheritance here
class Add extends Compare { class Add extends Compare {
public function __construct($col, $val) { public function __construct(string $col, $val) {
parent::__construct($col, $val, "+"); parent::__construct($col, $val, "+");
} }

@ -8,9 +8,9 @@ class Join {
private string $table; private string $table;
private string $columnA; private string $columnA;
private string $columnB; private string $columnB;
private $tableAlias; private ?string $tableAlias;
public function __construct($type, $table, $columnA, $columnB, $tableAlias=null) { public function __construct(string $type, string $table, string $columnA, string $columnB, ?string $tableAlias = null) {
$this->type = $type; $this->type = $type;
$this->table = $table; $this->table = $table;
$this->columnA = $columnA; $this->columnA = $columnA;
@ -18,10 +18,10 @@ class Join {
$this->tableAlias = $tableAlias; $this->tableAlias = $tableAlias;
} }
public function getType() { return $this->type; } public function getType(): string { return $this->type; }
public function getTable() { return $this->table; } public function getTable(): string { return $this->table; }
public function getColumnA() { return $this->columnA; } public function getColumnA(): string { return $this->columnA; }
public function getColumnB() { return $this->columnB; } public function getColumnB(): string { return $this->columnB; }
public function getTableAlias() { return $this->tableAlias; } public function getTableAlias(): ?string { return $this->tableAlias; }
} }

@ -6,10 +6,10 @@ class Keyword {
private string $value; private string $value;
public function __construct($value) { public function __construct(string $value) {
$this->value = $value; $this->value = $value;
} }
public function getValue() { return $this->value; } public function getValue(): string { return $this->value; }
} }

@ -0,0 +1,64 @@
<?php
namespace Driver\SQL\Query;
use Driver\SQL\Column\Column;
use Driver\SQL\Constraint\Constraint;
use Driver\SQL\SQL;
class AlterTable extends Query {
private string $table;
private string $action;
private ?Column $column;
private ?Constraint $constraint;
public function __construct(SQL $sql, string $table) {
parent::__construct($sql);
$this->table = $table;
$this->column = null;
$this->constraint = null;
}
public function add($what): AlterTable {
if ($what instanceof Column) {
$this->column = $what;
} else if ($what instanceof Constraint) {
$this->constraint = $what;
} else {
$this->column = new Column($what);
}
$this->action = "ADD";
return $this;
}
public function modify(Column $column): AlterTable {
$this->column = $column;
$this->action = "MODIFY";
return $this;
}
public function drop($what): AlterTable {
if ($what instanceof Column) {
$this->column = $what;
} else if ($what instanceof Constraint) {
$this->constraint = $what;
} else {
$this->column = new Column($what);
}
$this->action = "DROP";
return $this;
}
public function execute(): bool {
return $this->sql->executeAlter($this);
}
public function getAction(): string { return $this->action; }
public function getColumn(): ?Column { return $this->column; }
public function getConstraint(): ?Constraint { return $this->constraint; }
public function getTable(): string { return $this->table; }
}

@ -13,6 +13,8 @@ use Driver\SQL\Column\JsonColumn;
use Driver\SQL\Constraint\PrimaryKey; use Driver\SQL\Constraint\PrimaryKey;
use Driver\SQL\Constraint\Unique; use Driver\SQL\Constraint\Unique;
use Driver\SQL\Constraint\ForeignKey; use Driver\SQL\Constraint\ForeignKey;
use Driver\SQL\SQL;
use Driver\SQL\Strategy\Strategy;
class CreateTable extends Query { class CreateTable extends Query {
@ -21,7 +23,7 @@ class CreateTable extends Query {
private array $constraints; private array $constraints;
private bool $ifNotExists; private bool $ifNotExists;
public function __construct($sql, $name) { public function __construct(SQL $sql, string $name) {
parent::__construct($sql); parent::__construct($sql);
$this->tableName = $name; $this->tableName = $name;
$this->columns = array(); $this->columns = array();
@ -29,67 +31,67 @@ class CreateTable extends Query {
$this->ifNotExists = false; $this->ifNotExists = false;
} }
public function addSerial($name) { public function addSerial(string $name): CreateTable {
$this->columns[$name] = new SerialColumn($name); $this->columns[$name] = new SerialColumn($name);
return $this; return $this;
} }
public function addString($name, $maxSize=NULL, $nullable=false, $defaultValue=NULL) { public function addString(string $name, ?int $maxSize = NULL, bool $nullable = false, $defaultValue = NULL): CreateTable {
$this->columns[$name] = new StringColumn($name, $maxSize, $nullable, $defaultValue); $this->columns[$name] = new StringColumn($name, $maxSize, $nullable, $defaultValue);
return $this; return $this;
} }
public function addDateTime($name, $nullable=false, $defaultValue=NULL) { public function addDateTime(string $name, bool $nullable = false, $defaultValue = NULL): CreateTable {
$this->columns[$name] = new DateTimeColumn($name, $nullable, $defaultValue); $this->columns[$name] = new DateTimeColumn($name, $nullable, $defaultValue);
return $this; return $this;
} }
public function addInt($name, $nullable=false, $defaultValue=NULL) { public function addInt(string $name, bool $nullable = false, $defaultValue = NULL): CreateTable {
$this->columns[$name] = new IntColumn($name, $nullable, $defaultValue); $this->columns[$name] = new IntColumn($name, $nullable, $defaultValue);
return $this; return $this;
} }
public function addBool($name, $defaultValue=false) { public function addBool(string $name, $defaultValue = false): CreateTable {
$this->columns[$name] = new BoolColumn($name, $defaultValue); $this->columns[$name] = new BoolColumn($name, $defaultValue);
return $this; return $this;
} }
public function addJson($name, $nullable=false, $defaultValue=NULL) { public function addJson(string $name, bool $nullable = false, $defaultValue = NULL): CreateTable {
$this->columns[$name] = new JsonColumn($name, $nullable, $defaultValue); $this->columns[$name] = new JsonColumn($name, $nullable, $defaultValue);
return $this; return $this;
} }
public function addEnum($name, $values, $nullable=false, $defaultValue=NULL) { public function addEnum(string $name, array $values, bool $nullable = false, $defaultValue = NULL): CreateTable {
$this->columns[$name] = new EnumColumn($name, $values, $nullable, $defaultValue); $this->columns[$name] = new EnumColumn($name, $values, $nullable, $defaultValue);
return $this; return $this;
} }
public function primaryKey(...$names) { public function primaryKey(...$names): CreateTable {
$this->constraints[] = new PrimaryKey($names); $this->constraints[] = new PrimaryKey($names);
return $this; return $this;
} }
public function unique(...$names) { public function unique(...$names): CreateTable {
$this->constraints[] = new Unique($names); $this->constraints[] = new Unique($names);
return $this; return $this;
} }
public function foreignKey($name, $refTable, $refColumn, $strategy = NULL) { public function foreignKey(string $name, string $refTable, string $refColumn, ?Strategy $strategy = NULL): CreateTable {
$this->constraints[] = new ForeignKey($name, $refTable, $refColumn, $strategy); $this->constraints[] = new ForeignKey($name, $refTable, $refColumn, $strategy);
return $this; return $this;
} }
public function onlyIfNotExists() { public function onlyIfNotExists(): CreateTable {
$this->ifNotExists = true; $this->ifNotExists = true;
return $this; return $this;
} }
public function execute() { public function execute(): bool {
return $this->sql->executeCreateTable($this); return $this->sql->executeCreateTable($this);
} }
public function ifNotExists() { return $this->ifNotExists; } public function ifNotExists(): bool { return $this->ifNotExists; }
public function getTableName() { return $this->tableName; } public function getTableName(): string { return $this->tableName; }
public function getColumns() { return $this->columns; } public function getColumns(): array { return $this->columns; }
public function getConstraints() { return $this->constraints; } public function getConstraints(): array { return $this->constraints; }
} }

@ -3,27 +3,28 @@
namespace Driver\SQL\Query; namespace Driver\SQL\Query;
use Driver\SQL\Condition\CondOr; use Driver\SQL\Condition\CondOr;
use Driver\SQL\SQL;
class Delete extends Query { class Delete extends Query {
private string $table; private string $table;
private array $conditions; private array $conditions;
public function __construct($sql, $table) { public function __construct(SQL $sql, string $table) {
parent::__construct($sql); parent::__construct($sql);
$this->table = $table; $this->table = $table;
$this->conditions = array(); $this->conditions = array();
} }
public function where(...$conditions) { public function where(...$conditions): Delete {
$this->conditions[] = (count($conditions) === 1 ? $conditions : new CondOr($conditions)); $this->conditions[] = (count($conditions) === 1 ? $conditions : new CondOr($conditions));
return $this; return $this;
} }
public function execute() { public function execute(): bool {
return $this->sql->executeDelete($this); return $this->sql->executeDelete($this);
} }
public function getTable() { return $this->table; } public function getTable(): string { return $this->table; }
public function getConditions() { return $this->conditions; } public function getConditions(): array { return $this->conditions; }
} }

@ -14,16 +14,16 @@ class Drop extends Query {
* @param SQL $sql * @param SQL $sql
* @param string $table * @param string $table
*/ */
public function __construct(\Driver\SQL\SQL $sql, string $table) { public function __construct(SQL $sql, string $table) {
parent::__construct($sql); parent::__construct($sql);
$this->table = $table; $this->table = $table;
} }
public function execute() { public function execute(): bool {
$this->sql->executeDrop($this); return $this->sql->executeDrop($this);
} }
public function getTable() { public function getTable(): string {
return $this->table; return $this->table;
} }
} }

@ -2,6 +2,7 @@
namespace Driver\SQL\Query; namespace Driver\SQL\Query;
use Driver\SQL\SQL;
use Driver\SQL\Strategy\Strategy; use Driver\SQL\Strategy\Strategy;
class Insert extends Query { class Insert extends Query {
@ -12,7 +13,7 @@ class Insert extends Query {
private ?Strategy $onDuplicateKey; private ?Strategy $onDuplicateKey;
private ?string $returning; private ?string $returning;
public function __construct($sql, $name, $columns=array()) { public function __construct(SQL $sql, string $name, array $columns = array()) {
parent::__construct($sql); parent::__construct($sql);
$this->tableName = $name; $this->tableName = $name;
$this->columns = $columns; $this->columns = $columns;
@ -21,28 +22,28 @@ class Insert extends Query {
$this->returning = NULL; $this->returning = NULL;
} }
public function addRow(...$values) { public function addRow(...$values): Insert {
$this->rows[] = $values; $this->rows[] = $values;
return $this; return $this;
} }
public function onDuplicateKeyStrategy($strategy) { public function onDuplicateKeyStrategy(Strategy $strategy): Insert {
$this->onDuplicateKey = $strategy; $this->onDuplicateKey = $strategy;
return $this; return $this;
} }
public function returning($column) { public function returning(string $column): Insert {
$this->returning = $column; $this->returning = $column;
return $this; return $this;
} }
public function execute() { public function execute(): bool {
return $this->sql->executeInsert($this); return $this->sql->executeInsert($this);
} }
public function getTableName() { return $this->tableName; } public function getTableName(): string { return $this->tableName; }
public function getColumns() { return $this->columns; } public function getColumns(): array { return $this->columns; }
public function getRows() { return $this->rows; } public function getRows(): array { return $this->rows; }
public function onDuplicateKey() { return $this->onDuplicateKey; } public function onDuplicateKey(): ?Strategy { return $this->onDuplicateKey; }
public function getReturning() { return $this->returning; } public function getReturning(): ?string { return $this->returning; }
} }

@ -9,16 +9,17 @@ abstract class Query {
protected SQL $sql; protected SQL $sql;
public bool $dump; public bool $dump;
public function __construct($sql) { public function __construct(SQL $sql) {
$this->sql = $sql; $this->sql = $sql;
$this->dump = false; $this->dump = false;
} }
public function dump() { public function dump(): Query {
$this->dump = true; $this->dump = true;
return $this; return $this;
} }
// can actually return bool|array (depending on success and query type)
public abstract function execute(); public abstract function execute();
} }

@ -30,52 +30,52 @@ class Select extends Query {
$this->sortAscending = true; $this->sortAscending = true;
} }
public function from(...$tables) { public function from(...$tables): Select {
$this->tables = array_merge($this->tables, $tables); $this->tables = array_merge($this->tables, $tables);
return $this; return $this;
} }
public function where(...$conditions) { public function where(...$conditions): Select {
$this->conditions[] = (count($conditions) === 1 ? $conditions : new CondOr($conditions)); $this->conditions[] = (count($conditions) === 1 ? $conditions : new CondOr($conditions));
return $this; return $this;
} }
public function innerJoin($table, $columnA, $columnB, $tableAlias=null) { public function innerJoin(string $table, string $columnA, string $columnB, ?string $tableAlias = null): Select {
$this->joins[] = new Join("INNER", $table, $columnA, $columnB, $tableAlias); $this->joins[] = new Join("INNER", $table, $columnA, $columnB, $tableAlias);
return $this; return $this;
} }
public function leftJoin($table, $columnA, $columnB, $tableAlias=null) { public function leftJoin(string $table, string $columnA, string $columnB, ?string $tableAlias = null): Select {
$this->joins[] = new Join("LEFT", $table, $columnA, $columnB, $tableAlias); $this->joins[] = new Join("LEFT", $table, $columnA, $columnB, $tableAlias);
return $this; return $this;
} }
public function groupBy(...$columns) { public function groupBy(...$columns): Select {
$this->groupColumns = $columns; $this->groupColumns = $columns;
return $this; return $this;
} }
public function orderBy(...$columns) { public function orderBy(...$columns): Select {
$this->orderColumns = $columns; $this->orderColumns = $columns;
return $this; return $this;
} }
public function ascending() { public function ascending(): Select {
$this->sortAscending = true; $this->sortAscending = true;
return $this; return $this;
} }
public function descending() { public function descending(): Select {
$this->sortAscending = false; $this->sortAscending = false;
return $this; return $this;
} }
public function limit($limit) { public function limit(int $limit): Select {
$this->limit = $limit; $this->limit = $limit;
return $this; return $this;
} }
public function offset($offset) { public function offset(int $offset): Select {
$this->offset = $offset; $this->offset = $offset;
return $this; return $this;
} }
@ -84,14 +84,14 @@ class Select extends Query {
return $this->sql->executeSelect($this); return $this->sql->executeSelect($this);
} }
public function getColumns() { return $this->columns; } public function getColumns(): array { return $this->columns; }
public function getTables() { return $this->tables; } public function getTables(): array { return $this->tables; }
public function getConditions() { return $this->conditions; } public function getConditions(): array { return $this->conditions; }
public function getJoins() { return $this->joins; } public function getJoins(): array { return $this->joins; }
public function isOrderedAscending() { return $this->sortAscending; } public function isOrderedAscending(): bool { return $this->sortAscending; }
public function getOrderBy() { return $this->orderColumns; } public function getOrderBy(): array { return $this->orderColumns; }
public function getLimit() { return $this->limit; } public function getLimit(): int { return $this->limit; }
public function getOffset() { return $this->offset; } public function getOffset(): int { return $this->offset; }
public function getGroupBy() { return $this->groupColumns; } public function getGroupBy(): array { return $this->groupColumns; }
} }

@ -2,18 +2,20 @@
namespace Driver\SQL\Query; namespace Driver\SQL\Query;
use Driver\SQL\SQL;
class Truncate extends Query { class Truncate extends Query {
private string $tableName; private string $tableName;
public function __construct($sql, $name) { public function __construct(SQL $sql, string $name) {
parent::__construct($sql); parent::__construct($sql);
$this->tableName = $name; $this->tableName = $name;
} }
public function execute() { public function execute(): bool {
return $this->sql->executeTruncate($this); return $this->sql->executeTruncate($this);
} }
public function getTable() { return $this->tableName; } public function getTable(): string { return $this->tableName; }
} }

@ -3,6 +3,7 @@
namespace Driver\SQL\Query; namespace Driver\SQL\Query;
use Driver\SQL\Condition\CondOr; use Driver\SQL\Condition\CondOr;
use Driver\SQL\SQL;
class Update extends Query { class Update extends Query {
@ -10,19 +11,19 @@ class Update extends Query {
private string $table; private string $table;
private array $conditions; private array $conditions;
public function __construct($sql, $table) { public function __construct(SQL $sql, string $table) {
parent::__construct($sql); parent::__construct($sql);
$this->values = array(); $this->values = array();
$this->table = $table; $this->table = $table;
$this->conditions = array(); $this->conditions = array();
} }
public function where(...$conditions) { public function where(...$conditions): Update {
$this->conditions[] = (count($conditions) === 1 ? $conditions : new CondOr($conditions)); $this->conditions[] = (count($conditions) === 1 ? $conditions : new CondOr($conditions));
return $this; return $this;
} }
public function set($key, $val) { public function set(string $key, $val): Update {
$this->values[$key] = $val; $this->values[$key] = $val;
return $this; return $this;
} }
@ -31,7 +32,7 @@ class Update extends Query {
return $this->sql->executeUpdate($this); return $this->sql->executeUpdate($this);
} }
public function getTable() { return $this->table; } public function getTable(): string { return $this->table; }
public function getConditions() { return $this->conditions; } public function getConditions(): array { return $this->conditions; }
public function getValues() { return $this->values; } public function getValues(): array { return $this->values; }
} }

@ -15,6 +15,7 @@ use Driver\SQL\Constraint\Constraint;
use \Driver\SQL\Constraint\Unique; use \Driver\SQL\Constraint\Unique;
use \Driver\SQL\Constraint\PrimaryKey; use \Driver\SQL\Constraint\PrimaryKey;
use \Driver\SQL\Constraint\ForeignKey; use \Driver\SQL\Constraint\ForeignKey;
use Driver\SQL\Query\AlterTable;
use Driver\SQL\Query\CreateTable; use Driver\SQL\Query\CreateTable;
use Driver\SQL\Query\Delete; use Driver\SQL\Query\Delete;
use Driver\SQL\Query\Drop; use Driver\SQL\Query\Drop;
@ -79,6 +80,10 @@ abstract class SQL {
return new Drop($this, $table); return new Drop($this, $table);
} }
public function alterTable($tableName) {
return new AlterTable($this, $tableName);
}
// #################### // ####################
// ### ABSTRACT METHODS // ### ABSTRACT METHODS
// #################### // ####################
@ -262,6 +267,47 @@ abstract class SQL {
return $this->execute($query); return $this->execute($query);
} }
public function executeAlter(AlterTable $alter): bool {
$tableName = $this->tableName($alter->getTable());
$action = $alter->getAction();
$column = $alter->getColumn();
$constraint = $alter->getConstraint();
$query = "ALTER TABLE $tableName $action ";
if ($column) {
$query .= "COLUMN ";
if ($action === "DROP") {
$query .= $this->columnName($column->getName());
} else {
// ADD or modify
$query .= $this->getColumnDefinition($column);
}
} else if ($constraint) {
if ($action === "DROP") {
if ($constraint instanceof PrimaryKey) {
$query .= "PRIMARY KEY";
} else if ($constraint instanceof ForeignKey) {
// TODO: how can we pass the constraint name here?
$this->lastError = "DROP CONSTRAINT foreign key is not supported yet.";
return false;
}
} else if ($action === "ADD") {
$query .= "CONSTRAINT ";
$query .= $this->getConstraintDefinition($constraint);
} else if ($action === "MODIFY") {
$this->lastError = "MODIFY CONSTRAINT foreign key is not supported.";
return false;
}
} else {
$this->lastError = "ALTER TABLE requires at least a column or a constraint.";
return false;
}
if ($alter->dump) { var_dump($query); }
return $this->execute($query);
}
protected function getWhereClause($conditions, &$params) { protected function getWhereClause($conditions, &$params) {
if (!$conditions) { if (!$conditions) {
return ""; return "";

@ -12,9 +12,11 @@ class UpdateStrategy extends Strategy {
$this->values = $values; $this->values = $values;
} }
public function getConflictingColumns() { public function getConflictingColumns(): array {
return $this->conflictingColumns; return $this->conflictingColumns;
} }
public function getValues() { return $this->values; } public function getValues(): array {
return $this->values;
}
} }

@ -13,29 +13,38 @@ abstract class Document {
private ?string $activeView; private ?string $activeView;
public function __construct(User $user, $headClass, $bodyClass, ?string $view = NULL) { public function __construct(User $user, $headClass, $bodyClass, ?string $view = NULL) {
$this->user = $user;
$this->head = new $headClass($this); $this->head = new $headClass($this);
$this->body = new $bodyClass($this); $this->body = new $bodyClass($this);
$this->user = $user;
$this->databaseRequired = true; $this->databaseRequired = true;
$this->activeView = $view; $this->activeView = $view;
} }
public function getHead() { return $this->head; } public function getHead(): Head { return $this->head; }
public function getBody() { return $this->body; } public function getBody(): Body { return $this->body; }
public function getSQL() { return $this->user->getSQL(); } public function getSQL(): ?\Driver\SQL\SQL { return $this->user->getSQL(); }
public function getUser() { return $this->user; } public function getUser(): User { return $this->user; }
public function getView() : ?View { public function getView() : ?View {
$file = getClassPath($this->activeView); if ($this->activeView === null) {
if(!file_exists($file) || !is_subclass_of($this->activeView, View::class)) {
return null; return null;
} }
return new $this->activeView($this); $view = parseClass($this->activeView);
$file = getClassPath($view);
if(!file_exists($file) || !is_subclass_of($view, View::class)) {
return null;
} }
function getCode() { return new $view($this);
}
public function getRequestedView(): string {
return $this->activeView;
}
function getCode(): string {
if ($this->databaseRequired) { if ($this->databaseRequired) {
$sql = $this->user->getSQL(); $sql = $this->user->getSQL();

@ -11,16 +11,16 @@ class EmptyHead extends Head {
protected function initSources() { protected function initSources() {
} }
protected function initMetas() { protected function initMetas(): array {
return array( return array(
); );
} }
protected function initRawFields() { protected function initRawFields(): array {
return array(); return array();
} }
protected function initTitle() { protected function initTitle(): string {
return ""; return "";
} }
} }

@ -24,9 +24,9 @@ abstract class Head extends View {
} }
protected abstract function initSources(); protected abstract function initSources();
protected abstract function initMetas(); protected abstract function initMetas(): array;
protected abstract function initRawFields(); protected abstract function initRawFields(): array;
protected abstract function initTitle(); protected abstract function initTitle(): string;
protected function init() { protected function init() {
$this->keywords = array(); $this->keywords = array();
@ -38,13 +38,13 @@ abstract class Head extends View {
public function setDescription($description) { $this->description = $description; } public function setDescription($description) { $this->description = $description; }
public function setKeywords($keywords) { $this->keywords = $keywords; } public function setKeywords($keywords) { $this->keywords = $keywords; }
public function setTitle($title) { $this->title = $title; } public function setTitle($title) { $this->title = $title; }
public function getSources() { return $this->sources; } public function getSources(): array { return $this->sources; }
public function addScript($type, $url, $js = '') { $this->sources[] = new Script($type, $url, $js); } public function addScript($type, $url, $js = '') { $this->sources[] = new Script($type, $url, $js); }
public function addRawField($rawField) { $this->rawFields[] = $rawField; } public function addRawField($rawField) { $this->rawFields[] = $rawField; }
public function addMeta($aMeta) { $this->metas[] = $aMeta; } public function addMeta($aMeta) { $this->metas[] = $aMeta; }
public function addLink($rel, $href, $type = "") { $this->sources[] = new Link($rel, $href, $type); } public function addLink($rel, $href, $type = "") { $this->sources[] = new Link($rel, $href, $type); }
public function addKeywords($keywords) { array_merge($this->keywords, $keywords); } public function addKeywords($keywords) { $this->keywords = array_merge($this->keywords, $keywords); }
public function getTitle() { return $this->title; } public function getTitle(): string { return $this->title; }
public function addCSS($href, $type = Link::MIME_TEXT_CSS) { $this->sources[] = new Link(Link::STYLESHEET, $href, $type); } public function addCSS($href, $type = Link::MIME_TEXT_CSS) { $this->sources[] = new Link(Link::STYLESHEET, $href, $type); }
public function addStyle($style) { $this->sources[] = new Style($style); } public function addStyle($style) { $this->sources[] = new Style($style); }
@ -68,7 +68,7 @@ abstract class Head extends View {
$this->addJS(Script::BOOTSTRAP); $this->addJS(Script::BOOTSTRAP);
} }
public function getCode() { public function getCode(): string {
$header = "<head>"; $header = "<head>";
foreach($this->metas as $aMeta) { foreach($this->metas as $aMeta) {

@ -22,7 +22,7 @@ class Link extends StaticView {
$this->rel = $rel; $this->rel = $rel;
} }
function getCode() { function getCode(): string {
$type = (empty($this->type) ? "" : " type=\"$this->type\""); $type = (empty($this->type) ? "" : " type=\"$this->type\"");
return "<link rel=\"$this->rel\" href=\"$this->href\"$type/>"; return "<link rel=\"$this->rel\" href=\"$this->href\"$type/>";
} }

@ -23,7 +23,7 @@ class Script extends StaticView {
$this->content = $content; $this->content = $content;
} }
function getCode() { function getCode(): string {
$src = (empty($this->src) ? "" : " src=\"$this->src\""); $src = (empty($this->src) ? "" : " src=\"$this->src\"");
return "<script type=\"$this->type\"$src>$this->content</script>"; return "<script type=\"$this->type\"$src>$this->content</script>";
} }

@ -3,14 +3,15 @@
namespace Elements; namespace Elements;
abstract class SimpleBody extends Body { abstract class SimpleBody extends Body {
public function __construct($document) { public function __construct($document) {
parent::__construct($document); parent::__construct($document);
} }
public function getCode() { public function getCode(): string {
$content = $this->getContent(); $content = $this->getContent();
return parent::getCode() . "<body>$content</body>"; return parent::getCode() . "<body>$content</body>";
} }
protected abstract function getContent(); protected abstract function getContent(): string;
} }

@ -10,7 +10,7 @@ class Style extends StaticView {
$this->style = $style; $this->style = $style;
} }
function getCode() { function getCode(): string {
return "<style>$this->style</style>"; return "<style>$this->style</style>";
} }
} }

@ -7,23 +7,25 @@ abstract class View extends StaticView {
private Document $document; private Document $document;
private bool $loadView; private bool $loadView;
protected bool $searchable; protected bool $searchable;
protected string $reference;
protected string $title; protected string $title;
protected array $langModules; protected array $langModules;
public function __construct(Document $document, $loadView = true) { public function __construct(Document $document, bool $loadView = true) {
$this->document = $document; $this->document = $document;
$this->searchable = false; $this->searchable = false;
$this->reference = "";
$this->title = "Untitled View"; $this->title = "Untitled View";
$this->langModules = array(); $this->langModules = array();
$this->loadView = $loadView; $this->loadView = $loadView;
} }
public function getTitle() { return $this->title; } public function getTitle(): string { return $this->title; }
public function getDocument() { return $this->document; } public function getDocument(): Document { return $this->document; }
public function isSearchable() { return $this->searchable; } public function isSearchable(): bool { return $this->searchable; }
public function getReference() { return $this->reference; }
public function getSiteName(): string {
// what a chain lol
return $this->getDocument()->getUser()->getConfiguration()->getSettings()->getSiteName();
}
protected function load(string $viewClass) : string { protected function load(string $viewClass) : string {
try { try {
@ -50,7 +52,7 @@ abstract class View extends StaticView {
// Virtual Methods // Virtual Methods
public function loadView() { } public function loadView() { }
public function getCode() { public function getCode(): string {
// Load translations // Load translations
$this->loadLanguageModules(); $this->loadLanguageModules();
@ -64,33 +66,37 @@ abstract class View extends StaticView {
} }
// UI Functions // UI Functions
private function createList($items, $tag) { private function createList($items, $tag, $classes = ""): string {
if(count($items) === 0)
return "<$tag></$tag>"; $class = ($classes ? " class=\"$classes\"" : "");
else
return "<$tag><li>" . implode("</li><li>", $items) . "</li></$tag>"; if(count($items) === 0) {
return "<$tag$class></$tag>";
} else {
return "<$tag$class><li>" . implode("</li><li>", $items) . "</li></$tag>";
}
} }
public function createOrderedList($items=array()) { public function createOrderedList($items=array(), $classes = ""): string {
return $this->createList($items, "ol"); return $this->createList($items, "ol", $classes);
} }
public function createUnorderedList($items=array()) { public function createUnorderedList($items=array(), $classes = ""): string {
return $this->createList($items, "ul"); return $this->createList($items, "ul", $classes);
} }
protected function createLink($link, $title=null, $classes="") { protected function createLink($link, $title=null, $classes=""): string {
if(is_null($title)) $title=$link; if(is_null($title)) $title=$link;
if(!empty($classes)) $classes = " class=\"$classes\""; if(!empty($classes)) $classes = " class=\"$classes\"";
return "<a href=\"$link\"$classes>$title</a>"; return "<a href=\"$link\"$classes>$title</a>";
} }
protected function createExternalLink($link, $title=null) { protected function createExternalLink($link, $title=null): string {
if(is_null($title)) $title=$link; if(is_null($title)) $title=$link;
return "<a href=\"$link\" target=\"_blank\" class=\"external\">$title</a>"; return "<a href=\"$link\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"external\">$title</a>";
} }
protected function createIcon($icon, $type = "fas", $classes = "") { protected function createIcon($icon, $type = "fas", $classes = ""): string {
$iconClass = "$type fa-$icon"; $iconClass = "$type fa-$icon";
if($icon === "spinner" || $icon === "circle-notch") if($icon === "spinner" || $icon === "circle-notch")
@ -102,58 +108,54 @@ abstract class View extends StaticView {
return "<i class=\"$iconClass\" ></i>"; return "<i class=\"$iconClass\" ></i>";
} }
protected function createErrorText($text, $id="", $hidden=false) { protected function createErrorText($text, $id="", $hidden=false): string {
return $this->createStatusText("danger", $text, $id, $hidden); return $this->createStatusText("danger", $text, $id, $hidden);
} }
protected function createWarningText($text, $id="", $hidden=false) { protected function createWarningText($text, $id="", $hidden=false): string {
return $this->createStatusText("warning", $text, $id, $hidden); return $this->createStatusText("warning", $text, $id, $hidden);
} }
protected function createSuccessText($text, $id="", $hidden=false) { protected function createSuccessText($text, $id="", $hidden=false): string {
return $this->createStatusText("success", $text, $id, $hidden); return $this->createStatusText("success", $text, $id, $hidden);
} }
protected function createSecondaryText($text, $id="", $hidden=false) { protected function createSecondaryText($text, $id="", $hidden=false): string {
return $this->createStatusText("secondary", $text, $id, $hidden); return $this->createStatusText("secondary", $text, $id, $hidden);
} }
protected function createInfoText($text, $id="", $hidden=false) { protected function createInfoText($text, $id="", $hidden=false): string {
return $this->createStatusText("info", $text, $id, $hidden); return $this->createStatusText("info", $text, $id, $hidden);
} }
protected function createStatusText($type, $text, $id="", $hidden=false, $classes="") { protected function createStatusText($type, $text, $id="", $hidden=false, $classes=""): string {
if(strlen($id) > 0) $id = " id=\"$id\""; if(strlen($id) > 0) $id = " id=\"$id\"";
if($hidden) $classes .= " hidden"; if($hidden) $classes .= " hidden";
if(strlen($classes) > 0) $classes = " $classes"; if(strlen($classes) > 0) $classes = " $classes";
return "<div class=\"alert alert-$type$hidden$classes\" role=\"alert\"$id>$text</div>"; return "<div class=\"alert alert-$type$hidden$classes\" role=\"alert\"$id>$text</div>";
} }
protected function createBadge($type, $text) { protected function createBadge($type, $text): string {
$text = htmlspecialchars($text); $text = htmlspecialchars($text);
return "<span class=\"badge badge-$type\">$text</span>"; return "<span class=\"badge badge-$type\">$text</span>";
} }
protected function createJumbotron(string $content, bool $fluid=false, $class="") { protected function createJumbotron(string $content, bool $fluid=false, $class=""): string {
$jumbotronClass = "jumbotron" . ($fluid ? "-fluid" : ""); $jumbotronClass = "jumbotron" . ($fluid ? " jumbotron-fluid" : "");
if (!empty($class)) $jumbotronClass .= " $class"; if (!empty($class)) $jumbotronClass .= " $class";
return " return
<div class=\"row\"> "<div class=\"$jumbotronClass\">
<div class=\"col-12\">
<div class=\"$jumbotronClass\">
$content $content
</div>
</div>
</div>"; </div>";
} }
public function createSimpleParagraph(string $content, string $class="") { public function createSimpleParagraph(string $content, string $class=""): string {
if($class) $class = " class=\"$class\""; if($class) $class = " class=\"$class\"";
return "<p$class>$content</p>"; return "<p$class>$content</p>";
} }
public function createParagraph($title, $id, $content) { public function createParagraph($title, $id, $content): string {
$id = replaceCssSelector($id); $id = replaceCssSelector($id);
$iconId = urlencode("$id-icon"); $iconId = urlencode("$id-icon");
return " return "
@ -166,7 +168,7 @@ abstract class View extends StaticView {
</div>"; </div>";
} }
protected function createBootstrapTable($data, string $classes="") { protected function createBootstrapTable($data, string $classes=""): string {
$classes = empty($classes) ? "" : " $classes"; $classes = empty($classes) ? "" : " $classes";
$code = "<div class=\"container$classes\">"; $code = "<div class=\"container$classes\">";
foreach($data as $row) { foreach($data as $row) {

@ -4,7 +4,7 @@ namespace Objects;
abstract class ApiObject implements \JsonSerializable { abstract class ApiObject implements \JsonSerializable {
public abstract function jsonSerialize(); public abstract function jsonSerialize(): array;
public function __toString() { return json_encode($this); } public function __toString() { return json_encode($this); }

@ -24,14 +24,17 @@ namespace Objects {
} }
public function getId() { return $this->languageId; } public function getId() { return $this->languageId; }
public function getCode() { return $this->langCode; } public function getCode(): string { return $this->langCode; }
public function getShortCode() { return substr($this->langCode, 0, 2); } public function getShortCode() { return substr($this->langCode, 0, 2); }
public function getName() { return $this->langName; } public function getName() { return $this->langName; }
public function getIconPath() { return "/img/icons/lang/$this->langCode.gif"; } public function getIconPath() { return "/img/icons/lang/$this->langCode.gif"; }
public function getEntries() { return $this->entries; } public function getEntries() { return $this->entries; }
public function getModules() { return $this->modules; } public function getModules() { return $this->modules; }
public function loadModule(LanguageModule $module) { /**
* @param $module LanguageModule class or object
*/
public function loadModule($module) {
if(!is_object($module)) if(!is_object($module))
$module = new $module; $module = new $module;
@ -40,7 +43,7 @@ namespace Objects {
$this->modules[] = $module; $this->modules[] = $module;
} }
public function translate($key) { public function translate(string $key): string {
if(isset($this->entries[$key])) if(isset($this->entries[$key]))
return $this->entries[$key]; return $this->entries[$key];
@ -51,7 +54,7 @@ namespace Objects {
setcookie('lang', $this->langCode, 0, "/", ""); setcookie('lang', $this->langCode, 0, "/", "");
} }
public function jsonSerialize() { public function jsonSerialize(): array {
return array( return array(
'uid' => $this->languageId, 'uid' => $this->languageId,
'code' => $this->langCode, 'code' => $this->langCode,

@ -28,7 +28,7 @@ class Session extends ApiObject {
$this->csrfToken = $csrfToken ?? generateRandomString(16); $this->csrfToken = $csrfToken ?? generateRandomString(16);
} }
public static function create($user, $stayLoggedIn) { public static function create($user, $stayLoggedIn): ?Session {
$session = new Session($user, null, null); $session = new Session($user, null, null);
if($session->insert($stayLoggedIn)) { if($session->insert($stayLoggedIn)) {
return $session; return $session;
@ -69,15 +69,15 @@ class Session extends ApiObject {
setcookie('session', $sessionCookie, $this->getExpiresTime(), "/", "", $secure); setcookie('session', $sessionCookie, $this->getExpiresTime(), "/", "", $secure);
} }
public function getExpiresTime() { public function getExpiresTime(): int {
return ($this->stayLoggedIn == 0 ? 0 : $this->expires); return ($this->stayLoggedIn == 0 ? 0 : $this->expires);
} }
public function getExpiresSeconds() { public function getExpiresSeconds(): int {
return ($this->stayLoggedIn == 0 ? -1 : $this->expires - time()); return ($this->stayLoggedIn == 0 ? -1 : $this->expires - time());
} }
public function jsonSerialize() { public function jsonSerialize(): array {
return array( return array(
'uid' => $this->sessionId, 'uid' => $this->sessionId,
'user_id' => $this->user->getId(), 'user_id' => $this->user->getId(),
@ -89,7 +89,7 @@ class Session extends ApiObject {
); );
} }
public function insert($stayLoggedIn) { public function insert($stayLoggedIn): bool {
$this->updateMetaData(); $this->updateMetaData();
$sql = $this->user->getSQL(); $sql = $this->user->getSQL();

@ -3,9 +3,6 @@
namespace Objects; namespace Objects;
use Configuration\Configuration; use Configuration\Configuration;
use DateTime;
use Driver\SQL\Expression\Add;
use Driver\SQL\Strategy\UpdateStrategy;
use Exception; use Exception;
use External\JWT; use External\JWT;
use Driver\SQL\SQL; use Driver\SQL\SQL;
@ -52,19 +49,19 @@ class User extends ApiObject {
} }
} }
public function getId() { return $this->uid; } public function getId(): int { return $this->uid; }
public function isLoggedIn() { return $this->loggedIn; } public function isLoggedIn(): bool { return $this->loggedIn; }
public function getUsername() { return $this->username; } public function getUsername(): string { return $this->username; }
public function getEmail() { return $this->email; } public function getEmail(): ?string { return $this->email; }
public function getSQL() { return $this->sql; } public function getSQL(): ?SQL { return $this->sql; }
public function getLanguage() { return $this->language; } public function getLanguage(): Language { return $this->language; }
public function setLanguage(Language $language) { $this->language = $language; $language->load(); } public function setLanguage(Language $language) { $this->language = $language; $language->load(); }
public function getSession() { return $this->session; } public function getSession(): ?Session { return $this->session; }
public function getConfiguration() { return $this->configuration; } public function getConfiguration(): Configuration { return $this->configuration; }
public function getGroups() { return $this->groups; } public function getGroups(): array { return $this->groups; }
public function hasGroup(int $group) { return isset($this->groups[$group]); } public function hasGroup(int $group): bool { return isset($this->groups[$group]); }
public function __debugInfo() { public function __debugInfo(): array {
$debugInfo = array( $debugInfo = array(
'loggedIn' => $this->loggedIn, 'loggedIn' => $this->loggedIn,
'language' => $this->language->getName(), 'language' => $this->language->getName(),
@ -78,7 +75,7 @@ class User extends ApiObject {
return $debugInfo; return $debugInfo;
} }
public function jsonSerialize() { public function jsonSerialize(): array {
if ($this->isLoggedIn()) { if ($this->isLoggedIn()) {
return array( return array(
'uid' => $this->uid, 'uid' => $this->uid,
@ -103,7 +100,7 @@ class User extends ApiObject {
$this->session = null; $this->session = null;
} }
public function logout() { public function logout(): bool {
$success = true; $success = true;
if($this->loggedIn) { if($this->loggedIn) {
$success = $this->session->destroy(); $success = $this->session->destroy();
@ -113,7 +110,7 @@ class User extends ApiObject {
return $success; return $success;
} }
public function updateLanguage($lang) { public function updateLanguage($lang): bool {
if($this->sql) { if($this->sql) {
$request = new \Api\Language\Set($this); $request = new \Api\Language\Set($this);
return $request->execute(array("langCode" => $lang)); return $request->execute(array("langCode" => $lang));
@ -131,7 +128,13 @@ class User extends ApiObject {
session_write_close(); session_write_close();
} }
public function readData($userId, $sessionId, $sessionUpdate = true) { /**
* @param $userId user's id
* @param $sessionId session's id
* @param bool $sessionUpdate update session information, including session's lifetime and browser information
* @return bool true, if the data could be loaded
*/
public function readData($userId, $sessionId, $sessionUpdate = true): bool {
$res = $this->sql->select("User.name", "User.email", $res = $this->sql->select("User.name", "User.email",
"Language.uid as langId", "Language.code as langCode", "Language.name as langName", "Language.uid as langId", "Language.code as langCode", "Language.name as langName",
@ -203,10 +206,10 @@ class User extends ApiObject {
} }
} }
public function createSession($userId, $stayLoggedIn) { public function createSession($userId, $stayLoggedIn): bool {
$this->uid = $userId; $this->uid = $userId;
$this->session = Session::create($this, $stayLoggedIn); $this->session = Session::create($this, $stayLoggedIn);
if($this->session) { if ($this->session) {
$this->loggedIn = true; $this->loggedIn = true;
return true; return true;
} }
@ -214,10 +217,11 @@ class User extends ApiObject {
return false; return false;
} }
public function authorize($apiKey) { public function authorize($apiKey): bool {
if($this->loggedIn) if ($this->loggedIn) {
return true; return true;
}
$res = $this->sql->select("ApiKey.user_id as uid", "User.name", "User.email", "User.confirmed", $res = $this->sql->select("ApiKey.user_id as uid", "User.name", "User.email", "User.confirmed",
"Language.uid as langId", "Language.code as langCode", "Language.name as langName") "Language.uid as langId", "Language.code as langCode", "Language.name as langName")
@ -264,7 +268,7 @@ class User extends ApiObject {
} }
} }
private function isBot() { private function isBot(): bool {
if (!isset($_SERVER["HTTP_USER_AGENT"]) || empty($_SERVER["HTTP_USER_AGENT"])) { if (!isset($_SERVER["HTTP_USER_AGENT"]) || empty($_SERVER["HTTP_USER_AGENT"])) {
return false; return false;
} }

@ -26,7 +26,7 @@ abstract class AccountView extends View {
} }
} }
public function getCode() { public function getCode(): string {
$html = parent::getCode(); $html = parent::getCode();
$content = $this->getAccountContent(); $content = $this->getAccountContent();

@ -11,7 +11,7 @@ class AdminDashboardBody extends Body {
parent::__construct($document); parent::__construct($document);
} }
public function getCode() { public function getCode(): string {
$html = parent::getCode(); $html = parent::getCode();
$script = new Script(Script::MIME_TEXT_JAVASCRIPT, "/js/admin.min.js"); $script = new Script(Script::MIME_TEXT_JAVASCRIPT, "/js/admin.min.js");
$html .= "<body><div class=\"wrapper\" id=\"root\">$script</div></body>"; $html .= "<body><div class=\"wrapper\" id=\"root\">$script</div></body>";

@ -24,7 +24,7 @@ class LoginBody extends Body {
$head->addCSS(Link::ACCOUNT); $head->addCSS(Link::ACCOUNT);
} }
public function getCode() { public function getCode(): string {
$html = parent::getCode(); $html = parent::getCode();
$username = L("Username"); $username = L("Username");

@ -56,7 +56,7 @@ class LanguageFlags extends View {
} }
} }
public function getCode() { public function getCode(): string {
return implode('', $this->languageFlags); return implode('', $this->languageFlags);
} }
} }

@ -6,7 +6,7 @@ use Elements\View;
class View404 extends View { class View404 extends View {
public function getCode() { public function getCode(): string {
return parent::getCode() . "<b>Not found</b>"; return parent::getCode() . "<b>Not found</b>";
} }

@ -1,8 +1,8 @@
<?php <?php
define("WEBBASE_VERSION", "1.1.0"); define("WEBBASE_VERSION", "1.2.3");
function getProtocol() { function getProtocol(): string {
return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https" : "http"; return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https" : "http";
} }
@ -26,17 +26,44 @@ function generateRandomString($length): string {
return $randomString; return $randomString;
} }
function startsWith($haystack, $needle) { function startsWith($haystack, $needle, bool $ignoreCase = false): bool {
$length = strlen($needle); $length = strlen($needle);
if ($length === 0) {
return true;
}
if ($ignoreCase) {
$haystack = strtolower($haystack);
$needle = strtolower($needle);
}
// PHP 8.0 support
if (function_exists("str_starts_with")) {
return str_starts_with($haystack, $needle);
} else {
return (substr($haystack, 0, $length) === $needle); return (substr($haystack, 0, $length) === $needle);
}
} }
function endsWith($haystack, $needle) { function endsWith($haystack, $needle, bool $ignoreCase = false): bool {
$length = strlen($needle);
if ($length == 0)
return true;
$length = strlen($needle);
if ($length === 0) {
return true;
}
if ($ignoreCase) {
$haystack = strtolower($haystack);
$needle = strtolower($needle);
}
// PHP 8.0 support
if (function_exists("str_ends_with")) {
return str_ends_with($haystack, $needle);
} else {
return (substr($haystack, -$length) === $needle); return (substr($haystack, -$length) === $needle);
}
} }
function intendCode($code, $escape = true) { function intendCode($code, $escape = true) {
@ -83,7 +110,7 @@ function getClassPath($class, $suffix = true) {
$path = str_replace('\\', '/', $class); $path = str_replace('\\', '/', $class);
$path = array_values(array_filter(explode("/", $path))); $path = array_values(array_filter(explode("/", $path)));
if (strcasecmp($path[0], "api") === 0 && count($path) > 2 && strcasecmp($path[1], "Parameter") !== 0) { if (count($path) > 2 && strcasecmp($path[0], "api") === 0 && strcasecmp($path[1], "Parameter") !== 0) {
$path = "Api/" . $path[1] . "API"; $path = "Api/" . $path[1] . "API";
} else { } else {
$path = implode("/", $path); $path = implode("/", $path);
@ -97,7 +124,7 @@ function createError($msg) {
return json_encode(array("success" => false, "msg" => $msg)); return json_encode(array("success" => false, "msg" => $msg));
} }
function serveStatic(string $webRoot, string $file) { function serveStatic(string $webRoot, string $file): string {
$path = realpath($webRoot . "/" . $file); $path = realpath($webRoot . "/" . $file);
if (!startsWith($path, $webRoot . "/")) { if (!startsWith($path, $webRoot . "/")) {
@ -112,8 +139,8 @@ function serveStatic(string $webRoot, string $file) {
$pathInfo = pathinfo($path); $pathInfo = pathinfo($path);
// TODO: add more file extensions here // TODO: add more file extensions here, probably add them to settings?
$allowedExtension = array("html", "htm"); $allowedExtension = array("html", "htm", "pdf");
$ext = $pathInfo["extension"] ?? ""; $ext = $pathInfo["extension"] ?? "";
if (!in_array($ext, $allowedExtension)) { if (!in_array($ext, $allowedExtension)) {
http_response_code(406); http_response_code(406);

@ -0,0 +1 @@
DENY FROM ALL

File diff suppressed because it is too large Load Diff

@ -11,7 +11,7 @@
"react-draft-wysiwyg": "^1.14.5", "react-draft-wysiwyg": "^1.14.5",
"react-dropzone": "^11.2.4", "react-dropzone": "^11.2.4",
"react-router-dom": "^5.2.0", "react-router-dom": "^5.2.0",
"react-scripts": "^3.4.4", "react-scripts": "^4.0.3",
"react-tooltip": "^4.2.13" "react-tooltip": "^4.2.13"
}, },
"scripts": { "scripts": {

@ -41,10 +41,11 @@ export function TokenList(props) {
function fetchTokens() { function fetchTokens() {
api.listTokens().then((res) => { api.listTokens().then((res) => {
if (res) { if (res.success) {
setTokens(res.tokens); setTokens(res.tokens);
} else { } else {
pushAlert(res, "Error fetching tokens"); pushAlert(res, "Error fetching tokens");
setTokens([]);
} }
}); });
} }
@ -179,7 +180,7 @@ export function TokenList(props) {
onChange={(e) => onPopupChange(e, "maxFiles")}/> onChange={(e) => onPopupChange(e, "maxFiles")}/>
</div> </div>
<div className={"form-group"}> <div className={"form-group"}>
<label>Max. Size per file in MB (0 = unlimited):</label> <label>Max. Size per file in MiB (0 = unlimited):</label>
<input type={"number"} min={0} max={10} className={"form-control"} <input type={"number"} min={0} max={10} className={"form-control"}
value={popup.maxSize} onChange={(e) => onPopupChange(e, "maxSize")}/> value={popup.maxSize} onChange={(e) => onPopupChange(e, "maxSize")}/>
</div> </div>
@ -256,7 +257,8 @@ export function TokenList(props) {
}); });
} else if (popup.tokenType === "upload") { } else if (popup.tokenType === "upload") {
let parentId = popup.directory === 0 ? null : popup.directory; let parentId = popup.directory === 0 ? null : popup.directory;
api.createUploadToken(durability, parentId, popup.maxFiles, popup.maxSize, popup.extensions).then((res) => { let maxSize = Math.round(popup.maxSize * 1024 * 1024);
api.createUploadToken(durability, parentId, popup.maxFiles, maxSize, popup.extensions).then((res) => {
if (!res.success) { if (!res.success) {
pushAlert(res, "Error creating token"); pushAlert(res, "Error creating token");
} else { } else {

@ -141,12 +141,11 @@ if(isset($_GET["api"]) && is_string($_GET["api"])) {
$response = serveStatic($currentDir, $target); $response = serveStatic($currentDir, $target);
break; break;
case "dynamic": case "dynamic":
$view = parseClass($extra);
$file = getClassPath($target); $file = getClassPath($target);
if(!file_exists($file) || !is_subclass_of($target, Document::class)) { if (!file_exists($file) || !is_subclass_of($target, Document::class)) {
$document = new Document404($user, $view); $document = new Document404($user, $extra);
} else { } else {
$document = new $target($user, $view); $document = new $target($user, $extra);
} }
$response = $document->getCode(); $response = $document->getCode();

26
js/admin.min.js vendored

File diff suppressed because one or more lines are too long

12
js/files.min.js vendored

File diff suppressed because one or more lines are too long