if (isset($_REQUEST['shift_id']) && preg_match("/^[0-9]*$/", $_REQUEST['shift_id'])) {
$shift_id = 0;
if (isset($_REQUEST['shift_id']) && preg_match('/^\d*$/', $_REQUEST['shift_id'])) {
$shift_id = $_REQUEST['shift_id'];
} else {
redirect(page_link_to('user_shifts'));
}
// Locations laden
$rooms = sql_select("SELECT * FROM `Room` WHERE `show`='Y' ORDER BY `Name`");
$rooms = Rooms();
$room_array = [];
foreach ($rooms as $room) {
$room_array[$room['RID']] = $room['Name'];
@ -25,7 +31,8 @@ function shift_entry_add_controller() {
redirect(page_link_to('user_shifts'));
}
if (isset($_REQUEST['type_id']) && preg_match("/^[0-9]*$/", $_REQUEST['type_id'])) {
$type_id = 0;
if (isset($_REQUEST['type_id']) && preg_match('/^\d*$/', $_REQUEST['type_id'])) {
$type_id = $_REQUEST['type_id'];
} else {
redirect(page_link_to('user_shifts'));
@ -34,15 +41,35 @@ function shift_entry_add_controller() {
if (in_array('user_shifts_admin', $privileges) || in_array('shiftentry_edit_angeltype_supporter', $privileges)) {
$type = AngelType($type_id);
} else {
$type = sql_select("SELECT * FROM `UserAngelTypes` JOIN `AngelTypes` ON (`UserAngelTypes`.`angeltype_id` = `AngelTypes`.`id`) WHERE `AngelTypes`.`id` = '" . sql_escape($type_id) . "' AND (`AngelTypes`.`restricted` = 0 OR (`UserAngelTypes`.`user_id` = '" . sql_escape($user['UID']) . "' AND NOT `UserAngelTypes`.`confirm_user_id` IS NULL))");
$type = $type[0];
// TODO: Move queries to model
$type = DB::select('
SELECT *
FROM `UserAngelTypes`
JOIN `AngelTypes` ON (`UserAngelTypes`.`angeltype_id` = `AngelTypes`.`id`)
WHERE `AngelTypes`.`id` = ?
AND (
`AngelTypes`.`restricted` = 0
OR (
`UserAngelTypes`.`user_id` = ?
AND NOT `UserAngelTypes`.`confirm_user_id` IS NULL
$users = sql_select("SELECT *, (SELECT count(*) FROM `ShiftEntry` WHERE `freeloaded`=1 AND `ShiftEntry`.`UID`=`User`.`UID`) AS `freeloaded` FROM `User` ORDER BY `Nick`");
error(sprintf(_("Do you want to delete the shift %s from %s to %s?"), $shift['name'], date("Y-m-d H:i", $shift['start']), date("H:i", $shift['end'])), true),
$success_message = sprintf($supporter ? _("Added supporter rights for %s to %s.") : _("Removed supporter rights for %s from %s."), AngelType_name_render($angeltype), User_Nick_render($user_source));
$success_message = sprintf(
$supporter ? _('Added supporter rights for %s to %s.') : _('Removed supporter rights for %s from %s.'),
AngelType_name_render($angeltype),
User_Nick_render($user_source)
);
engelsystem_log($success_message);
success($success_message);
@ -253,7 +289,7 @@ function user_angeltype_update_controller() {
* Generates a hint, if user joined angeltypes that require a driving license and the user has no driver license information provided.
* Generates a hint, if user joined angeltypes that require a driving license and the user has no driver license
* information provided.
*
* @return string|null
*/
function user_driver_license_required_hint() {
function user_driver_license_required_hint()
{
global $user;
$angeltypes = User_angeltypes($user);
@ -16,7 +20,10 @@ function user_driver_license_required_hint() {
foreach ($angeltypes as $angeltype) {
if ($angeltype['requires_driver_license']) {
return sprintf(_("You joined an angeltype which requires a driving license. Please edit your driving license information here: %s."), '<ahref="' . user_driver_license_edit_link() . '">' . _("driving license information") . '</a>');
return sprintf(
_('You joined an angeltype which requires a driving license. Please edit your driving license information here: %s.'),
$shift['needed_angeltypes'] = sql_select("SELECT DISTINCT `AngelTypes`.* FROM `ShiftEntry` JOIN `AngelTypes` ON `ShiftEntry`.`TID`=`AngelTypes`.`id` WHERE `ShiftEntry`.`SID`='" . sql_escape($shift['SID']) . "' ORDER BY `AngelTypes`.`name`");
$shift['needed_angeltypes'] = DB::select('
SELECT DISTINCT `AngelTypes`.*
FROM `ShiftEntry`
JOIN `AngelTypes` ON `ShiftEntry`.`TID`=`AngelTypes`.`id`
WHERE `ShiftEntry`.`SID` = ?
ORDER BY `AngelTypes`.`name`
',
[$shift['SID']]
);
foreach ($shift['needed_angeltypes'] as &$needed_angeltype) {
$needed_angeltype['users'] = sql_select("
$needed_angeltype['users'] = DB::select('
SELECT `ShiftEntry`.`freeloaded`, `User`.*
FROM `ShiftEntry`
JOIN `User` ON `ShiftEntry`.`UID`=`User`.`UID`
WHERE `ShiftEntry`.`SID`='" . sql_escape($shift['SID']) . "'
AND `ShiftEntry`.`TID`='" . sql_escape($needed_angeltype['id']) . "'");
function engelsystem_email_to_user($recipient_user, $title, $message, $not_if_its_me = false) {
/**
* @param array $recipient_user
* @param string $title
* @param string $message
* @param bool $not_if_its_me
* @return bool
*/
function engelsystem_email_to_user($recipient_user, $title, $message, $not_if_its_me = false)
{
global $user;
if ($not_if_its_me && $user['UID'] == $recipient_user['UID']) {
@ -9,18 +17,34 @@ function engelsystem_email_to_user($recipient_user, $title, $message, $not_if_it
gettext_locale($recipient_user['Sprache']);
$message = sprintf(_("Hi %s,"), $recipient_user['Nick']) . "\n\n" . _("here is a message for you from the engelsystem:") . "\n\n" . $message . "\n\n" . _("This email is autogenerated and has not to be signed. You got this email because you are registered in the engelsystem.");
engelsystem_email_to_user($user, '[engelsystem] ' . _("Your account has been deleted"), _("Your angelsystem account has been deleted. If you have any questions regarding your account deletion, please contact heaven."));
function mail_user_delete($user)
{
return engelsystem_email_to_user(
$user,
'[engelsystem] ' . _('Your account has been deleted'),
_('Your angelsystem account has been deleted. If you have any questions regarding your account deletion, please contact heaven.')
return sql_query("INSERT INTO `LogEntries` SET `timestamp`='" . sql_escape(time()) . "', `nick`='" . sql_escape($nick) . "', `message`='" . sql_escape($message) . "'");
function LogEntry_create($nick, $message)
{
return DB::insert('
INSERT INTO `LogEntries` (`timestamp`, `nick`, `message`)
VALUES(?, ?, ?)
', [time(), $nick, $message]);
}
/**
* Returns log entries with maximum count of 10000.
*
* @return array
*/
function LogEntries() {
return sql_select("SELECT * FROM `LogEntries` ORDER BY `timestamp` DESC LIMIT 10000");
function LogEntries()
{
return DB::select('SELECT * FROM `LogEntries` ORDER BY `timestamp` DESC LIMIT 10000');
}
/**
* Returns log entries filtered by a keyword
*
* @param string $keyword
* @return array
*/
function LogEntries_filter($keyword) {
if ($keyword == "") {
function LogEntries_filter($keyword)
{
if ($keyword == '') {
return LogEntries();
}
return sql_select("SELECT * FROM `LogEntries` WHERE `nick` LIKE '%" . sql_escape($keyword) . "%' OR `message` LIKE '%" . sql_escape($keyword) . "%' ORDER BY `timestamp` DESC");
if (isset($_REQUEST['id']) && preg_match("/^-[0-9]{1,11}$/", $_REQUEST['id'])) {
if (isset($_REQUEST['id']) && preg_match('/^-\d{1,11}$/', $_REQUEST['id'])) {
$group_id = $_REQUEST['id'];
} else {
return error("Incomplete call, missing Groups ID.", true);
return error('Incomplete call, missing Groups ID.', true);
}
$group = sql_select("SELECT * FROM `Groups` WHERE `UID`='" . sql_escape($group_id) . "' LIMIT 1");
if (count($group) > 0) {
list($group) = $group;
$privileges = sql_select("SELECT `Privileges`.*, `GroupPrivileges`.`group_id` FROM `Privileges` LEFT OUTER JOIN `GroupPrivileges` ON (`Privileges`.`id` = `GroupPrivileges`.`privilege_id` AND `GroupPrivileges`.`group_id`='" . sql_escape($group_id) . "') ORDER BY `Privileges`.`name`");
$privileges_html = "";
$group = DB::select('SELECT * FROM `Groups` WHERE `UID`=? LIMIT 1', [$group_id]);
global $user, $privileges, $tshirt_sizes, $privileges;
/**
* @return string
*/
function admin_user()
{
global $user, $privileges;
$tshirt_sizes = config('tshirt_sizes');
foreach ($tshirt_sizes as $key => $size) {
if (empty($size)) {
unset($tshirt_sizes[$key]);
}
}
$html = '';
@ -21,103 +38,135 @@ function admin_user() {
redirect(users_link());
}
$html .= "Hallo,<br/>" . "hier kannst du den Eintrag ändern. Unter dem Punkt 'Gekommen' " . "wird der Engel als anwesend markiert, ein Ja bei Aktiv bedeutet, " . "dass der Engel aktiv war und damit ein Anspruch auf ein T-Shirt hat. " . "Wenn T-Shirt ein 'Ja' enthält, bedeutet dies, dass der Engel " . "bereits sein T-Shirt erhalten hat.<br/><br/>\n";
$html .= form_info('', _('Please visit the angeltypes page or the users profile to manage users angeltypes.'));
$html .= "Hier kannst Du das Passwort dieses Engels neu setzen:<formaction=\"".page_link_to("admin_user")."&action=change_pw&id=$user_id\"method=\"post\">\n";
if ($user_id != $user['UID'] && $my_highest_group <= $his_highest_group) {
$html .= "Hier kannst Du die Benutzergruppen des Engels festlegen:<formaction=\"".page_link_to("admin_user")."&action=save_groups&id=".$user_id."\"method=\"post\">\n";
$html .= 'Hier kannst Du die Benutzergruppen des Engels festlegen:<formaction="'
$groups = sql_select("SELECT * FROM `Groups` LEFT OUTER JOIN `UserGroups` ON (`UserGroups`.`group_id` = `Groups`.`UID` AND `UserGroups`.`uid` = '" . sql_escape($user_id) . "') WHERE `Groups`.`UID` >= '" . sql_escape($my_highest_group) . "' ORDER BY `Groups`.`Name`");
$groups_source = sql_select("SELECT * FROM `Groups` LEFT OUTER JOIN `UserGroups` ON (`UserGroups`.`group_id` = `Groups`.`UID` AND `UserGroups`.`uid` = '" . sql_escape($user_id) . "') WHERE `Groups`.`UID` >= '" . sql_escape($my_highest_group[0]['group_id']) . "' ORDER BY `Groups`.`Name`");
$my_highest_group = DB::select(
'SELECT * FROM `UserGroups` WHERE `uid`=? ORDER BY `group_id`',
[$user['UID']]
);
$his_highest_group = DB::select(
'SELECT * FROM `UserGroups` WHERE `uid`=? ORDER BY `group_id`',
form_checkboxes('angel_types', _("What do you want to do?") . sprintf(" (<ahref=\"%s\">%s</a>)", page_link_to('angeltypes') . '&action=about', _("Description of job types")), $angel_types, $selected_angel_types),
form_info("", _("Restricted angel types need will be confirmed later by a supporter. You can change your selection in the options section."))
form_checkboxes(
'angel_types',
_('What do you want to do?') . sprintf(
' (<ahref="%s">%s</a>)',
page_link_to('angeltypes') . '&action=about',
_('Description of job types')
),
$angel_types,
$selected_angel_types
),
form_info(
'',
_('Restricted angel types need will be confirmed later by a supporter. You can change your selection in the options section.')
$done_shifts_seconds = sql_select_single_cell("SELECT SUM(`Shifts`.`end` - `Shifts`.`start`) FROM `ShiftEntry` JOIN `Shifts` USING (`SID`) WHERE `Shifts`.`end` <UNIX_TIMESTAMP()");
$users_in_action = sql_select("SELECT `Shifts`.`start`, `Shifts`.`end` FROM `ShiftEntry` JOIN `Shifts` ON `Shifts`.`SID`=`ShiftEntry`.`SID` WHERE UNIX_TIMESTAMP() BETWEEN `Shifts`.`start` AND `Shifts`.`end`");
$users_in_action = DB::select('
SELECT `Shifts`.`start`, `Shifts`.`end`
FROM `ShiftEntry`
JOIN `Shifts` ON `Shifts`.`SID`=`ShiftEntry`.`SID`
WHERE UNIX_TIMESTAMP() BETWEEN `Shifts`.`start` AND `Shifts`.`end`
$messages = sql_select("SELECT * FROM `Messages` WHERE `SUID`='" . sql_escape($user['UID']) . "' OR `RUID`='" . sql_escape($user['UID']) . "' ORDER BY `isRead`,`Datum` DESC");
error(_("If you reset the key, the url to your iCal- and JSON-export and your atom feed changes! You have to update it in every application using one of these exports."), true),
_('If you reset the key, the url to your iCal- and JSON-export and your atom feed changes! You have to update it in every application using one of these exports.'),
error(_("Please enter your planned date of departure. It should be after your planned arrival date and after buildup start date and before teardown end date."));
error(_('Please enter your planned date of departure. It should be after your planned arrival date and after buildup start date and before teardown end date.'));
}
}
@ -74,7 +83,7 @@ function user_settings_main($user_source, $enable_tshirt_size, $tshirt_sizes) {
if ($valid) {
User_update($user_source);
success(_("Settings saved."));
success(_('Settings saved.'));
redirect(page_link_to('user_settings'));
}
@ -84,21 +93,23 @@ function user_settings_main($user_source, $enable_tshirt_size, $tshirt_sizes) {
/**
* Change user password.
*
* @param User $user_source
* The user
* @param array $user_source The user
*/
function user_settings_password($user_source) {
global $min_password_length;
if (! isset($_REQUEST['password']) || ! verify_password($_REQUEST['password'], $user_source['Passwort'], $user_source['UID'])) {
$rooms = sql_select("SELECT `RID` AS `id`, `Name` AS `name` FROM `Room` WHERE `show`='Y' ORDER BY `Name`");
if (count($rooms) == 0) {
error(_("The administration has not configured any rooms yet."));
/**
* @return array
*/
function load_rooms()
{
$rooms = DB::select(
'SELECT `RID` AS `id`, `Name` AS `name` FROM `Room` WHERE `show`=\'Y\' ORDER BY `Name`'
);
if (empty($rooms)) {
error(_('The administration has not configured any rooms yet.'));
redirect('?');
}
return $rooms;
}
function load_days() {
$days = sql_select_single_col("
/**
* @return array
*/
function load_days()
{
$days = DB::select('
SELECT DISTINCT DATE(FROM_UNIXTIME(`start`)) AS `id`, DATE(FROM_UNIXTIME(`start`)) AS `name`
FROM `Shifts`
ORDER BY `start`");
if (count($days) == 0) {
error(_("The administration has not configured any shifts yet."));
ORDER BY `start`
');
$days = array_map('array_shift', $days);
if (empty($days)) {
error(_('The administration has not configured any shifts yet.'));
redirect('?');
}
return $days;
}
function load_types() {
/**
* @return array|false
*/
function load_types()
{
global $user;
if (sql_num_query("SELECT `id`, `name` FROM `AngelTypes` WHERE `restricted` = 0") == 0) {
error(_("The administration has not configured any angeltypes yet - or you are not subscribed to any angeltype."));
if (!count(DB::select('SELECT `id`, `name` FROM `AngelTypes` WHERE `restricted` = 0'))) {
error(_('The administration has not configured any angeltypes yet - or you are not subscribed to any angeltype.'));
redirect('?');
}
$types = sql_select("SELECT `AngelTypes`.`id`, `AngelTypes`.`name`, (`AngelTypes`.`restricted`=0 OR (NOT `UserAngelTypes`.`confirm_user_id` IS NULL OR `UserAngelTypes`.`id` IS NULL)) as `enabled` FROM `AngelTypes` LEFT JOIN `UserAngelTypes` ON (`UserAngelTypes`.`angeltype_id`=`AngelTypes`.`id` AND `UserAngelTypes`.`user_id`='" . sql_escape($user['UID']) . "') ORDER BY `AngelTypes`.`name`");
$types = DB::select('
SELECT
`AngelTypes`.`id`,
`AngelTypes`.`name`,
(
`AngelTypes`.`restricted`=0
OR (
NOT `UserAngelTypes`.`confirm_user_id` IS NULL
OR `UserAngelTypes`.`id` IS NULL
)
) AS `enabled`
FROM `AngelTypes`
LEFT JOIN `UserAngelTypes`
ON (
`UserAngelTypes`.`angeltype_id`=`AngelTypes`.`id`
AND `UserAngelTypes`.`user_id`=?
)
ORDER BY `AngelTypes`.`name`
',
[
$user['UID'],
]
);
if (empty($types)) {
return sql_select("SELECT `id`, `name` FROM `AngelTypes` WHERE `restricted` = 0");
return DB::select('SELECT `id`, `name` FROM `AngelTypes` WHERE `restricted` = 0');
}
return $types;
}
function view_user_shifts() {
global $user, $privileges;
global $ical_shifts;
/**
* @return string
*/
function view_user_shifts()
{
global $user, $privileges, $ical_shifts;
$ical_shifts = [];
$days = load_days();
@ -132,66 +183,91 @@ function view_user_shifts() {
'task_notice' => '<sup>1</sup>' . _("The tasks shown here are influenced by the angeltypes you joined already!") . " <ahref=\"".page_link_to('angeltypes').'&action=about'."\">" . _("Description of the jobs.") . "</a>",
_('Export of shown shifts. <ahref="%s">iCal format</a> or <ahref="%s">JSON format</a> available (please keep secret, otherwise <ahref="%s">reset the api key</a>).'),
info(sprintf($supporter ? _("Do you really want to add supporter rights for %s to %s?") : _("Do you really want to remove supporter rights for %s from %s?"), $angeltype['name'], User_Nick_render($user)), true),
info(sprintf(
$supporter
? _('Do you really want to add supporter rights for %s to %s?')
: _('Do you really want to remove supporter rights for %s from %s?'),
button(user_link($user_source), _("Back to profile"), 'back')
button(user_link($user_source), _('Back to profile'), 'back')
]),
msg(),
form([
form_info(_("Privacy"), _("Your driving license information is only visible for supporters and admins.")),
form_checkbox('wants_to_drive', _("I am willing to drive a car for the event"), $wants_to_drive),
form_info(_('Privacy'), _('Your driving license information is only visible for supporters and admins.')),
form_checkbox('wants_to_drive', _('I am willing to operate cars for the PL'), $wants_to_drive),
div('panel panel-default', [
div('panel-body', [
form_checkbox('has_car', _("I have my own car with me and am willing to use it for the event (You'll get reimbursed for fuel)"), $user_driver_license['has_car']),
form_checkbox('email_shiftinfo', _("The engelsystem is allowed to send me an email (e.g. when my shifts change)"), $user_source['email_shiftinfo']),
form_checkbox('email_by_human_allowed', _("Humans are allowed to send me an email (e.g. for ticket vouchers)"), $user_source['email_by_human_allowed']),
$its_me ? info(glyph('info-sign') . _("Your night shifts between 2 and 8 am count twice."), true) : '',
$its_me && count($shifts) == 0 ? error(sprintf(_("Go to the <ahref=\"%s\">shifts table</a> to sign yourself up for some shifts."), page_link_to('user_shifts')), true) : ''
]);
$its_me ? info(glyph('info-sign') . _('Your night shifts between 2 and 8 am count twice.'), true) : '',
$its_me && count($shifts) == 0
? error(sprintf(
_('Go to the <ahref="%s">shifts table</a> to sign yourself up for some shifts.'),
if (!isset($user['planned_departure_date']) || $user['planned_departure_date'] == null) {
return _("Please enter your planned date of departure on your settings page to give us a feeling for teardown capacities.");
return _('Please enter your planned date of departure on your settings page to give us a feeling for teardown capacities.');
}
return null;
}
function render_user_freeloader_hint() {
global $user, $max_freeloadable_shifts;
/**
* @return string|null
*/
function render_user_freeloader_hint()
{
global $user;
if (User_is_freeloader($user)) {
return sprintf(_("You freeloaded at least %s shifts. Shift signup is locked. Please go to heavens desk to be unlocked again."), $max_freeloadable_shifts);
return sprintf(
_('You freeloaded at least %s shifts. Shift signup is locked. Please go to heavens desk to be unlocked again.'),
config('max_freeloadable_shifts')
);
}
return null;
}
// Hinweis für Engel, die noch nicht angekommen sind
function render_user_arrived_hint() {
/**
* Hinweis für Engel, die noch nicht angekommen sind
*
* @return string|null
*/
function render_user_arrived_hint()
{
global $user;
if ($user['Gekommen'] == 0) {
return _("You are not marked as arrived. Please go to heaven's desk, get your angel badge and/or tell them that you arrived already.");
return _('You are not marked as arrived. Please go to heaven\'s desk, get your angel badge and/or tell them that you arrived already.');
}
return null;
}
function render_user_tshirt_hint() {
global $enable_tshirt_size, $user;
/**
* @return string|null
*/
function render_user_tshirt_hint()
{
global $user;
if ($enable_tshirt_size && $user['Size'] == "") {
return _("You need to specify a tshirt size in your settings!");
if (config('enable_tshirt_size') && $user['Size'] == '') {
return _('You need to specify a tshirt size in your settings!');
}
return null;
}
function render_user_dect_hint() {
/**
* @return string|null
*/
function render_user_dect_hint()
{
global $user;
if ($user['DECT'] == "") {
return _("You need to specify a DECT phone number in your settings! If you don't have a DECT phone, just enter \"-\".");
if ($user['DECT'] == '') {
return _('You need to specify a DECT phone number in your settings! If you don\'t have a DECT phone, just enter \'-\'.');
...archangels closing the gates of heaven. <br>...somebody's stolen the power chord and now the battery is empty. <br>...DHCP decided to give me another ip address.
...archangels closing the gates of heaven. <br>
...somebody's stolen the power chord and now the battery is empty. <br>
<p>The great interest in becoming an angel and participating at 33C3 is is something we are grateful for every time. There is a record number of angels and helping volunteers this year.</p>
<p>We did anticipate a great number but we are overwhelmed by this endless wave of support. We do want to enable each and every one of you to be an angel at the congress, but sadly our resources and capacities at Heaven are limited. The amount of angels at this point is beyond our
planing and to ensure we can support the angels already checked in. We did make a choice never thought possible on a chaos event:</p>
<p>We closed the registration in the Engelsystem and at Heaven Desk at 19:00 27. Dec. 2016.</p>
<p>Everyone of us works for you to support you in being an angel, but the Heaven Desk and the Kitchen among others are limited and so we decided to focus our effort to support those of you already arrived to the best of our abilities.</p>
<p>
For the Heaven Team<br/> Agnes, Jen, LLF and Knuth
The great interest in becoming an angel and participating at 33C3 is is something we are
grateful for every time. There is a record number of angels and helping volunteers this year.
</p>
<p>
We did anticipate a great number but we are overwhelmed by this endless wave of support. We do
want to enable each and every one of you to be an angel at the congress, but sadly our resources
and capacities at Heaven are limited. The amount of angels at this point is beyond our
planing and to ensure we can support the angels already checked in. We did make a choice never
thought possible on a chaos event:
</p>
<p>
We closed the registration in the Engelsystem and at Heaven Desk at 19:00 27. Dec. 2016.
</p>
<p>
Everyone of us works for you to support you in being an angel, but the Heaven Desk and the
Kitchen among others are limited and so we decided to focus our effort to support those of you
The original system was written by <ahref="https://github.com/cookieBerlin/engelsystem">cookie</a>. It was then completely rewritten and greatly enhanced by <ahref="http://notrademark.de/">msquare</a> and <ahref="http://mortzu.de/">mortzu</a> of <ahref="http://planetcyborg.de">planet
cyborg</a>, <ahref="http://jplitza.de/">jplitza</a> and gnomus.
The original system was written by <ahref="https://github.com/cookieBerlin/engelsystem">cookie</a>.
It was then completely rewritten and greatly enhanced by <ahref="http://notrademark.de/">msquare</a>
and <ahref="http://myigel.name/">MyIgel</a>,
<ahref="http://mortzu.de/">mortzu</a> of <ahref="http://planetcyborg.de">planet cyborg</a>,
<ahref="http://jplitza.de/">jplitza</a> and gnomus.
</p>
<p>
Please look at the <ahref="https://github.com/engelsystem/engelsystem/graphs/contributors">contributor list on github</a> for a more complete version.
Please look at the <ahref="https://github.com/engelsystem/engelsystem/graphs/contributors">contributor
list on github</a> for a more complete version.
</p>
</div>
<divclass="col-md-4">
<h2>Hosting</h2>
<p>
Webspace, development platform and domain is currently provided by <ahref="https://www.wybt.net/">would you buy this?</a> (ichdasich)<br/> and adminstrated by <ahref="http://mortzu.de/">mortzu</a>, <ahref="http://derf.homelinux.org/">derf</a> and ichdasich.
Webspace, development platform and domain is currently provided by
<ahref="https://www.wybt.net/">would you buy this?</a> (ichdasich)<br/>
and adminstrated by <ahref="http://mortzu.de/">mortzu</a>,