From dfe234a7aaaacedcc94716f955b44a1b50c4a057 Mon Sep 17 00:00:00 2001
From: emkael <emkael@tlen.pl>
Date: Wed, 6 Apr 2016 13:50:31 +0200
Subject:  * components -> controls

---
 app/php/application.xml                     |   2 +-
 app/php/components/CalendarScaffold.php     | 127 ----------------------------
 app/php/components/CalendarScaffold.tpl     |  57 -------------
 app/php/components/HeaderMenu.php           |  16 ----
 app/php/components/HeaderMenu.tpl           |  22 -----
 app/php/components/LoginBox.php             |  23 -----
 app/php/components/LoginBox.tpl             |  29 -------
 app/php/components/PasswordChange.php       |  38 ---------
 app/php/components/PasswordChange.tpl       |  52 ------------
 app/php/components/RegistrationForm.php     |  26 ------
 app/php/components/RegistrationForm.tpl     |  59 -------------
 app/php/components/SafeActiveFileUpload.php |  12 ---
 app/php/components/SafeFileUpload.php       |  34 --------
 app/php/components/TimezoneSelect.php       |  49 -----------
 app/php/components/TimezoneSelect.tpl       |   4 -
 app/php/components/UpcomingEvents.php       |  35 --------
 app/php/components/UpcomingEvents.tpl       |  10 ---
 app/php/components/UserSelection.php        |  39 ---------
 app/php/components/UserSelection.tpl        |  25 ------
 app/php/components/config.xml               |   6 --
 app/php/controls/CalendarScaffold.php       | 127 ++++++++++++++++++++++++++++
 app/php/controls/CalendarScaffold.tpl       |  57 +++++++++++++
 app/php/controls/HeaderMenu.php             |  16 ++++
 app/php/controls/HeaderMenu.tpl             |  22 +++++
 app/php/controls/LoginBox.php               |  23 +++++
 app/php/controls/LoginBox.tpl               |  29 +++++++
 app/php/controls/PasswordChange.php         |  38 +++++++++
 app/php/controls/PasswordChange.tpl         |  52 ++++++++++++
 app/php/controls/RegistrationForm.php       |  26 ++++++
 app/php/controls/RegistrationForm.tpl       |  59 +++++++++++++
 app/php/controls/SafeActiveFileUpload.php   |  12 +++
 app/php/controls/SafeFileUpload.php         |  34 ++++++++
 app/php/controls/TimezoneSelect.php         |  49 +++++++++++
 app/php/controls/TimezoneSelect.tpl         |   4 +
 app/php/controls/UpcomingEvents.php         |  35 ++++++++
 app/php/controls/UpcomingEvents.tpl         |  10 +++
 app/php/controls/UserSelection.php          |  39 +++++++++
 app/php/controls/UserSelection.tpl          |  25 ++++++
 app/php/controls/config.xml                 |   6 ++
 39 files changed, 664 insertions(+), 664 deletions(-)
 delete mode 100644 app/php/components/CalendarScaffold.php
 delete mode 100644 app/php/components/CalendarScaffold.tpl
 delete mode 100644 app/php/components/HeaderMenu.php
 delete mode 100644 app/php/components/HeaderMenu.tpl
 delete mode 100644 app/php/components/LoginBox.php
 delete mode 100644 app/php/components/LoginBox.tpl
 delete mode 100644 app/php/components/PasswordChange.php
 delete mode 100644 app/php/components/PasswordChange.tpl
 delete mode 100644 app/php/components/RegistrationForm.php
 delete mode 100644 app/php/components/RegistrationForm.tpl
 delete mode 100644 app/php/components/SafeActiveFileUpload.php
 delete mode 100644 app/php/components/SafeFileUpload.php
 delete mode 100644 app/php/components/TimezoneSelect.php
 delete mode 100644 app/php/components/TimezoneSelect.tpl
 delete mode 100644 app/php/components/UpcomingEvents.php
 delete mode 100644 app/php/components/UpcomingEvents.tpl
 delete mode 100644 app/php/components/UserSelection.php
 delete mode 100644 app/php/components/UserSelection.tpl
 delete mode 100644 app/php/components/config.xml
 create mode 100644 app/php/controls/CalendarScaffold.php
 create mode 100644 app/php/controls/CalendarScaffold.tpl
 create mode 100644 app/php/controls/HeaderMenu.php
 create mode 100644 app/php/controls/HeaderMenu.tpl
 create mode 100644 app/php/controls/LoginBox.php
 create mode 100644 app/php/controls/LoginBox.tpl
 create mode 100644 app/php/controls/PasswordChange.php
 create mode 100644 app/php/controls/PasswordChange.tpl
 create mode 100644 app/php/controls/RegistrationForm.php
 create mode 100644 app/php/controls/RegistrationForm.tpl
 create mode 100644 app/php/controls/SafeActiveFileUpload.php
 create mode 100644 app/php/controls/SafeFileUpload.php
 create mode 100644 app/php/controls/TimezoneSelect.php
 create mode 100644 app/php/controls/TimezoneSelect.tpl
 create mode 100644 app/php/controls/UpcomingEvents.php
 create mode 100644 app/php/controls/UpcomingEvents.tpl
 create mode 100644 app/php/controls/UserSelection.php
 create mode 100644 app/php/controls/UserSelection.tpl
 create mode 100644 app/php/controls/config.xml

diff --git a/app/php/application.xml b/app/php/application.xml
index 7b4741a..8b76f74 100644
--- a/app/php/application.xml
+++ b/app/php/application.xml
@@ -6,7 +6,7 @@
   <include file="Application.facades.config" />
   <include file="Application.user.config" />
   <include file="Application.url.config" />
-  <include file="Application.components.config" />
+  <include file="Application.controls.config" />
 
   <services>
     <service id="page" class="TPageService" />
diff --git a/app/php/components/CalendarScaffold.php b/app/php/components/CalendarScaffold.php
deleted file mode 100644
index f265d53..0000000
--- a/app/php/components/CalendarScaffold.php
+++ /dev/null
@@ -1,127 +0,0 @@
-<?php
-
-Prado::using('System.Web.UI.ActiveControls.TActiveDataGrid');
-Prado::using('System.Web.UI.ActiveControls.TActiveTextBox');
-Prado::using('Application.facades.CalendarFacade');
-
-class CalendarScaffold extends TTemplateControl {
-
-    public function setFacade(Facade $facade) {
-        $this->setViewState('Facade', $facade);
-    }
-
-    public function getFacade() {
-        return $this->getViewState('Facade');
-    }
-
-    public function onPreRender($param) {
-        parent::onPreRender($param);
-        if (!$this->Page->IsPostBack && !$this->Page->IsCallBack) {
-            $this->_rebindData();
-        }
-    }
-
-    private function _rebindCalendars(array $calendars) {
-        $this->Calendars->DataSource = $calendars;
-        $this->Calendars->dataBind();
-    }
-
-    private function _rebindCategoryList(array $categories) {
-        foreach ($this->Calendars->Columns as $column) {
-            if ($column->ID === 'Category'
-                && $column instanceof TActiveDropDownListColumn) {
-                $column->ListDataSource = $categories;
-            }
-        }
-    }
-
-    private function _rebindData($refresh = FALSE) {
-        $this->_rebindCategoryList(
-            $this->_getCategories()
-        );
-        $this->_rebindCalendars(
-            $this->_getCalendars($refresh)
-        );
-    }
-
-    private function _getCalendars($refresh = FALSE) {
-        if ($refresh) {
-            $this->clearViewState('Calendars');
-        }
-        $calendars = $this->getViewState(
-            'Calendars',
-            $this->getFacade()->getAll()
-        );
-        $this->setViewState('Calendars', $calendars);
-        return $calendars;
-    }
-
-    private function _getCategories() {
-        $categories = $this->getViewState(
-            'Categories',
-            $this->getFacade()->getCategories()
-        );
-        $this->setViewState('Categories', $categories);
-        return $categories;
-    }
-
-    public function editRow($sender, $param) {
-        $this->Calendars->EditItemIndex = $param->Item->ItemIndex;
-        $this->_rebindData();
-    }
-
-    private function _compileSaveData(TDataGridItem $item) {
-        return [
-            'CategoryID' => $item->Category->DropDownList->SelectedValue,
-            'Visible' => $item->Visible->CheckBox->Checked,
-            'CustomName' => $item->CustomName->TextBox->SafeText,
-            'CustomUrl' => $item->CustomUrl->TextBox->SafeText,
-            'CustomImage' => $item->CustomImage->Value->SafeText
-        ];
-    }
-
-    public function saveRow($sender, $param) {
-        $calendar = $this->getFacade()->get(
-            $sender->DataKeys[$param->Item->ItemIndex]
-        );
-        if ($calendar) {
-            foreach ($calendar as $c) {
-                $c->saveData($this->_compileSaveData($param->Item));
-            }
-        } else {
-            throw new TInvalidDataValueException('Calendar not found');
-        }
-        $this->Calendars->EditItemIndex = -1;
-        $this->_rebindData(TRUE);
-    }
-
-    public function cancelRowEdit($sender, $param) {
-        $this->Calendars->EditItemIndex = -1;
-        $this->_rebindData();
-    }
-
-    public function uploadRowFile($sender, $param) {
-        $fileType = $sender->getFileType();
-        if (preg_match('/^image\//', $fileType)) {
-            $calendar = $this->getFacade()->get($sender->CustomData);
-            if ($calendar) {
-                $targetFile = $calendar[0]->getCustomImagePath(
-                    $sender->getLocalName(),
-                    $fileType
-                );
-                if ($sender->saveAs($targetFile)) {
-                    $sender->NamingContainer->CustomImage->Value->Text = basename(
-                        $targetFile
-                    );
-                }
-            } else {
-                throw new TInvalidDataValueException('Calendar not found');
-            }
-        } else {
-            throw new TInvalidDataTypeException('Invalid file type');
-        }
-    }
-
-}
-
-?>
diff --git a/app/php/components/CalendarScaffold.tpl b/app/php/components/CalendarScaffold.tpl
deleted file mode 100644
index 6688869..0000000
--- a/app/php/components/CalendarScaffold.tpl
+++ /dev/null
@@ -1,57 +0,0 @@
-<com:TActiveDataGrid ID="Calendars"
-                     DataKeyField="UID"
-                     AutoGenerateColumns="false"
-                     OnEditCommand="editRow"
-                     OnCancelCommand="cancelRowEdit"
-                     OnUpdateCommand="saveRow">
-  <com:TActiveBoundColumn ID="Name"
-                          ReadOnly="true"
-                          HeaderText="Calendar"
-                          DataField="Name" />
-  <com:TActiveHyperLinkColumn ID="Website"
-                              HeaderText="WWW"
-                              Text="[www]"
-                              Target="_blank"
-                              DataNavigateUrlField="Website" />
-  <com:TActiveHyperLinkColumn ID="Url"
-                              HeaderText="ICS"
-                              Text="[ics]"
-                              Target="_blank"
-                              DataNavigateUrlField="Url" />
-  <com:TActiveDropDownListColumn ID="Category"
-                                 HeaderText="Category"
-                                 DataTextField="Category.Name"
-                                 DataValueField="CategoryID"
-                                 ListValueField="ID"
-                                 ListTextField="Name" />
-  <com:TActiveCheckBoxColumn ID="Visible"
-                             HeaderText="Default"
-                             DataField="Visible" />
-  <com:TActiveBoundColumn ID="CustomName"
-                          HeaderText="Name"
-                          DataField="CustomName" />
-  <com:TActiveBoundColumn ID="CustomUrl"
-                          HeaderText="URL"
-                          DataField="CustomUrl" />
-  <com:TActiveTemplateColumn ID="CustomImage"
-                             HeaderText="Image">
-    <prop:ItemTemplate>
-      <com:TImage>
-        <prop:ImageUrl><%# $this->Parent->Data->CustomImageUrl %></prop:ImageUrl>
-      </com:TImage>
-    </prop:ItemTemplate>
-    <prop:EditItemTemplate>
-      <com:TActiveTextBox ID="Value">
-        <prop:Text><%# $this->Parent->Data->CustomImage %></prop:Text>
-      </com:TActiveTextBox><br />
-      <com:SafeActiveFileUpload
-          OnFileUpload="SourceTemplateControl.uploadRowFile">
-        <prop:CustomData><%# $this->Parent->Data->UID %></prop:CustomData>
-      </com:SafeActiveFileUpload>
-    </prop:EditItemTemplate>
-  </com:TActiveTemplateColumn>
-  <com:TActiveEditCommandColumn
-      HeaderText="Edit"
-      UpdateText="Save"
-      CancelText="Cancel" />
-</com:TActiveDataGrid>
diff --git a/app/php/components/HeaderMenu.php b/app/php/components/HeaderMenu.php
deleted file mode 100644
index bffe4d2..0000000
--- a/app/php/components/HeaderMenu.php
+++ /dev/null
@@ -1,16 +0,0 @@
-<?php
-
-Prado::using('System.Web.UI.ActiveControls.TActiveLinkButton');
-
-class HeaderMenu extends TTemplateControl {
-
-    public function logoutUser($sender, $param) {
-        $this->Application->getModule('auth')->logout();
-        $this->Response->redirect(
-            $this->Service->ConstructUrl(NULL)
-        );
-    }
-
-}
-
-?>
diff --git a/app/php/components/HeaderMenu.tpl b/app/php/components/HeaderMenu.tpl
deleted file mode 100644
index 603a231..0000000
--- a/app/php/components/HeaderMenu.tpl
+++ /dev/null
@@ -1,22 +0,0 @@
-<nav role="navigation">
-  <com:THyperLink Text="Login">
-    <prop:NavigateUrl><%= $this->Service->constructUrl('Login') %></prop:NavigateUrl>
-    <prop:Visible><%= $this->User->IsGuest %></prop:Visible>
-  </com:THyperLink>
-  <com:THyperLink Text="Profile">
-    <prop:NavigateUrl><%= $this->Service->constructUrl('Profile') %></prop:NavigateUrl>
-    <prop:Visible><%= !$this->User->IsGuest %></prop:Visible>
-  </com:THyperLink>
-  <com:TActiveLinkButton OnCommand="logoutUser">
-    <prop:Text>Logout (<%= $this->User->Name %>)</prop:Text>
-    <prop:Visible><%= !$this->User->IsGuest %></prop:Visible>
-  </com:TActiveLinkButton>
-  <com:THyperLink Text="New user">
-    <prop:NavigateUrl><%= $this->Service->constructUrl('Signup') %></prop:NavigateUrl>
-    <prop:Visible><%= $this->User->getIsAdmin() %></prop:Visible>
-  </com:THyperLink>
-  <com:THyperLink Text="Admin calendars">
-    <prop:NavigateUrl><%= $this->Service->constructUrl('Admin') %></prop:NavigateUrl>
-    <prop:Visible><%= $this->User->getIsAdmin() %></prop:Visible>
-  </com:THyperLink>
-</nav>
diff --git a/app/php/components/LoginBox.php b/app/php/components/LoginBox.php
deleted file mode 100644
index 33bbcc1..0000000
--- a/app/php/components/LoginBox.php
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-
-class LoginBox extends TTemplateControl {
-
-    public function loginUser($sender, $param) {
-        if ($this->Page->IsValid) {
-            $this->Response->redirect(
-                $this->Application->getModule('auth')->ReturnUrl
-                ?: $this->Service->constructUrl(NULL)
-            );
-        }
-    }
-
-    public function validatePassword($sender, $param) {
-        $param->IsValid = $this->Application->getModule('auth')->login(
-            $this->Login->Text,
-            $this->Password->Text
-        );
-    }
-
-}
-
-?>
diff --git a/app/php/components/LoginBox.tpl b/app/php/components/LoginBox.tpl
deleted file mode 100644
index d3e1a1e..0000000
--- a/app/php/components/LoginBox.tpl
+++ /dev/null
@@ -1,29 +0,0 @@
-Username:
-<com:TTextBox ID="Login"
-              ValidationGroup="LoginGroup" />
-<com:TRequiredFieldValidator
-    ControlToValidate="Login"
-    Display="Dynamic"
-    ErrorMessage="Username cannot be empty"
-    ValidationGroup="LoginGroup" />
-<br />
-Password:
-<com:TTextBox ID="Password"
-              TextMode="Password"
-              ValidationGroup="LoginGroup" />
-<com:TRequiredFieldValidator
-    ControlToValidate="Password"
-    Display="Dynamic"
-    ErrorMessage="Password cannot be empty"
-    ValidationGroup="LoginGroup" />
-<com:TCustomValidator
-    ControlToValidate="Password"
-    OnServerValidate="validatePassword"
-    Display="Dynamic"
-    ErrorMessage="Username and password don't match"
-    ValidationGroup="LoginGroup" />
-<br />
-<com:TButton
-    Text="Login"
-    OnCommand="loginUser"
-    ValidationGroup="LoginGroup" />
diff --git a/app/php/components/PasswordChange.php b/app/php/components/PasswordChange.php
deleted file mode 100644
index 9f2ac7f..0000000
--- a/app/php/components/PasswordChange.php
+++ /dev/null
@@ -1,38 +0,0 @@
-<?php
-
-Prado::using('Application.user.DbUser');
-
-class PasswordChange extends TTemplateControl {
-
-    public function getUserToChange() {
-        return $this->getControlState('user');
-    }
-
-    public function setUserToChange(DbUser $user) {
-        if ($user->IsGuest) {
-            throw new TInvalidDataValueException(
-                'Password change impossible for guest user'
-            );
-        }
-        $this->setControlState('user', $user);
-    }
-
-    public function checkPassword($sender, $param) {
-        $param->IsValid = DbUser::verifyPassword(
-            $this->Password->Text, $this->UserToChange->getPassword()
-        );
-    }
-
-    public function changePassword($sender, $param) {
-        $this->SuccessMessage->Visible = FALSE;
-        if ($this->Page->IsValid) {
-            $this->UserToChange->changePassword(
-                $this->NewPassword->Text
-            );
-            $this->SuccessMessage->Visible = TRUE;
-        }
-    }
-
-}
-
-?>
diff --git a/app/php/components/PasswordChange.tpl b/app/php/components/PasswordChange.tpl
deleted file mode 100644
index 915e8b3..0000000
--- a/app/php/components/PasswordChange.tpl
+++ /dev/null
@@ -1,52 +0,0 @@
-Change password<br />
-Current password:
-<com:TTextBox ID="Password"
-              TextMode="Password"
-              ValidationGroup="ChangePasswordGroup" />
-<com:TRequiredFieldValidator
-    ControlToValidate="Password"
-    Display="Dynamic"
-    ErrorMessage="Current password cannot be empty"
-    ValidationGroup="ChangePasswordGroup" />
-<com:TCustomValidator
-    ControlToValidate="Password"
-    OnServerValidate="checkPassword"
-    Display="Dynamic"
-    ErrorMessage="Password is incorrect"
-    ValidationGroup="ChangePasswordGroup" />
-<br />
-New password:
-<com:TTextBox ID="NewPassword"
-              TextMode="Password"
-              ValidationGroup="ChangePasswordGroup" />
-<com:TRequiredFieldValidator
-    ControlToValidate="NewPassword"
-    Display="Dynamic"
-    ErrorMessage="New password cannot be empty"
-    ValidationGroup="ChangePasswordGroup" />
-<br />
-Repeat password:
-<com:TTextBox ID="ReNewPassword"
-              TextMode="Password"
-              ValidationGroup="ChangePasswordGroup" />
-<com:TRequiredFieldValidator
-    ControlToValidate="ReNewPassword"
-    Display="Dynamic"
-    ErrorMessage="New password cannot be empty"
-    ValidationGroup="ChangePasswordGroup" />
-<com:TCompareValidator
-    ControlToValidate="ReNewPassword"
-    ControlToCompare="NewPassword"
-    DataType="String"
-    Operator="Equal"
-    Display="Dynamic"
-    ErrorMessage="Passwords don't match"
-    ValidationGroup="ChangePasswordGroup" />
-<br />
-<com:TButton
-    Text="Change password"
-    OnCommand="changePassword"
-    ValidationGroup="ChangePasswordGroup" />
-<com:TLabel ID="SuccessMessage"
-            Text="Your password has been changed"
-            Visible="false" />
diff --git a/app/php/components/RegistrationForm.php b/app/php/components/RegistrationForm.php
deleted file mode 100644
index 71d4df1..0000000
--- a/app/php/components/RegistrationForm.php
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-
-Prado::using('Application.model.User');
-
-class RegistrationForm extends TTemplateControl {
-
-    public function checkUsername($sender, $param) {
-        $param->IsValid = !User::finder()->countByLogin($this->Login->SafeText);
-    }
-
-    public function registerUser($sender, $param) {
-        if ($this->Page->IsValid) {
-            $newUser = new User();
-            $newUser->Login = $this->Login->SafeText;
-            $newUser->Password = DbUser::generatePassword($this->Password->Text);
-            $newUser->IsAdmin = $this->Admin->Checked;
-            $newUser->save();
-            $this->Response->redirect(
-                $this->Service->constructUrl(NULL)
-            );
-        }
-    }
-
-}
-
-?>
diff --git a/app/php/components/RegistrationForm.tpl b/app/php/components/RegistrationForm.tpl
deleted file mode 100644
index ffa4778..0000000
--- a/app/php/components/RegistrationForm.tpl
+++ /dev/null
@@ -1,59 +0,0 @@
-Username:
-<com:TTextBox ID="Login"
-              ValidationGroup="SignupGroup" />
-<com:TRequiredFieldValidator
-    ControlToValidate="Login"
-    Display="Dynamic"
-    ErrorMessage="Username cannot be empty"
-    ValidationGroup="SignupGroup" />
-<com:TRegularExpressionValidator
-    ControlToValidate="Login"
-    RegularExpression="[a-zA-Z0-9_]{6,255}"
-    Display="Dynamic"
-    ErrorMessage="Username must contain 6-255 characters, all Latin alphanumeric or underscore"
-    ValidationGroup="SignupGroup" />
-<com:TCustomValidator
-    ControlToValidate="Login"
-    OnServerValidate="checkUsername"
-    Display="Dynamic"
-    ErrorMessage="Username already exists"
-    ValidationGroup="SignupGroup" />
-<br />
-Password:
-<com:TTextBox ID="Password"
-              TextMode="Password"
-              ValidationGroup="SignupGroup" />
-<com:TRequiredFieldValidator
-    ControlToValidate="Password"
-    Display="Dynamic"
-    ErrorMessage="Password cannot be empty"
-    ValidationGroup="SignupGroup" />
-<br />
-Repeat password:
-<com:TTextBox ID="RePassword"
-              TextMode="Password"
-              ValidationGroup="SignupGroup" />
-<com:TRequiredFieldValidator
-    ControlToValidate="RePassword"
-    Display="Dynamic"
-    ErrorMessage="Password cannot be empty"
-    ValidationGroup="SignupGroup" />
-<com:TCompareValidator
-    ControlToValidate="RePassword"
-    ControlToCompare="Password"
-    DataType="String"
-    Operator="Equal"
-    Display="Dynamic"
-    ErrorMessage="Passwords don't match"
-    ValidationGroup="SignupGroup" />
-<br />
-Admin:
-<com:TCheckBox ID="Admin"
-               ValidationGroup="SignupGroup" />
-<br />
-<com:TButton
-    Text="Create"
-    OnCommand="registerUser"
-    ValidationGroup="SignupGroup" />
-<com:TValidationSummary
-    ValidationGroup="SignupGroup" />
diff --git a/app/php/components/SafeActiveFileUpload.php b/app/php/components/SafeActiveFileUpload.php
deleted file mode 100644
index 9b8e2a8..0000000
--- a/app/php/components/SafeActiveFileUpload.php
+++ /dev/null
@@ -1,12 +0,0 @@
-<?php
-
-Prado::using('System.Web.UI.ActiveControls.TActiveFileUpload');
-Prado::using('Application.components.SafeFileUpload');
-
-class SafeActiveFileUpload extends TActiveFileUpload {
-
-    use MimeTypeCheckForFileUpload;
-
-}
-
-?>
diff --git a/app/php/components/SafeFileUpload.php b/app/php/components/SafeFileUpload.php
deleted file mode 100644
index 98e120a..0000000
--- a/app/php/components/SafeFileUpload.php
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php
-
-class SafeFileUpload extends TFileUpload {
-
-    use MimeTypeCheckForFileUpload;
-
-}
-
-trait MimeTypeCheckForFileUpload {
-
-    protected $_isSecure = TRUE;
-
-    public function getIsSecure() {
-        return $this->_isSecure;
-    }
-
-    public function setIsSecure($bool) {
-        $this->_isSecure = $bool;
-    }
-
-    public function getFileType() {
-        $type = parent::getFileType();
-        if ($this->getIsSecure()) {
-            $fileInfo = new finfo(FILEINFO_MIME_TYPE);
-            return $fileInfo->file($this->getLocalName());
-        }
-        else {
-            return $type;
-        }
-    }
-
-}
-
-?>
diff --git a/app/php/components/TimezoneSelect.php b/app/php/components/TimezoneSelect.php
deleted file mode 100644
index 3302e2a..0000000
--- a/app/php/components/TimezoneSelect.php
+++ /dev/null
@@ -1,49 +0,0 @@
-<?php
-
-Prado::using('Application.user.DbUser');
-Prado::using('Application.dto.TimezoneDTO');
-
-class TimezoneSelect extends TTemplateControl {
-
-    public function getUserToChange() {
-        return $this->getControlState('user');
-    }
-
-    public function setUserToChange(DbUser $user) {
-        if ($user->IsGuest) {
-            throw new TInvalidDataValueException(
-                'Timezone preference change impossible for guest user'
-            );
-        }
-        $this->setControlState('user', $user);
-    }
-
-    public function onPreRender($param) {
-        parent::onPreRender($param);
-        $this->Timezones->DataSource = $this->_getTimezones();
-        $this->Timezones->DataValueField = 'Name';
-        $this->Timezones->DataTextField = 'Label';
-        $this->Timezones->dataBind();
-        $this->Timezones->setSelectedValue(
-            $this->UserToChange->getTimezonePreference()->Name
-        );
-    }
-
-    public function saveTimezone($sender, $param) {
-        $this->UserToChange->setTimezonePreference($this->Timezones->SelectedValue);
-    }
-
-    private function _getTimezones() {
-        $timezones = array_map(
-            function($tz) {
-                return new TimezoneDTO($tz);
-            },
-            DateTimeZone::listIdentifiers()
-        );
-        usort($timezones, ['TimezoneDTO', '__compare']);
-        return $timezones;
-    }
-
-}
-
-?>
diff --git a/app/php/components/TimezoneSelect.tpl b/app/php/components/TimezoneSelect.tpl
deleted file mode 100644
index 2d40014..0000000
--- a/app/php/components/TimezoneSelect.tpl
+++ /dev/null
@@ -1,4 +0,0 @@
-<com:TDropDownList ID="Timezones" />
-<com:TButton
-    Text="Save timezone"
-    OnCommand="saveTimezone" />
diff --git a/app/php/components/UpcomingEvents.php b/app/php/components/UpcomingEvents.php
deleted file mode 100644
index 27fa8c6..0000000
--- a/app/php/components/UpcomingEvents.php
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php
-
-Prado::using('Application.facades.EventFacade');
-
-class UpcomingEvents extends TTemplateControl {
-
-    public function getUserToDisplay() {
-        return $this->getControlState('user');
-    }
-
-    public function setUserToDisplay($user) {
-        $this->setControlState('user', $user);
-    }
-
-    public function onPreRender($param) {
-        parent::onPreRender($param);
-        $this->Events->setDataSource(
-            $this->_getEventsForUser($this->UserToDisplay)
-        );
-        $this->Events->dataBind();
-    }
-
-    private function _getEventsForUser(DbUser $user) {
-        $utc = new DateTimeZone('UTC');
-        $dateFrom = new DateTime('now', $utc);
-        $dateTo = new DateTime('+7 days', $utc);
-        return EventFacade::getInstance()->getTimeframeListForUser(
-            $user,
-            $dateFrom, $dateTo
-        );
-    }
-
-}
-
-?>
diff --git a/app/php/components/UpcomingEvents.tpl b/app/php/components/UpcomingEvents.tpl
deleted file mode 100644
index d660f54..0000000
--- a/app/php/components/UpcomingEvents.tpl
+++ /dev/null
@@ -1,10 +0,0 @@
-Upcoming events:
-<br />
-<com:TRepeater ID="Events">
-  <prop:ItemTemplate>
-    <%# $this->Data->DateString %>
-    <%# $this->Data->Name %>
-    (<%# $this->Data->Calendar->Name %>)
-    <br />
-  </prop:ItemTemplate>
-</com:TRepeater>
diff --git a/app/php/components/UserSelection.php b/app/php/components/UserSelection.php
deleted file mode 100644
index 6ae68e4..0000000
--- a/app/php/components/UserSelection.php
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-
-Prado::using('Application.facades.CalendarFacade');
-
-class UserSelection extends TTemplateControl {
-
-    public function getUserToDisplay() {
-        return $this->getControlState('user');
-    }
-
-    public function setUserToDisplay($user) {
-        $this->setControlState('user', $user);
-    }
-
-    public function onPreRender($param) {
-        parent::onPreRender($param);
-        $this->Categories->setDataSource(
-            $this->_getUserSelection($this->UserToDisplay)
-        );
-        $this->Categories->dataBind();
-    }
-
-    public function categoryDataBind($sender, $param) {
-        $param->Item->Calendars->setDataSource($param->Item->Data->Calendars);
-        $param->Item->Calendars->dataBind();
-    }
-
-    public function removeFromSelection($sender, $param) {
-        if (!$this->UserToDisplay->IsGuest) {
-        }
-    }
-
-    private function _getUserSelection(DbUser $user) {
-        return CalendarFacade::getInstance()->getPreferenceList($user);
-    }
-
-}
-
-?>
diff --git a/app/php/components/UserSelection.tpl b/app/php/components/UserSelection.tpl
deleted file mode 100644
index 14035ca..0000000
--- a/app/php/components/UserSelection.tpl
+++ /dev/null
@@ -1,25 +0,0 @@
-Selected calendars:
-<br />
-<com:TRepeater ID="Categories" OnItemDataBound="categoryDataBind">
-  <prop:ItemTemplate>
-    <%# $this->Data->Name %><br />
-    <com:TRepeater ID="Calendars">
-      <prop:ItemTemplate>
-        <com:TLinkButton
-            Text="[X]"
-            OnCommand="SourceTemplateControl.removeFromSelection">
-          <prop:CommandParameter><%# $this->Data->ID %></prop:CommandParameter>
-          <prop:Visible><%# !$this->SourceTemplateControl->UserToDisplay->IsGuest %></prop:Visible>
-        </com:TLinkButton>
-        <%# $this->Data->Name %>
-        <com:THyperLink
-            Text="(www)"
-            Target="_blank">
-          <prop:NavigateUrl><%# $this->Data->Website %></prop:NavigateUrl>
-        </com:THyperLink>
-        <br />
-      </prop:ItemTemplate>
-    </com:TRepeater>
-    <br />
-  </prop:ItemTemplate>
-</com:TRepeater>
diff --git a/app/php/components/config.xml b/app/php/components/config.xml
deleted file mode 100644
index f75e19f..0000000
--- a/app/php/components/config.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<configuration>
-  <paths>
-    <using namespace="Application.components.*" />
-  </paths>
-</configuration>
diff --git a/app/php/controls/CalendarScaffold.php b/app/php/controls/CalendarScaffold.php
new file mode 100644
index 0000000..f265d53
--- /dev/null
+++ b/app/php/controls/CalendarScaffold.php
@@ -0,0 +1,127 @@
+<?php
+
+Prado::using('System.Web.UI.ActiveControls.TActiveDataGrid');
+Prado::using('System.Web.UI.ActiveControls.TActiveTextBox');
+Prado::using('Application.facades.CalendarFacade');
+
+class CalendarScaffold extends TTemplateControl {
+
+    public function setFacade(Facade $facade) {
+        $this->setViewState('Facade', $facade);
+    }
+
+    public function getFacade() {
+        return $this->getViewState('Facade');
+    }
+
+    public function onPreRender($param) {
+        parent::onPreRender($param);
+        if (!$this->Page->IsPostBack && !$this->Page->IsCallBack) {
+            $this->_rebindData();
+        }
+    }
+
+    private function _rebindCalendars(array $calendars) {
+        $this->Calendars->DataSource = $calendars;
+        $this->Calendars->dataBind();
+    }
+
+    private function _rebindCategoryList(array $categories) {
+        foreach ($this->Calendars->Columns as $column) {
+            if ($column->ID === 'Category'
+                && $column instanceof TActiveDropDownListColumn) {
+                $column->ListDataSource = $categories;
+            }
+        }
+    }
+
+    private function _rebindData($refresh = FALSE) {
+        $this->_rebindCategoryList(
+            $this->_getCategories()
+        );
+        $this->_rebindCalendars(
+            $this->_getCalendars($refresh)
+        );
+    }
+
+    private function _getCalendars($refresh = FALSE) {
+        if ($refresh) {
+            $this->clearViewState('Calendars');
+        }
+        $calendars = $this->getViewState(
+            'Calendars',
+            $this->getFacade()->getAll()
+        );
+        $this->setViewState('Calendars', $calendars);
+        return $calendars;
+    }
+
+    private function _getCategories() {
+        $categories = $this->getViewState(
+            'Categories',
+            $this->getFacade()->getCategories()
+        );
+        $this->setViewState('Categories', $categories);
+        return $categories;
+    }
+
+    public function editRow($sender, $param) {
+        $this->Calendars->EditItemIndex = $param->Item->ItemIndex;
+        $this->_rebindData();
+    }
+
+    private function _compileSaveData(TDataGridItem $item) {
+        return [
+            'CategoryID' => $item->Category->DropDownList->SelectedValue,
+            'Visible' => $item->Visible->CheckBox->Checked,
+            'CustomName' => $item->CustomName->TextBox->SafeText,
+            'CustomUrl' => $item->CustomUrl->TextBox->SafeText,
+            'CustomImage' => $item->CustomImage->Value->SafeText
+        ];
+    }
+
+    public function saveRow($sender, $param) {
+        $calendar = $this->getFacade()->get(
+            $sender->DataKeys[$param->Item->ItemIndex]
+        );
+        if ($calendar) {
+            foreach ($calendar as $c) {
+                $c->saveData($this->_compileSaveData($param->Item));
+            }
+        } else {
+            throw new TInvalidDataValueException('Calendar not found');
+        }
+        $this->Calendars->EditItemIndex = -1;
+        $this->_rebindData(TRUE);
+    }
+
+    public function cancelRowEdit($sender, $param) {
+        $this->Calendars->EditItemIndex = -1;
+        $this->_rebindData();
+    }
+
+    public function uploadRowFile($sender, $param) {
+        $fileType = $sender->getFileType();
+        if (preg_match('/^image\//', $fileType)) {
+            $calendar = $this->getFacade()->get($sender->CustomData);
+            if ($calendar) {
+                $targetFile = $calendar[0]->getCustomImagePath(
+                    $sender->getLocalName(),
+                    $fileType
+                );
+                if ($sender->saveAs($targetFile)) {
+                    $sender->NamingContainer->CustomImage->Value->Text = basename(
+                        $targetFile
+                    );
+                }
+            } else {
+                throw new TInvalidDataValueException('Calendar not found');
+            }
+        } else {
+            throw new TInvalidDataTypeException('Invalid file type');
+        }
+    }
+
+}
+
+?>
diff --git a/app/php/controls/CalendarScaffold.tpl b/app/php/controls/CalendarScaffold.tpl
new file mode 100644
index 0000000..6688869
--- /dev/null
+++ b/app/php/controls/CalendarScaffold.tpl
@@ -0,0 +1,57 @@
+<com:TActiveDataGrid ID="Calendars"
+                     DataKeyField="UID"
+                     AutoGenerateColumns="false"
+                     OnEditCommand="editRow"
+                     OnCancelCommand="cancelRowEdit"
+                     OnUpdateCommand="saveRow">
+  <com:TActiveBoundColumn ID="Name"
+                          ReadOnly="true"
+                          HeaderText="Calendar"
+                          DataField="Name" />
+  <com:TActiveHyperLinkColumn ID="Website"
+                              HeaderText="WWW"
+                              Text="[www]"
+                              Target="_blank"
+                              DataNavigateUrlField="Website" />
+  <com:TActiveHyperLinkColumn ID="Url"
+                              HeaderText="ICS"
+                              Text="[ics]"
+                              Target="_blank"
+                              DataNavigateUrlField="Url" />
+  <com:TActiveDropDownListColumn ID="Category"
+                                 HeaderText="Category"
+                                 DataTextField="Category.Name"
+                                 DataValueField="CategoryID"
+                                 ListValueField="ID"
+                                 ListTextField="Name" />
+  <com:TActiveCheckBoxColumn ID="Visible"
+                             HeaderText="Default"
+                             DataField="Visible" />
+  <com:TActiveBoundColumn ID="CustomName"
+                          HeaderText="Name"
+                          DataField="CustomName" />
+  <com:TActiveBoundColumn ID="CustomUrl"
+                          HeaderText="URL"
+                          DataField="CustomUrl" />
+  <com:TActiveTemplateColumn ID="CustomImage"
+                             HeaderText="Image">
+    <prop:ItemTemplate>
+      <com:TImage>
+        <prop:ImageUrl><%# $this->Parent->Data->CustomImageUrl %></prop:ImageUrl>
+      </com:TImage>
+    </prop:ItemTemplate>
+    <prop:EditItemTemplate>
+      <com:TActiveTextBox ID="Value">
+        <prop:Text><%# $this->Parent->Data->CustomImage %></prop:Text>
+      </com:TActiveTextBox><br />
+      <com:SafeActiveFileUpload
+          OnFileUpload="SourceTemplateControl.uploadRowFile">
+        <prop:CustomData><%# $this->Parent->Data->UID %></prop:CustomData>
+      </com:SafeActiveFileUpload>
+    </prop:EditItemTemplate>
+  </com:TActiveTemplateColumn>
+  <com:TActiveEditCommandColumn
+      HeaderText="Edit"
+      UpdateText="Save"
+      CancelText="Cancel" />
+</com:TActiveDataGrid>
diff --git a/app/php/controls/HeaderMenu.php b/app/php/controls/HeaderMenu.php
new file mode 100644
index 0000000..bffe4d2
--- /dev/null
+++ b/app/php/controls/HeaderMenu.php
@@ -0,0 +1,16 @@
+<?php
+
+Prado::using('System.Web.UI.ActiveControls.TActiveLinkButton');
+
+class HeaderMenu extends TTemplateControl {
+
+    public function logoutUser($sender, $param) {
+        $this->Application->getModule('auth')->logout();
+        $this->Response->redirect(
+            $this->Service->ConstructUrl(NULL)
+        );
+    }
+
+}
+
+?>
diff --git a/app/php/controls/HeaderMenu.tpl b/app/php/controls/HeaderMenu.tpl
new file mode 100644
index 0000000..603a231
--- /dev/null
+++ b/app/php/controls/HeaderMenu.tpl
@@ -0,0 +1,22 @@
+<nav role="navigation">
+  <com:THyperLink Text="Login">
+    <prop:NavigateUrl><%= $this->Service->constructUrl('Login') %></prop:NavigateUrl>
+    <prop:Visible><%= $this->User->IsGuest %></prop:Visible>
+  </com:THyperLink>
+  <com:THyperLink Text="Profile">
+    <prop:NavigateUrl><%= $this->Service->constructUrl('Profile') %></prop:NavigateUrl>
+    <prop:Visible><%= !$this->User->IsGuest %></prop:Visible>
+  </com:THyperLink>
+  <com:TActiveLinkButton OnCommand="logoutUser">
+    <prop:Text>Logout (<%= $this->User->Name %>)</prop:Text>
+    <prop:Visible><%= !$this->User->IsGuest %></prop:Visible>
+  </com:TActiveLinkButton>
+  <com:THyperLink Text="New user">
+    <prop:NavigateUrl><%= $this->Service->constructUrl('Signup') %></prop:NavigateUrl>
+    <prop:Visible><%= $this->User->getIsAdmin() %></prop:Visible>
+  </com:THyperLink>
+  <com:THyperLink Text="Admin calendars">
+    <prop:NavigateUrl><%= $this->Service->constructUrl('Admin') %></prop:NavigateUrl>
+    <prop:Visible><%= $this->User->getIsAdmin() %></prop:Visible>
+  </com:THyperLink>
+</nav>
diff --git a/app/php/controls/LoginBox.php b/app/php/controls/LoginBox.php
new file mode 100644
index 0000000..33bbcc1
--- /dev/null
+++ b/app/php/controls/LoginBox.php
@@ -0,0 +1,23 @@
+<?php
+
+class LoginBox extends TTemplateControl {
+
+    public function loginUser($sender, $param) {
+        if ($this->Page->IsValid) {
+            $this->Response->redirect(
+                $this->Application->getModule('auth')->ReturnUrl
+                ?: $this->Service->constructUrl(NULL)
+            );
+        }
+    }
+
+    public function validatePassword($sender, $param) {
+        $param->IsValid = $this->Application->getModule('auth')->login(
+            $this->Login->Text,
+            $this->Password->Text
+        );
+    }
+
+}
+
+?>
diff --git a/app/php/controls/LoginBox.tpl b/app/php/controls/LoginBox.tpl
new file mode 100644
index 0000000..d3e1a1e
--- /dev/null
+++ b/app/php/controls/LoginBox.tpl
@@ -0,0 +1,29 @@
+Username:
+<com:TTextBox ID="Login"
+              ValidationGroup="LoginGroup" />
+<com:TRequiredFieldValidator
+    ControlToValidate="Login"
+    Display="Dynamic"
+    ErrorMessage="Username cannot be empty"
+    ValidationGroup="LoginGroup" />
+<br />
+Password:
+<com:TTextBox ID="Password"
+              TextMode="Password"
+              ValidationGroup="LoginGroup" />
+<com:TRequiredFieldValidator
+    ControlToValidate="Password"
+    Display="Dynamic"
+    ErrorMessage="Password cannot be empty"
+    ValidationGroup="LoginGroup" />
+<com:TCustomValidator
+    ControlToValidate="Password"
+    OnServerValidate="validatePassword"
+    Display="Dynamic"
+    ErrorMessage="Username and password don't match"
+    ValidationGroup="LoginGroup" />
+<br />
+<com:TButton
+    Text="Login"
+    OnCommand="loginUser"
+    ValidationGroup="LoginGroup" />
diff --git a/app/php/controls/PasswordChange.php b/app/php/controls/PasswordChange.php
new file mode 100644
index 0000000..9f2ac7f
--- /dev/null
+++ b/app/php/controls/PasswordChange.php
@@ -0,0 +1,38 @@
+<?php
+
+Prado::using('Application.user.DbUser');
+
+class PasswordChange extends TTemplateControl {
+
+    public function getUserToChange() {
+        return $this->getControlState('user');
+    }
+
+    public function setUserToChange(DbUser $user) {
+        if ($user->IsGuest) {
+            throw new TInvalidDataValueException(
+                'Password change impossible for guest user'
+            );
+        }
+        $this->setControlState('user', $user);
+    }
+
+    public function checkPassword($sender, $param) {
+        $param->IsValid = DbUser::verifyPassword(
+            $this->Password->Text, $this->UserToChange->getPassword()
+        );
+    }
+
+    public function changePassword($sender, $param) {
+        $this->SuccessMessage->Visible = FALSE;
+        if ($this->Page->IsValid) {
+            $this->UserToChange->changePassword(
+                $this->NewPassword->Text
+            );
+            $this->SuccessMessage->Visible = TRUE;
+        }
+    }
+
+}
+
+?>
diff --git a/app/php/controls/PasswordChange.tpl b/app/php/controls/PasswordChange.tpl
new file mode 100644
index 0000000..915e8b3
--- /dev/null
+++ b/app/php/controls/PasswordChange.tpl
@@ -0,0 +1,52 @@
+Change password<br />
+Current password:
+<com:TTextBox ID="Password"
+              TextMode="Password"
+              ValidationGroup="ChangePasswordGroup" />
+<com:TRequiredFieldValidator
+    ControlToValidate="Password"
+    Display="Dynamic"
+    ErrorMessage="Current password cannot be empty"
+    ValidationGroup="ChangePasswordGroup" />
+<com:TCustomValidator
+    ControlToValidate="Password"
+    OnServerValidate="checkPassword"
+    Display="Dynamic"
+    ErrorMessage="Password is incorrect"
+    ValidationGroup="ChangePasswordGroup" />
+<br />
+New password:
+<com:TTextBox ID="NewPassword"
+              TextMode="Password"
+              ValidationGroup="ChangePasswordGroup" />
+<com:TRequiredFieldValidator
+    ControlToValidate="NewPassword"
+    Display="Dynamic"
+    ErrorMessage="New password cannot be empty"
+    ValidationGroup="ChangePasswordGroup" />
+<br />
+Repeat password:
+<com:TTextBox ID="ReNewPassword"
+              TextMode="Password"
+              ValidationGroup="ChangePasswordGroup" />
+<com:TRequiredFieldValidator
+    ControlToValidate="ReNewPassword"
+    Display="Dynamic"
+    ErrorMessage="New password cannot be empty"
+    ValidationGroup="ChangePasswordGroup" />
+<com:TCompareValidator
+    ControlToValidate="ReNewPassword"
+    ControlToCompare="NewPassword"
+    DataType="String"
+    Operator="Equal"
+    Display="Dynamic"
+    ErrorMessage="Passwords don't match"
+    ValidationGroup="ChangePasswordGroup" />
+<br />
+<com:TButton
+    Text="Change password"
+    OnCommand="changePassword"
+    ValidationGroup="ChangePasswordGroup" />
+<com:TLabel ID="SuccessMessage"
+            Text="Your password has been changed"
+            Visible="false" />
diff --git a/app/php/controls/RegistrationForm.php b/app/php/controls/RegistrationForm.php
new file mode 100644
index 0000000..71d4df1
--- /dev/null
+++ b/app/php/controls/RegistrationForm.php
@@ -0,0 +1,26 @@
+<?php
+
+Prado::using('Application.model.User');
+
+class RegistrationForm extends TTemplateControl {
+
+    public function checkUsername($sender, $param) {
+        $param->IsValid = !User::finder()->countByLogin($this->Login->SafeText);
+    }
+
+    public function registerUser($sender, $param) {
+        if ($this->Page->IsValid) {
+            $newUser = new User();
+            $newUser->Login = $this->Login->SafeText;
+            $newUser->Password = DbUser::generatePassword($this->Password->Text);
+            $newUser->IsAdmin = $this->Admin->Checked;
+            $newUser->save();
+            $this->Response->redirect(
+                $this->Service->constructUrl(NULL)
+            );
+        }
+    }
+
+}
+
+?>
diff --git a/app/php/controls/RegistrationForm.tpl b/app/php/controls/RegistrationForm.tpl
new file mode 100644
index 0000000..ffa4778
--- /dev/null
+++ b/app/php/controls/RegistrationForm.tpl
@@ -0,0 +1,59 @@
+Username:
+<com:TTextBox ID="Login"
+              ValidationGroup="SignupGroup" />
+<com:TRequiredFieldValidator
+    ControlToValidate="Login"
+    Display="Dynamic"
+    ErrorMessage="Username cannot be empty"
+    ValidationGroup="SignupGroup" />
+<com:TRegularExpressionValidator
+    ControlToValidate="Login"
+    RegularExpression="[a-zA-Z0-9_]{6,255}"
+    Display="Dynamic"
+    ErrorMessage="Username must contain 6-255 characters, all Latin alphanumeric or underscore"
+    ValidationGroup="SignupGroup" />
+<com:TCustomValidator
+    ControlToValidate="Login"
+    OnServerValidate="checkUsername"
+    Display="Dynamic"
+    ErrorMessage="Username already exists"
+    ValidationGroup="SignupGroup" />
+<br />
+Password:
+<com:TTextBox ID="Password"
+              TextMode="Password"
+              ValidationGroup="SignupGroup" />
+<com:TRequiredFieldValidator
+    ControlToValidate="Password"
+    Display="Dynamic"
+    ErrorMessage="Password cannot be empty"
+    ValidationGroup="SignupGroup" />
+<br />
+Repeat password:
+<com:TTextBox ID="RePassword"
+              TextMode="Password"
+              ValidationGroup="SignupGroup" />
+<com:TRequiredFieldValidator
+    ControlToValidate="RePassword"
+    Display="Dynamic"
+    ErrorMessage="Password cannot be empty"
+    ValidationGroup="SignupGroup" />
+<com:TCompareValidator
+    ControlToValidate="RePassword"
+    ControlToCompare="Password"
+    DataType="String"
+    Operator="Equal"
+    Display="Dynamic"
+    ErrorMessage="Passwords don't match"
+    ValidationGroup="SignupGroup" />
+<br />
+Admin:
+<com:TCheckBox ID="Admin"
+               ValidationGroup="SignupGroup" />
+<br />
+<com:TButton
+    Text="Create"
+    OnCommand="registerUser"
+    ValidationGroup="SignupGroup" />
+<com:TValidationSummary
+    ValidationGroup="SignupGroup" />
diff --git a/app/php/controls/SafeActiveFileUpload.php b/app/php/controls/SafeActiveFileUpload.php
new file mode 100644
index 0000000..ada1e34
--- /dev/null
+++ b/app/php/controls/SafeActiveFileUpload.php
@@ -0,0 +1,12 @@
+<?php
+
+Prado::using('System.Web.UI.ActiveControls.TActiveFileUpload');
+Prado::using('Application.controls.SafeFileUpload');
+
+class SafeActiveFileUpload extends TActiveFileUpload {
+
+    use MimeTypeCheckForFileUpload;
+
+}
+
+?>
diff --git a/app/php/controls/SafeFileUpload.php b/app/php/controls/SafeFileUpload.php
new file mode 100644
index 0000000..98e120a
--- /dev/null
+++ b/app/php/controls/SafeFileUpload.php
@@ -0,0 +1,34 @@
+<?php
+
+class SafeFileUpload extends TFileUpload {
+
+    use MimeTypeCheckForFileUpload;
+
+}
+
+trait MimeTypeCheckForFileUpload {
+
+    protected $_isSecure = TRUE;
+
+    public function getIsSecure() {
+        return $this->_isSecure;
+    }
+
+    public function setIsSecure($bool) {
+        $this->_isSecure = $bool;
+    }
+
+    public function getFileType() {
+        $type = parent::getFileType();
+        if ($this->getIsSecure()) {
+            $fileInfo = new finfo(FILEINFO_MIME_TYPE);
+            return $fileInfo->file($this->getLocalName());
+        }
+        else {
+            return $type;
+        }
+    }
+
+}
+
+?>
diff --git a/app/php/controls/TimezoneSelect.php b/app/php/controls/TimezoneSelect.php
new file mode 100644
index 0000000..3302e2a
--- /dev/null
+++ b/app/php/controls/TimezoneSelect.php
@@ -0,0 +1,49 @@
+<?php
+
+Prado::using('Application.user.DbUser');
+Prado::using('Application.dto.TimezoneDTO');
+
+class TimezoneSelect extends TTemplateControl {
+
+    public function getUserToChange() {
+        return $this->getControlState('user');
+    }
+
+    public function setUserToChange(DbUser $user) {
+        if ($user->IsGuest) {
+            throw new TInvalidDataValueException(
+                'Timezone preference change impossible for guest user'
+            );
+        }
+        $this->setControlState('user', $user);
+    }
+
+    public function onPreRender($param) {
+        parent::onPreRender($param);
+        $this->Timezones->DataSource = $this->_getTimezones();
+        $this->Timezones->DataValueField = 'Name';
+        $this->Timezones->DataTextField = 'Label';
+        $this->Timezones->dataBind();
+        $this->Timezones->setSelectedValue(
+            $this->UserToChange->getTimezonePreference()->Name
+        );
+    }
+
+    public function saveTimezone($sender, $param) {
+        $this->UserToChange->setTimezonePreference($this->Timezones->SelectedValue);
+    }
+
+    private function _getTimezones() {
+        $timezones = array_map(
+            function($tz) {
+                return new TimezoneDTO($tz);
+            },
+            DateTimeZone::listIdentifiers()
+        );
+        usort($timezones, ['TimezoneDTO', '__compare']);
+        return $timezones;
+    }
+
+}
+
+?>
diff --git a/app/php/controls/TimezoneSelect.tpl b/app/php/controls/TimezoneSelect.tpl
new file mode 100644
index 0000000..2d40014
--- /dev/null
+++ b/app/php/controls/TimezoneSelect.tpl
@@ -0,0 +1,4 @@
+<com:TDropDownList ID="Timezones" />
+<com:TButton
+    Text="Save timezone"
+    OnCommand="saveTimezone" />
diff --git a/app/php/controls/UpcomingEvents.php b/app/php/controls/UpcomingEvents.php
new file mode 100644
index 0000000..27fa8c6
--- /dev/null
+++ b/app/php/controls/UpcomingEvents.php
@@ -0,0 +1,35 @@
+<?php
+
+Prado::using('Application.facades.EventFacade');
+
+class UpcomingEvents extends TTemplateControl {
+
+    public function getUserToDisplay() {
+        return $this->getControlState('user');
+    }
+
+    public function setUserToDisplay($user) {
+        $this->setControlState('user', $user);
+    }
+
+    public function onPreRender($param) {
+        parent::onPreRender($param);
+        $this->Events->setDataSource(
+            $this->_getEventsForUser($this->UserToDisplay)
+        );
+        $this->Events->dataBind();
+    }
+
+    private function _getEventsForUser(DbUser $user) {
+        $utc = new DateTimeZone('UTC');
+        $dateFrom = new DateTime('now', $utc);
+        $dateTo = new DateTime('+7 days', $utc);
+        return EventFacade::getInstance()->getTimeframeListForUser(
+            $user,
+            $dateFrom, $dateTo
+        );
+    }
+
+}
+
+?>
diff --git a/app/php/controls/UpcomingEvents.tpl b/app/php/controls/UpcomingEvents.tpl
new file mode 100644
index 0000000..d660f54
--- /dev/null
+++ b/app/php/controls/UpcomingEvents.tpl
@@ -0,0 +1,10 @@
+Upcoming events:
+<br />
+<com:TRepeater ID="Events">
+  <prop:ItemTemplate>
+    <%# $this->Data->DateString %>
+    <%# $this->Data->Name %>
+    (<%# $this->Data->Calendar->Name %>)
+    <br />
+  </prop:ItemTemplate>
+</com:TRepeater>
diff --git a/app/php/controls/UserSelection.php b/app/php/controls/UserSelection.php
new file mode 100644
index 0000000..6ae68e4
--- /dev/null
+++ b/app/php/controls/UserSelection.php
@@ -0,0 +1,39 @@
+<?php
+
+Prado::using('Application.facades.CalendarFacade');
+
+class UserSelection extends TTemplateControl {
+
+    public function getUserToDisplay() {
+        return $this->getControlState('user');
+    }
+
+    public function setUserToDisplay($user) {
+        $this->setControlState('user', $user);
+    }
+
+    public function onPreRender($param) {
+        parent::onPreRender($param);
+        $this->Categories->setDataSource(
+            $this->_getUserSelection($this->UserToDisplay)
+        );
+        $this->Categories->dataBind();
+    }
+
+    public function categoryDataBind($sender, $param) {
+        $param->Item->Calendars->setDataSource($param->Item->Data->Calendars);
+        $param->Item->Calendars->dataBind();
+    }
+
+    public function removeFromSelection($sender, $param) {
+        if (!$this->UserToDisplay->IsGuest) {
+        }
+    }
+
+    private function _getUserSelection(DbUser $user) {
+        return CalendarFacade::getInstance()->getPreferenceList($user);
+    }
+
+}
+
+?>
diff --git a/app/php/controls/UserSelection.tpl b/app/php/controls/UserSelection.tpl
new file mode 100644
index 0000000..14035ca
--- /dev/null
+++ b/app/php/controls/UserSelection.tpl
@@ -0,0 +1,25 @@
+Selected calendars:
+<br />
+<com:TRepeater ID="Categories" OnItemDataBound="categoryDataBind">
+  <prop:ItemTemplate>
+    <%# $this->Data->Name %><br />
+    <com:TRepeater ID="Calendars">
+      <prop:ItemTemplate>
+        <com:TLinkButton
+            Text="[X]"
+            OnCommand="SourceTemplateControl.removeFromSelection">
+          <prop:CommandParameter><%# $this->Data->ID %></prop:CommandParameter>
+          <prop:Visible><%# !$this->SourceTemplateControl->UserToDisplay->IsGuest %></prop:Visible>
+        </com:TLinkButton>
+        <%# $this->Data->Name %>
+        <com:THyperLink
+            Text="(www)"
+            Target="_blank">
+          <prop:NavigateUrl><%# $this->Data->Website %></prop:NavigateUrl>
+        </com:THyperLink>
+        <br />
+      </prop:ItemTemplate>
+    </com:TRepeater>
+    <br />
+  </prop:ItemTemplate>
+</com:TRepeater>
diff --git a/app/php/controls/config.xml b/app/php/controls/config.xml
new file mode 100644
index 0000000..61d7e5b
--- /dev/null
+++ b/app/php/controls/config.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+  <paths>
+    <using namespace="Application.controls.*" />
+  </paths>
+</configuration>
-- 
cgit v1.2.3