Added/updated comments
This commit is contained in:
parent
9bf9bd2823
commit
905d91d6ed
11
README.md
11
README.md
|
@ -28,7 +28,7 @@ The Engelsystem may be installed manually or by using the provided [docker setup
|
|||
* MySQL-Server >= 5.7.8 or MariaDB-Server >= 10.2.2
|
||||
* Webserver, i.e. lighttpd, nginx, or Apache
|
||||
|
||||
From experience 2 cores and 2GB ram are roughly enough for about 1000 Angels (~700 arrived + 500 arrived but not working) during an event.
|
||||
From previous experience, 2 cores and 2GB ram are roughly enough for up to 1000 Angels (~700 arrived + 500 arrived but not working) during an event.
|
||||
|
||||
### Download
|
||||
* Go to the [Releases](https://github.com/engelsystem/engelsystem/releases) page and download the latest stable release file.
|
||||
|
@ -42,7 +42,14 @@ From experience 2 cores and 2GB ram are roughly enough for about 1000 Angels (~7
|
|||
* Recommended: Directory Listing should be disabled.
|
||||
* There must be a MySQL database set up with a user who has full rights to that database.
|
||||
* If necessary, create a `config/config.php` to override values from `config/config.default.php`.
|
||||
* To disable/remove values from the `themes`, `tshirt_sizes`, `headers`, `header_items`, `footer_items`, or `locales` lists, set the value of the entry to `null`.
|
||||
* To disable/remove values from the following lists, set the value of the entry to `null`:
|
||||
* `themes`
|
||||
* `tshirt_sizes`
|
||||
* `headers`
|
||||
* `header_items`
|
||||
* `footer_items`
|
||||
* `locales`
|
||||
* `contact_options`
|
||||
* To import the database, the `bin/migrate` script has to be run. If you can't execute scripts, you can use the `initial-install.sql` file from the release zip.
|
||||
* In the browser, login with credentials `admin` : `asdfasdf` and change the password.
|
||||
|
||||
|
|
|
@ -128,11 +128,14 @@ function User_get_shifts_sum_query()
|
|||
'
|
||||
COALESCE(SUM(
|
||||
(1 + (
|
||||
/* Starts during night */
|
||||
HOUR(shifts.start) >= %1$d AND HOUR(shifts.start) < %2$d
|
||||
/* Ends during night */
|
||||
OR (
|
||||
HOUR(shifts.end) > %1$d
|
||||
|| HOUR(shifts.end) = %1$d AND MINUTE(shifts.end) > 0
|
||||
) AND HOUR(shifts.end) <= %2$d
|
||||
/* Starts before and ends after night */
|
||||
OR HOUR(shifts.start) <= %1$d AND HOUR(shifts.end) >= %2$d
|
||||
))
|
||||
* (UNIX_TIMESTAMP(shifts.end) - UNIX_TIMESTAMP(shifts.start))
|
||||
|
|
|
@ -6,6 +6,7 @@ use Engelsystem\Application;
|
|||
use Engelsystem\Middleware\Dispatcher;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
// Include app bootstrapping
|
||||
require_once realpath(__DIR__ . '/../includes/engelsystem.php');
|
||||
|
||||
/** @var Application $app */
|
||||
|
@ -18,4 +19,5 @@ $middleware = $app->getMiddleware();
|
|||
$dispatcher = new Dispatcher($middleware);
|
||||
$dispatcher->setContainer($app);
|
||||
|
||||
// Handle the request
|
||||
$dispatcher->handle($request);
|
||||
|
|
|
@ -14,9 +14,16 @@ class ConfigServiceProvider extends ServiceProvider
|
|||
{
|
||||
protected array $configFiles = ['app.php', 'config.default.php', 'config.php'];
|
||||
|
||||
# remember to update ConfigServiceProviderTest, config.default.php, and README.md
|
||||
protected array $configVarsToPruneNulls
|
||||
= ['themes', 'tshirt_sizes', 'headers', 'header_items', 'footer_items', 'locales', 'contact_options'];
|
||||
// Remember to update ConfigServiceProviderTest, config.default.php, and README.md
|
||||
protected array $configVarsToPruneNulls = [
|
||||
'themes',
|
||||
'tshirt_sizes',
|
||||
'headers',
|
||||
'header_items',
|
||||
'footer_items',
|
||||
'locales',
|
||||
'contact_options',
|
||||
];
|
||||
|
||||
public function __construct(Application $app, protected ?EventConfig $eventConfig = null)
|
||||
{
|
||||
|
@ -29,6 +36,7 @@ class ConfigServiceProvider extends ServiceProvider
|
|||
$this->app->instance(Config::class, $config);
|
||||
$this->app->instance('config', $config);
|
||||
|
||||
// Load configuration from files
|
||||
foreach ($this->configFiles as $file) {
|
||||
$file = $this->getConfigPath($file);
|
||||
|
||||
|
@ -47,6 +55,7 @@ class ConfigServiceProvider extends ServiceProvider
|
|||
throw new Exception('Configuration not found');
|
||||
}
|
||||
|
||||
// Prune values with null to remove them
|
||||
foreach ($this->configVarsToPruneNulls as $key) {
|
||||
$config->set($key, array_filter($config->get($key), function ($v) {
|
||||
return !is_null($v);
|
||||
|
|
|
@ -98,6 +98,7 @@ class LocationsController extends BaseController
|
|||
$location->neededAngelTypes()->getQuery()->delete();
|
||||
$angelsInfo = '';
|
||||
|
||||
// Associate angel types with the room
|
||||
foreach ($angelTypes as $angelType) {
|
||||
$count = $data['angel_type_' . $angelType->id];
|
||||
if (!$count) {
|
||||
|
|
|
@ -113,6 +113,7 @@ class ShiftTypesController extends BaseController
|
|||
$shiftType->save();
|
||||
$shiftType->neededAngelTypes()->delete();
|
||||
|
||||
// Associate angel types with the shift type
|
||||
$angelsInfo = '';
|
||||
foreach ($angelTypes as $angelType) {
|
||||
$count = $data['angel_type_' . $angelType->id];
|
||||
|
|
|
@ -69,6 +69,7 @@ class UserWorkLogController extends BaseController
|
|||
'comment' => 'required|max:200',
|
||||
]);
|
||||
|
||||
// Search / create worklog
|
||||
if (isset($worklogId)) {
|
||||
$worklog = $this->worklog->findOrFail((int) $worklogId);
|
||||
|
||||
|
|
|
@ -70,6 +70,7 @@ class FeedController extends BaseController
|
|||
$shift = $entry->shift;
|
||||
// Data required for the Fahrplan app integration https://github.com/johnjohndoe/engelsystem
|
||||
// See engelsystem-base/src/main/kotlin/info/metadude/kotlin/library/engelsystem/models/Shift.kt
|
||||
// Explicitly typecasts used to stay consistent
|
||||
// ! All attributes not defined in $data might change at any time !
|
||||
$data = [
|
||||
// Name of the shift (type)
|
||||
|
|
|
@ -171,7 +171,7 @@ class MessagesController extends BaseController
|
|||
if ($msg->user_id == $currentUser->id) {
|
||||
$msg->delete();
|
||||
} else {
|
||||
throw new HttpForbidden('You can not delete a message you haven\'t send');
|
||||
throw new HttpForbidden();
|
||||
}
|
||||
|
||||
return $this->redirect->to('/messages/' . $otherUserId . '#newest');
|
||||
|
|
|
@ -225,7 +225,7 @@ class Controller extends BaseController
|
|||
}
|
||||
|
||||
/**
|
||||
* Ensure that the if the request is authorized
|
||||
* Ensure that the request is authorized
|
||||
*/
|
||||
protected function checkAuth(bool $isJson = false): void
|
||||
{
|
||||
|
|
|
@ -51,6 +51,7 @@ class OAuthController extends BaseController
|
|||
throw new HttpNotFound('oauth.' . $request->get('error'));
|
||||
}
|
||||
|
||||
// Initial request redirects to provider
|
||||
if (!$request->has('code')) {
|
||||
$authorizationUrl = $provider->getAuthorizationUrl(
|
||||
[
|
||||
|
@ -64,6 +65,7 @@ class OAuthController extends BaseController
|
|||
return $this->redirect->to($authorizationUrl);
|
||||
}
|
||||
|
||||
// Redirected URL got called a second time
|
||||
if (
|
||||
!$this->session->get('oauth2_state')
|
||||
|| $request->get('state') !== $this->session->get('oauth2_state')
|
||||
|
@ -75,6 +77,7 @@ class OAuthController extends BaseController
|
|||
throw new HttpNotFound('oauth.invalid-state');
|
||||
}
|
||||
|
||||
// Fetch access token
|
||||
$accessToken = null;
|
||||
try {
|
||||
$accessToken = $provider->getAccessToken(
|
||||
|
@ -87,6 +90,7 @@ class OAuthController extends BaseController
|
|||
$this->handleOAuthError($e, $providerName);
|
||||
}
|
||||
|
||||
// Load resource identifier
|
||||
$resourceOwner = null;
|
||||
try {
|
||||
$resourceOwner = $provider->getResourceOwner($accessToken);
|
||||
|
@ -95,6 +99,7 @@ class OAuthController extends BaseController
|
|||
}
|
||||
$resourceId = $this->getId($providerName, $resourceOwner);
|
||||
|
||||
// Fetch existing oauth state
|
||||
/** @var OAuth|null $oauth */
|
||||
$oauth = $this->oauth
|
||||
->query()
|
||||
|
@ -105,6 +110,7 @@ class OAuthController extends BaseController
|
|||
->where('identifier', '===', (string) $resourceId)
|
||||
->first();
|
||||
|
||||
// Update oauth state
|
||||
$expirationTime = $accessToken->getExpires();
|
||||
$expirationTime = $expirationTime ? Carbon::createFromTimestamp($expirationTime) : null;
|
||||
if ($oauth) {
|
||||
|
@ -115,6 +121,7 @@ class OAuthController extends BaseController
|
|||
$oauth->save();
|
||||
}
|
||||
|
||||
// Load user
|
||||
$user = $this->auth->user();
|
||||
if ($oauth && $user && $user->id != $oauth->user_id) {
|
||||
throw new HttpNotFound('oauth.already-connected');
|
||||
|
@ -122,6 +129,7 @@ class OAuthController extends BaseController
|
|||
|
||||
$connectProvider = $this->session->get('oauth2_connect_provider');
|
||||
$this->session->remove('oauth2_connect_provider');
|
||||
// Connect user with oauth
|
||||
if (!$oauth && $user && $connectProvider && $connectProvider == $providerName) {
|
||||
$oauth = new OAuth([
|
||||
'provider' => $providerName,
|
||||
|
@ -141,6 +149,7 @@ class OAuthController extends BaseController
|
|||
$this->addNotification('oauth.connected');
|
||||
}
|
||||
|
||||
// Load user data
|
||||
$resourceData = $resourceOwner->toArray();
|
||||
if (!empty($config['nested_info'])) {
|
||||
$resourceData = Arr::dot($resourceData);
|
||||
|
@ -148,6 +157,7 @@ class OAuthController extends BaseController
|
|||
|
||||
$userdata = new Collection($resourceData);
|
||||
if (!$oauth) {
|
||||
// User authenticated but has no account
|
||||
return $this->redirectRegister(
|
||||
$providerName,
|
||||
(string) $resourceId,
|
||||
|
@ -307,11 +317,13 @@ class OAuthController extends BaseController
|
|||
throw new HttpNotFound('oauth.not-found');
|
||||
}
|
||||
|
||||
// Set registration form field data
|
||||
$this->session->set('form-data-username', $userdata->get($config['username']));
|
||||
$this->session->set('form-data-email', $userdata->get($config['email']));
|
||||
$this->session->set('form-data-firstname', $userdata->get($config['first_name']));
|
||||
$this->session->set('form-data-lastname', $userdata->get($config['last_name']));
|
||||
|
||||
// Define OAuth state
|
||||
$this->session->set('oauth2_groups', $userdata->get($config['groups'], []));
|
||||
$this->session->set('oauth2_connect_provider', $providerName);
|
||||
$this->session->set('oauth2_user_id', $providerUserIdentifier);
|
||||
|
|
|
@ -115,7 +115,7 @@ class RegistrationController extends BaseController
|
|||
}
|
||||
|
||||
/**
|
||||
* @return Array<string, 1> Checkbox field name/Id → 1
|
||||
* @return Array<string, 1> Checkbox field name/id → 1
|
||||
*/
|
||||
private function determinePreselectedAngelTypes(): array
|
||||
{
|
||||
|
|
|
@ -210,6 +210,7 @@ class User
|
|||
*/
|
||||
private function createUser(array $data, array $rawData): EngelsystemUser
|
||||
{
|
||||
// Ensure all user entries got created before saving
|
||||
$this->dbConnection->beginTransaction();
|
||||
|
||||
$user = new EngelsystemUser([
|
||||
|
@ -274,6 +275,7 @@ class User
|
|||
->associate($user)
|
||||
->save();
|
||||
|
||||
// Handle OAuth registration
|
||||
if ($this->session->has('oauth2_connect_provider') && $this->session->has('oauth2_user_id')) {
|
||||
$oauth = new OAuth([
|
||||
'provider' => $this->session->get('oauth2_connect_provider'),
|
||||
|
|
|
@ -4,14 +4,11 @@ declare(strict_types=1);
|
|||
|
||||
namespace Engelsystem\Helpers;
|
||||
|
||||
use Engelsystem\Helpers\Carbon;
|
||||
|
||||
class DayOfEvent
|
||||
{
|
||||
/**
|
||||
* @return The current day of the event.
|
||||
* If "event_has_day0" is set to true in config,
|
||||
* the first day of the event will be 0, else 1.
|
||||
* @return ?int The current day of the event.
|
||||
* If `event_has_day0` is set to true in config, the first day of the event will be 0, else 1.
|
||||
* Returns null if "event_start" is not set.
|
||||
*/
|
||||
public static function get(Carbon $date = null): int | null
|
||||
|
|
|
@ -18,11 +18,14 @@ class Shifts
|
|||
|
||||
/** @see User_get_shifts_sum_query to keep it in sync */
|
||||
return $config['enabled'] && (
|
||||
// Starts during night
|
||||
$start->hour >= $config['start'] && $start->hour < $config['end']
|
||||
// Ends during night
|
||||
|| (
|
||||
$end->hour > $config['start']
|
||||
|| $end->hour == $config['start'] && $end->minute > 0
|
||||
) && $end->hour <= $config['end']
|
||||
// Starts before and ends after night
|
||||
|| $start->hour <= $config['start'] && $end->hour >= $config['end']
|
||||
);
|
||||
}
|
||||
|
|
|
@ -72,7 +72,10 @@ class TranslationServiceProvider extends ServiceProvider
|
|||
|
||||
public function getTranslator(string $locale): GettextTranslator
|
||||
{
|
||||
if (!isset($this->translators[$locale])) {
|
||||
if (isset($this->translators[$locale])) {
|
||||
return $this->translators[$locale];
|
||||
}
|
||||
|
||||
$names = ['default', 'additional'];
|
||||
|
||||
/** @var Translations $translations */
|
||||
|
@ -86,9 +89,9 @@ class TranslationServiceProvider extends ServiceProvider
|
|||
$file = $this->getFile($locale, $this->app->get('path.config') . '/lang', 'custom');
|
||||
$translations = $this->loadFile($file, $translations);
|
||||
|
||||
/** @var GettextTranslator $translator */
|
||||
$translator = GettextTranslator::createFromTranslations($translations);
|
||||
$this->translators[$locale] = $translator;
|
||||
}
|
||||
|
||||
return $this->translators[$locale];
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ class Uuid
|
|||
mt_rand(0, 0xffff),
|
||||
// first bit is the uuid version, here 4
|
||||
mt_rand(0, 0x0fff) | 0x4000,
|
||||
// variant
|
||||
// variant, here OSF DCE UUID
|
||||
mt_rand(0, 0x3fff) | 0x8000,
|
||||
mt_rand(0, 0xffffffffffff)
|
||||
);
|
||||
|
@ -53,7 +53,7 @@ class Uuid
|
|||
Str::substr($value, 8, 4),
|
||||
// first bit is the uuid version, here 4
|
||||
'4' . Str::substr($value, 13, 3),
|
||||
// first bit is the variant (0x8-0xb)
|
||||
// first bit is the variant (0x8-0xb), here OSF DCE UUID
|
||||
dechex(8 + (hexdec(Str::substr($value, 16, 1)) % 4))
|
||||
. Str::substr($value, 17, 3),
|
||||
Str::substr($value, 20, 12)
|
||||
|
|
|
@ -41,7 +41,7 @@ class UrlGenerator implements UrlGeneratorInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Prepend the auto detected or configured app base path and domain
|
||||
* Prepend the auto-detected or configured app base path and domain
|
||||
*
|
||||
* @param $path
|
||||
*/
|
||||
|
|
|
@ -37,6 +37,7 @@ class Validator
|
|||
$value = isset($data[$key]) ? $data[$key] : null;
|
||||
$values = explode('|', $values);
|
||||
|
||||
// Rules that have side effects on others like inverting the result with not and making them optional
|
||||
$packing = [];
|
||||
foreach ($this->nestedRules as $rule) {
|
||||
if (in_array($rule, $values)) {
|
||||
|
|
|
@ -12,6 +12,9 @@ use Psr\Http\Message\ServerRequestInterface;
|
|||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
|
||||
/**
|
||||
* Wraps a callable to be used to respond / as a middleware
|
||||
*/
|
||||
class CallableHandler implements MiddlewareInterface, RequestHandlerInterface
|
||||
{
|
||||
/** @var callable */
|
||||
|
|
|
@ -43,7 +43,7 @@ class Dispatcher implements MiddlewareInterface, RequestHandlerInterface
|
|||
/**
|
||||
* Handle the request and return a response.
|
||||
*
|
||||
* It calls all configured middleware and handles their response
|
||||
* It calls all configured middlewares and handles their response
|
||||
*/
|
||||
public function handle(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
|
|
|
@ -22,7 +22,7 @@ class ErrorHandler implements MiddlewareInterface
|
|||
protected string $viewPrefix = 'errors/';
|
||||
|
||||
/**
|
||||
* A list of inputs that are not saved from form input
|
||||
* A list of inputs that are not saved from input
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
|
@ -42,7 +42,7 @@ class ErrorHandler implements MiddlewareInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Handles any error messages
|
||||
* Handles any error messages / http exceptions / validation errors
|
||||
*
|
||||
* Should be added at the beginning
|
||||
*/
|
||||
|
@ -50,6 +50,7 @@ class ErrorHandler implements MiddlewareInterface
|
|||
ServerRequestInterface $request,
|
||||
RequestHandlerInterface $handler
|
||||
): ResponseInterface {
|
||||
// Handle response
|
||||
try {
|
||||
$response = $handler->handle($request);
|
||||
} catch (HttpException $e) {
|
||||
|
@ -75,6 +76,7 @@ class ErrorHandler implements MiddlewareInterface
|
|||
$contentType = 'text/html';
|
||||
}
|
||||
|
||||
// Handle response based on status
|
||||
if (
|
||||
$statusCode < 400
|
||||
|| !$response instanceof Response
|
||||
|
|
|
@ -13,6 +13,9 @@ use Psr\Http\Message\ServerRequestInterface;
|
|||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
|
||||
/**
|
||||
* Middleware to support the old routing / pages from includes
|
||||
*/
|
||||
class LegacyMiddleware implements MiddlewareInterface
|
||||
{
|
||||
/** @var array<string> */
|
||||
|
@ -33,7 +36,7 @@ class LegacyMiddleware implements MiddlewareInterface
|
|||
/**
|
||||
* Handle the request the old way
|
||||
*
|
||||
* Should be used before a 404 is send
|
||||
* Should be used before a 404 is sent
|
||||
*/
|
||||
public function process(
|
||||
ServerRequestInterface $request,
|
||||
|
@ -42,6 +45,7 @@ class LegacyMiddleware implements MiddlewareInterface
|
|||
/** @var Request $appRequest */
|
||||
$appRequest = $this->container->get('request');
|
||||
$page = $appRequest->query->get('p');
|
||||
// Support old URL scheme
|
||||
if (empty($page)) {
|
||||
$page = $appRequest->path();
|
||||
$page = str_replace('-', '_', $page);
|
||||
|
|
|
@ -24,6 +24,7 @@ class RequestHandler implements MiddlewareInterface
|
|||
/**
|
||||
* Process an incoming server request and return a response, optionally delegating
|
||||
* response creation to a handler.
|
||||
* Implements basic permission checking if the controller supports it.
|
||||
*/
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
|
|
|
@ -35,7 +35,6 @@ class TrimInput implements MiddlewareInterface
|
|||
$request = $request->withParsedBody($trimmedParsedBody);
|
||||
}
|
||||
|
||||
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
|
|
|
@ -39,11 +39,10 @@ class Globals extends TwigExtension implements GlobalsInterface
|
|||
{
|
||||
$user = $this->auth->user();
|
||||
$themes = config('themes');
|
||||
$themeId = config('theme');
|
||||
$userMessages = null;
|
||||
|
||||
if ($user === null) {
|
||||
$themeId = config('theme');
|
||||
} else {
|
||||
if ($user) {
|
||||
$themeId = $user->settings->theme;
|
||||
$userMessages = $user
|
||||
->messagesReceived()
|
||||
|
|
|
@ -26,6 +26,7 @@ class Url extends TwigExtension
|
|||
|
||||
public function getUrl(string $path, array $parameters = []): string
|
||||
{
|
||||
// Fix legacy URLs
|
||||
$path = str_replace('_', '-', $path);
|
||||
|
||||
return $this->urlGenerator->to($path, $parameters);
|
||||
|
|
Loading…
Reference in New Issue