summaryrefslogtreecommitdiff
path: root/app/frontend/controls
diff options
context:
space:
mode:
Diffstat (limited to 'app/frontend/controls')
-rw-r--r--app/frontend/controls/AddToFilter.php41
-rw-r--r--app/frontend/controls/AddToFilter.tpl8
-rw-r--r--app/frontend/controls/CalendarDetails.php7
-rw-r--r--app/frontend/controls/CalendarDetails.tpl13
-rw-r--r--app/frontend/controls/CalendarGrid.php59
-rw-r--r--app/frontend/controls/CalendarGrid.tpl28
-rw-r--r--app/frontend/controls/CalendarGroupFilter.php21
-rw-r--r--app/frontend/controls/CalendarGroupFilter.tpl16
-rw-r--r--app/frontend/controls/CalendarLabel.php13
-rw-r--r--app/frontend/controls/CalendarLabel.tpl12
-rw-r--r--app/frontend/controls/CalendarScaffold.php142
-rw-r--r--app/frontend/controls/CalendarScaffold.tpl81
-rw-r--r--app/frontend/controls/CalendarSelection.php17
-rw-r--r--app/frontend/controls/CalendarSelection.tpl8
-rw-r--r--app/frontend/controls/EventList.php59
-rw-r--r--app/frontend/controls/EventList.tpl6
-rw-r--r--app/frontend/controls/EventRepeater.php25
-rw-r--r--app/frontend/controls/EventRepeater.tpl12
-rw-r--r--app/frontend/controls/HeaderMenu.php26
-rw-r--r--app/frontend/controls/HeaderMenu.tpl36
-rw-r--r--app/frontend/controls/LoginBox.php39
-rw-r--r--app/frontend/controls/LoginBox.tpl33
-rw-r--r--app/frontend/controls/PasswordChange.php44
-rw-r--r--app/frontend/controls/PasswordChange.tpl59
-rw-r--r--app/frontend/controls/RegistrationForm.php28
-rw-r--r--app/frontend/controls/RegistrationForm.tpl66
-rw-r--r--app/frontend/controls/TimezoneSelect.php58
-rw-r--r--app/frontend/controls/TimezoneSelect.tpl5
-rw-r--r--app/frontend/controls/UpcomingEvents.php33
-rw-r--r--app/frontend/controls/UpcomingEvents.tpl5
-rw-r--r--app/frontend/controls/UrlBasedCalendarControl.php40
-rw-r--r--app/frontend/controls/UserSelection.php45
-rw-r--r--app/frontend/controls/UserSelection.tpl29
-rw-r--r--app/frontend/controls/config.xml6
-rw-r--r--app/frontend/controls/scripts/AddToFilter.js5
-rw-r--r--app/frontend/controls/scripts/CalendarGroupFilter.js29
-rw-r--r--app/frontend/controls/scripts/CalendarLabel.js11
-rw-r--r--app/frontend/controls/scripts/CalendarScaffold.js8
-rw-r--r--app/frontend/controls/styles/CalendarGrid.css16
-rw-r--r--app/frontend/controls/styles/CalendarScaffold.css11
40 files changed, 1200 insertions, 0 deletions
diff --git a/app/frontend/controls/AddToFilter.php b/app/frontend/controls/AddToFilter.php
new file mode 100644
index 0000000..9146f3b
--- /dev/null
+++ b/app/frontend/controls/AddToFilter.php
@@ -0,0 +1,41 @@
+<?php
+
+Prado::using('System.Web.UI.ActiveControls.TActiveCheckBox');
+
+class AddToFilter extends UrlBasedCalendarControl {
+
+ public function setDescription($val) {
+ $this->setViewState('Description', TPropertyValue::ensureString($val));
+ }
+
+ public function getDescription() {
+ return $this->getViewState('Description');
+ }
+
+ public function setUserPreference($sender, $param) {
+ $user = $this->getUserToManage();
+ if ($user && !$user->IsGuest) {
+ if ($sender->Checked) {
+ $this->getFacade()->addToPreference($user, $this->getCalendar()->ID);
+ } else {
+ $this->getFacade()->removeFromPreference($user, $this->getCalendar()->ID);
+ }
+ $this->Page->CallbackClient->jQuery($this->Box, 'removeAttr', 'disabled');
+ }
+ }
+
+ public function getUserToManage() {
+ return $this->getControlState('user');
+ }
+
+ public function setUserToManage($user) {
+ $this->setControlState('user', $user);
+ }
+
+ public function getPradoScriptDependencies() {
+ return ['jquery'];
+ }
+
+}
+
+?>
diff --git a/app/frontend/controls/AddToFilter.tpl b/app/frontend/controls/AddToFilter.tpl
new file mode 100644
index 0000000..a202aa7
--- /dev/null
+++ b/app/frontend/controls/AddToFilter.tpl
@@ -0,0 +1,8 @@
+<com:TActiveCheckBox ID="Box" OnCheckedChanged="setUserPreference" CssClass="addToFilterBox">
+ <prop:Enabled><%= !$this->UserToManage->IsGuest %></prop:Enabled>
+ <prop:Checked><%= $this->Facade->isCalendarPreferred($this->UserToManage, $this->getCalendar()->ID) %></prop:Checked>
+ <prop:ToolTip><%= $this->UserToManage->IsGuest ? Prado::localize('log in to manage your selections') : '' %></prop:ToolTip>
+</com:TActiveCheckBox>
+<com:TLabel ForControl="Box">
+ <prop:Text><%= $this->getDescription() %></prop:Text>
+</com:TLabel>
diff --git a/app/frontend/controls/CalendarDetails.php b/app/frontend/controls/CalendarDetails.php
new file mode 100644
index 0000000..95ee563
--- /dev/null
+++ b/app/frontend/controls/CalendarDetails.php
@@ -0,0 +1,7 @@
+<?php
+
+class CalendarDetails extends UrlBasedCalendarControl {
+
+}
+
+?>
diff --git a/app/frontend/controls/CalendarDetails.tpl b/app/frontend/controls/CalendarDetails.tpl
new file mode 100644
index 0000000..2fd755c
--- /dev/null
+++ b/app/frontend/controls/CalendarDetails.tpl
@@ -0,0 +1,13 @@
+<com:THeader2>
+ <%= $this->getCalendar()->Name %>
+</com:THeader2>
+<com:TImage>
+ <prop:ImageUrl><%= $this->getCalendar()->Image %></prop:ImageUrl>
+</com:TImage>
+<com:THyperLink Target="_blank">
+ <prop:Text><%[ Source website ]%></prop:Text>
+ <prop:NavigateUrl><%= $this->getCalendar()->Website %></prop:NavigateUrl>
+</com:THyperLink>
+<p>
+ <%[ Last updated: ]%> <%= $this->getCalendar()->LastUpdated %>
+</p>
diff --git a/app/frontend/controls/CalendarGrid.php b/app/frontend/controls/CalendarGrid.php
new file mode 100644
index 0000000..4ebfacd
--- /dev/null
+++ b/app/frontend/controls/CalendarGrid.php
@@ -0,0 +1,59 @@
+<?php
+
+Prado::using('Application.web.FacadeTemplateControl');
+Prado::using('Application.facades.EventFacade');
+Prado::using('Application.user.DbUser');
+
+class CalendarGrid extends FacadeTemplateControl {
+
+ public function setMonth($month) {
+ $this->setControlState('Month', $month);
+ }
+
+ public function getMonth() {
+ return $this->getControlState('Month');
+ }
+
+ public function setYear($year) {
+ $this->setControlState('Year', $year);
+ }
+
+ public function getYear() {
+ return $this->getControlState('Year');
+ }
+
+ public function setUserToDisplay(DbUser $user) {
+ $this->setControlState('User', $user);
+ }
+
+ public function getUserToDisplay() {
+ return $this->getControlState('User');
+ }
+
+ private function _getGrid() {
+ return $this->getFacade()->getCalendarListForUser(
+ $this->UserToDisplay,
+ $this->Month,
+ $this->Year
+ );
+ }
+
+ public function onPreRender($param) {
+ parent::onPreRender($param);
+ $this->Weeks->DataSource = $this->_getGrid()->Weeks;
+ $this->Weeks->dataBind();
+ }
+
+ public function weekDataBind($sender, $param) {
+ $param->Item->Days->DataSource = $param->Item->Data;
+ $param->Item->Days->dataBind();
+ }
+
+ public function dayDataBind($sender, $param) {
+ $param->Item->Events->DataSource = $param->Item->Data->Events;
+ $param->Item->Events->dataBind();
+ }
+
+}
+
+?>
diff --git a/app/frontend/controls/CalendarGrid.tpl b/app/frontend/controls/CalendarGrid.tpl
new file mode 100644
index 0000000..2b3ada8
--- /dev/null
+++ b/app/frontend/controls/CalendarGrid.tpl
@@ -0,0 +1,28 @@
+<com:TRepeater ID="Weeks" OnItemDataBound="weekDataBind">
+ <prop:ItemTemplate>
+ <div class="gridWeek">
+ <com:TRepeater ID="Days" OnItemDataBound="SourceTemplateControl.dayDataBind">
+ <prop:ItemTemplate>
+ <div class="gridDay">
+ <%# $this->Data->Date %>
+ <com:TRepeater ID="Events">
+ <prop:ItemTemplate>
+ <com:TConditional Condition="$this->Data">
+ <prop:TrueTemplate>
+ <com:THtmlElement TagName="div">
+ <prop:CssClass>gridEvent <%# $this->Parent->Parent->Data->Date == $this->Data->DateFrom ? 'beginDate' : '' %> <%# $this->Parent->Parent->Data->Date == $this->Data->DateTo ? 'endDate' : '' %></prop:CssClass>
+ <%# $this->Data->Name %>
+ </com:THtmlElement>
+ </prop:TrueTemplate>
+ <prop:FalseTemplate>
+ <div class="gridItem">&nbsp;</div>
+ </prop:FalseTemplate>
+ </com:TConditional>
+ </prop:ItemTemplate>
+ </com:TRepeater>
+ </div>
+ </prop:ItemTemplate>
+ </com:TRepeater>
+ </div>
+ </prop:ItemTemplate>
+</com:TRepeater>
diff --git a/app/frontend/controls/CalendarGroupFilter.php b/app/frontend/controls/CalendarGroupFilter.php
new file mode 100644
index 0000000..6f19bcd
--- /dev/null
+++ b/app/frontend/controls/CalendarGroupFilter.php
@@ -0,0 +1,21 @@
+<?php
+
+Prado::using('Application.web.FacadeTemplateControl');
+
+Prado::using('Application.facades.CalendarFacade');
+
+class CalendarGroupFilter extends FacadeTemplateControl {
+
+ public function onPreRender($param) {
+ parent::onPreRender($param);
+ $this->Categories->DataSource = $this->Facade->getCategories();
+ $this->Categories->dataBind();
+ }
+
+ public function getPradoScriptDependencies() {
+ return ['jquery'];
+ }
+
+}
+
+?>
diff --git a/app/frontend/controls/CalendarGroupFilter.tpl b/app/frontend/controls/CalendarGroupFilter.tpl
new file mode 100644
index 0000000..ac82465
--- /dev/null
+++ b/app/frontend/controls/CalendarGroupFilter.tpl
@@ -0,0 +1,16 @@
+<com:TRepeater ID="Categories">
+ <prop:HeaderTemplate>
+ <div class="selectAllGroups">
+ <%[ All ]%>
+ <com:TCheckBox CssClass="box" Checked="True" />
+ </div>
+ </prop:HeaderTemplate>
+ <prop:ItemTemplate>
+ <div class="selectGroup">
+ <%# $this->Data->Name %>
+ <com:TCheckBox CssClass="box" Checked="True">
+ <prop:Value><%# $this->Data->ID %></prop:Value>
+ </com:TCheckBox>
+ </div>
+ </prop:ItemTemplate>
+</com:TRepeater>
diff --git a/app/frontend/controls/CalendarLabel.php b/app/frontend/controls/CalendarLabel.php
new file mode 100644
index 0000000..667e847
--- /dev/null
+++ b/app/frontend/controls/CalendarLabel.php
@@ -0,0 +1,13 @@
+<?php
+
+Prado::using('Application.controls.UrlBasedCalendarControl');
+
+class CalendarLabel extends UrlBasedCalendarControl {
+
+ public function getPradoScriptDependencies() {
+ return ['jquery'];
+ }
+
+}
+
+?>
diff --git a/app/frontend/controls/CalendarLabel.tpl b/app/frontend/controls/CalendarLabel.tpl
new file mode 100644
index 0000000..69e0147
--- /dev/null
+++ b/app/frontend/controls/CalendarLabel.tpl
@@ -0,0 +1,12 @@
+<com:TPanel CssClass="calendar">
+ <prop:Attributes.data-group><%= $this->Calendar->GroupID %></prop:Attributes.data-group>
+ <com:THyperLink>
+ <prop:NavigateUrl><%= $this->Service->constructUrl('Calendar', ['calendar' => $this->Calendar->Url]) %></prop:NavigateUrl>
+ <prop:Text><%= $this->Calendar->Name %></prop:Text>
+ </com:THyperLink>
+ <com:AddToFilter>
+ <prop:Facade><%= $this->Facade %></prop:Facade>
+ <prop:CalendarUrl><%= $this->Calendar->Url %></prop:CalendarUrl>
+ <prop:UserToManage><%= $this->User %></prop:UserToManage>
+ </com:AddToFilter>
+</com:TPanel>
diff --git a/app/frontend/controls/CalendarScaffold.php b/app/frontend/controls/CalendarScaffold.php
new file mode 100644
index 0000000..7f15a32
--- /dev/null
+++ b/app/frontend/controls/CalendarScaffold.php
@@ -0,0 +1,142 @@
+<?php
+
+Prado::using('Application.web.FacadeTemplateControl');
+
+Prado::using('System.Web.UI.ActiveControls.TActiveDataGrid');
+Prado::using('System.Web.UI.ActiveControls.TActiveTextBox');
+Prado::using('Application.components.SafeActiveFileUpload');
+
+Prado::using('Application.facades.CalendarFacade');
+
+class CalendarScaffold extends FacadeTemplateControl {
+
+ 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(bool $refresh = FALSE) {
+ $this->_rebindCategoryList(
+ $this->_getCategories()
+ );
+ $this->_rebindCalendars(
+ $this->_getCalendars($refresh)
+ );
+ }
+
+ private function _getCalendars(bool $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(
+ Prado::localize('Calendar not found')
+ );
+ }
+ $this->Calendars->EditItemIndex = -1;
+ $this->_rebindData(TRUE);
+ }
+
+ public function cancelRowEdit($sender, $param) {
+ $this->Calendars->EditItemIndex = -1;
+ $this->_rebindData();
+ }
+
+ public function toggleDefaultState($sender, $param) {
+ $calendar = $this->getFacade()->get($sender->CustomData);
+ if ($calendar) {
+ $calendar[0]->Visible = $sender->Checked;
+ $calendar[0]->save();
+ $this->_rebindData(TRUE);
+ }
+ }
+
+ 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(
+ Prado::localize('Calendar not found')
+ );
+ }
+ } else {
+ throw new TInvalidDataTypeException(
+ Prado::localize('Invalid file type')
+ );
+ }
+ }
+
+ protected function getPradoScriptDependencies() {
+ return ['jquery'];
+ }
+
+}
+
+?>
diff --git a/app/frontend/controls/CalendarScaffold.tpl b/app/frontend/controls/CalendarScaffold.tpl
new file mode 100644
index 0000000..6a22bc2
--- /dev/null
+++ b/app/frontend/controls/CalendarScaffold.tpl
@@ -0,0 +1,81 @@
+<com:TPanel
+ CssClass="calendarScaffold">
+ <com:TActiveDataGrid ID="Calendars"
+ DataKeyField="UID"
+ AutoGenerateColumns="false"
+ OnEditCommand="editRow"
+ OnCancelCommand="cancelRowEdit"
+ OnUpdateCommand="saveRow">
+ <com:TActiveBoundColumn ID="Name"
+ ReadOnly="true"
+ DataField="Name">
+ <prop:HeaderText><%[ Calendar ]%></prop:HeaderText>
+ </com:TActiveBoundColumn>
+ <com:TActiveHyperLinkColumn ID="Website"
+ Target="_blank"
+ DataNavigateUrlField="Website">
+ <prop:HeaderText><%[ WWW ]%></prop:HeaderText>
+ <prop:Text><%[ [www] ]%></prop:Text>
+ </com:TActiveHyperLinkColumn>
+ <com:TActiveHyperLinkColumn ID="Url"
+ Target="_blank"
+ DataNavigateUrlField="Url">
+ <prop:HeaderText><%[ ICS ]%></prop:HeaderText>
+ <prop:Text><%[ [ics] ]%></prop:Text>
+ </com:TActiveHyperLinkColumn>
+ <com:TActiveDropDownListColumn ID="Category"
+ DataTextField="Category.Name"
+ DataValueField="CategoryID"
+ ListValueField="ID"
+ ListTextField="Name">
+ <prop:HeaderText><%[ Category ]%></prop:HeaderText>
+ </com:TActiveDropDownListColumn>
+ <com:TActiveTemplateColumn ID="Visible">
+ <prop:HeaderText><%[ Default ]%></prop:HeaderText>
+ <prop:ItemTemplate>
+ <com:TActiveCheckBox
+ OnCheckedChanged="SourceTemplateControl.toggleDefaultState"
+ CssClass="visibilityToggle">
+ <prop:Checked><%# $this->Parent->Data->Visible %></prop:Checked>
+ <prop:CustomData><%# $this->Parent->Data->UID %></prop:CustomData>
+ </com:TActiveCheckBox>
+ </prop:ItemTemplate>
+ <prop:EditItemTemplate>
+ <com:TCheckBox ID="CheckBox">
+ <prop:Checked><%# $this->Parent->Data->Visible %></prop:Checked>
+ </com:TCheckBox>
+ </prop:EditItemTemplate>
+ </com:TActiveTemplateColumn>
+ <com:TActiveBoundColumn ID="CustomName"
+ DataField="CustomName">
+ <prop:HeaderText><%[ Custom name ]%></prop:HeaderText>
+ </com:TActiveBoundColumn>
+ <com:TActiveBoundColumn ID="CustomUrl"
+ DataField="CustomUrl">
+ <prop:HeaderText><%[ URL ]%></prop:HeaderText>
+ </com:TActiveBoundColumn>
+ <com:TActiveTemplateColumn ID="CustomImage">
+ <prop:HeaderText><%[ Image ]%></prop:HeaderText>
+ <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="">
+ <prop:EditText><%[ Edit ]%></prop:EditText>
+ <prop:UpdateText><%[ Save ]%></prop:UpdateText>
+ <prop:CancelText><%[ Cancel ]%></prop:CancelText>
+ </com:TActiveEditCommandColumn>
+ </com:TActiveDataGrid>
+</com:TPanel>
diff --git a/app/frontend/controls/CalendarSelection.php b/app/frontend/controls/CalendarSelection.php
new file mode 100644
index 0000000..e53aa36
--- /dev/null
+++ b/app/frontend/controls/CalendarSelection.php
@@ -0,0 +1,17 @@
+<?php
+
+Prado::using('Application.controls.FacadeTemplateControl');
+
+class CalendarSelection extends FacadeTemplateControl {
+
+ public function onPreRender($param) {
+ parent::onPreRender($param);
+ if (!$this->Page->IsCallBack) {
+ $this->Calendars->DataSource = $this->Facade->getAll();
+ $this->Calendars->dataBind();
+ }
+ }
+
+}
+
+?>
diff --git a/app/frontend/controls/CalendarSelection.tpl b/app/frontend/controls/CalendarSelection.tpl
new file mode 100644
index 0000000..d6bdd83
--- /dev/null
+++ b/app/frontend/controls/CalendarSelection.tpl
@@ -0,0 +1,8 @@
+<com:TRepeater ID="Calendars">
+ <prop:ItemTemplate>
+ <com:CalendarLabel>
+ <prop:Facade><%# $this->SourceTemplateControl->Facade %></prop:Facade>
+ <prop:CalendarUrl><%# $this->Data->CustomUrl %></prop:CalendarUrl>
+ </com:CalendarLabel>
+ </prop:ItemTemplate>
+</com:TRepeater>
diff --git a/app/frontend/controls/EventList.php b/app/frontend/controls/EventList.php
new file mode 100644
index 0000000..d40e000
--- /dev/null
+++ b/app/frontend/controls/EventList.php
@@ -0,0 +1,59 @@
+<?php
+
+class EventList extends UrlBasedCalendarControl {
+
+ private function _setDate($key, $date) {
+ $datetime = new DateTime($date, new DateTimeZone('UTC'));
+ if (!$datetime) {
+ throw new TInvalidDataValueException(
+ Prado::localize('Invalid date string: {date}',
+ ['date' => $date])
+ );
+ }
+ $this->setViewState($key, $datetime);
+ }
+
+ public function setDateFrom($date) {
+ $this->_setDate('DateFrom', $date);
+ }
+
+ public function getDateFrom() {
+ return $this->getViewState('DateFrom');
+ }
+
+ public function setDateTo($date) {
+ $this->_setDate('DateTo', $date);
+ }
+
+ public function getDateTo() {
+ return $this->getViewState('DateTo');
+ }
+
+ public function setHeaderText($text) {
+ $this->setViewState('HeaderText', TPropertyValue::ensureString($text));
+ }
+
+ public function getHeaderText() {
+ return $this->getViewState('HeaderText');
+ }
+
+ public function setReverse($value) {
+ $this->setViewState('Reverse', TPropertyValue::ensureBoolean($value));
+ }
+
+ public function getReverse() {
+ return $this->getViewState('Reverse');
+ }
+
+ public function getEvents() {
+ return $this->getFacade()->getEventsForTimeframe(
+ $this->getCalendar(),
+ $this->getDateFrom() ?: new DateTime('0000-00-00'),
+ $this->getDateTo() ?: new DateTime('9999-99-99'),
+ $this->getReverse() ? 'DESC' : 'ASC'
+ );
+ }
+
+}
+
+?>
diff --git a/app/frontend/controls/EventList.tpl b/app/frontend/controls/EventList.tpl
new file mode 100644
index 0000000..5881ce7
--- /dev/null
+++ b/app/frontend/controls/EventList.tpl
@@ -0,0 +1,6 @@
+<com:THeader3>
+ <%= $this->getHeaderText() %>
+</com:THeader3>
+<com:EventRepeater CalendarLinkVisible="false">
+ <prop:Events><%= $this->getEvents() %></prop:Events>
+</com:EventRepeater>
diff --git a/app/frontend/controls/EventRepeater.php b/app/frontend/controls/EventRepeater.php
new file mode 100644
index 0000000..4fb2812
--- /dev/null
+++ b/app/frontend/controls/EventRepeater.php
@@ -0,0 +1,25 @@
+<?php
+
+Prado::using('Application.web.TemplateControl');
+
+class EventRepeater extends TemplateControl {
+
+ public function setEvents($events) {
+ $this->Events->DataSource = $events;
+ $this->Events->dataBind();
+ }
+
+ public function setCalendarLinkVisible($value) {
+ $this->setViewState(
+ 'CalendarLinkVisible',
+ TPropertyValue::ensureBoolean($value)
+ );
+ }
+
+ public function getCalendarLinkVisible() {
+ return $this->getViewState('CalendarLinkVisible', TRUE);
+ }
+
+}
+
+?>
diff --git a/app/frontend/controls/EventRepeater.tpl b/app/frontend/controls/EventRepeater.tpl
new file mode 100644
index 0000000..69aaabf
--- /dev/null
+++ b/app/frontend/controls/EventRepeater.tpl
@@ -0,0 +1,12 @@
+<com:TRepeater ID="Events">
+ <prop:ItemTemplate>
+ <%# $this->Data->DateString %>
+ <%# $this->Data->Name %>
+ <com:THyperLink>
+ <prop:Visible><%# $this->SourceTemplateControl->CalendarLinkVisible %></prop:Visible>
+ <prop:Text>(<%# $this->Data->Calendar->Name %>)</prop:Text>
+ <prop:NavigateUrl><%# $this->Service->constructUrl('Calendar', ['calendar' => $this->Data->Calendar->Url]) %></prop:NavigateUrl>
+ </com:THyperLink>
+ <br />
+ </prop:ItemTemplate>
+</com:TRepeater>
diff --git a/app/frontend/controls/HeaderMenu.php b/app/frontend/controls/HeaderMenu.php
new file mode 100644
index 0000000..2488629
--- /dev/null
+++ b/app/frontend/controls/HeaderMenu.php
@@ -0,0 +1,26 @@
+<?php
+
+Prado::using('Application.web.TemplateControl');
+
+Prado::using('System.Web.UI.ActiveControls.TActiveLinkButton');
+
+class HeaderMenu extends TemplateControl {
+
+ public function loginUser($sender, $param) {
+ $authModule = $this->Application->getModule('auth');
+ $authModule->setReturnUrl($this->Request->RequestUri);
+ $this->Response->redirect(
+ $this->Service->ConstructUrl($authModule->LoginPage)
+ );
+ }
+
+ public function logoutUser($sender, $param) {
+ $this->Application->getModule('auth')->logout();
+ $this->Response->redirect(
+ $this->Service->ConstructUrl(NULL)
+ );
+ }
+
+}
+
+?>
diff --git a/app/frontend/controls/HeaderMenu.tpl b/app/frontend/controls/HeaderMenu.tpl
new file mode 100644
index 0000000..a760588
--- /dev/null
+++ b/app/frontend/controls/HeaderMenu.tpl
@@ -0,0 +1,36 @@
+<nav role="navigation">
+ <com:TActiveLinkButton OnCommand="loginUser">
+ <prop:Text><%[ Login ]%></prop:Text>
+ <prop:Visible><%= $this->User->IsGuest %></prop:Visible>
+ <prop:ClientSide.OnFailure>window.location.replace('<%= $this->Service->constructUrl('Login') %>')</prop:ClientSide.OnFailure>
+ <prop:ClientSide.OnException>window.location.replace('<%= $this->Service->constructUrl('Login') %>')</prop:ClientSide.OnException>
+ </com:TActiveLinkButton>
+ <com:THyperLink>
+ <prop:Text><%[ Profile ]%></prop:Text>
+ <prop:NavigateUrl><%= $this->Service->constructUrl('Profile') %></prop:NavigateUrl>
+ <prop:Visible><%= !$this->User->IsGuest %></prop:Visible>
+ </com:THyperLink>
+ <com:THyperLink>
+ <prop:Text><%[ Calendar list ]%></prop:Text>
+ <prop:NavigateUrl><%= $this->Service->constructUrl('Select') %></prop:NavigateUrl>
+ </com:THyperLink>
+ <com:TActiveLinkButton OnCommand="logoutUser">
+ <com:TTranslate>
+ Logout ({name})
+ <com:TTranslateParameter Key="name"><%= $this->User->Name %></com:TTranslateParameter>
+ </com:TTranslate>
+ <prop:Visible><%= !$this->User->IsGuest %></prop:Visible>
+ <prop:ClientSide.OnFailure>window.location.reload()</prop:ClientSide.OnFailure>
+ <prop:ClientSide.OnException>window.location.reload()</prop:ClientSide.OnException>
+ </com:TActiveLinkButton>
+ <com:THyperLink>
+ <prop:Text><%[ New user ]%></prop:Text>
+ <prop:NavigateUrl><%= $this->Service->constructUrl('Signup') %></prop:NavigateUrl>
+ <prop:Visible><%= $this->User->getIsAdmin() %></prop:Visible>
+ </com:THyperLink>
+ <com:THyperLink>
+ <prop:Text><%[ Admin calendars ]%></prop:Text>
+ <prop:NavigateUrl><%= $this->Service->constructUrl('Admin') %></prop:NavigateUrl>
+ <prop:Visible><%= $this->User->getIsAdmin() %></prop:Visible>
+ </com:THyperLink>
+</nav>
diff --git a/app/frontend/controls/LoginBox.php b/app/frontend/controls/LoginBox.php
new file mode 100644
index 0000000..1136a79
--- /dev/null
+++ b/app/frontend/controls/LoginBox.php
@@ -0,0 +1,39 @@
+<?php
+
+Prado::using('Application.web.TemplateControl');
+
+class LoginBox extends TemplateControl {
+
+ public function onInit($param) {
+ parent::onInit($param);
+ if (!$this->Page->IsPostBack && !$this->User->IsGuest) {
+ $this->_afterLoginRedirect();
+ }
+ }
+
+ private function _afterLoginRedirect() {
+ $authModule = $this->Application->getModule('auth');
+ $redirUrl = $authModule->ReturnUrl;
+ if (!$redirUrl
+ || $redirUrl == $this->Service->constructUrl($authModule->LoginPage)) {
+ $redirUrl = $this->Service->constructUrl(NULL);
+ }
+ $this->Response->redirect($redirUrl);
+ }
+
+ public function loginUser($sender, $param) {
+ if ($this->Page->IsValid) {
+ $this->_afterLoginRedirect();
+ }
+ }
+
+ public function validatePassword($sender, $param) {
+ $param->IsValid = $this->Application->getModule('auth')->login(
+ $this->Login->Text,
+ $this->Password->Text
+ );
+ }
+
+}
+
+?>
diff --git a/app/frontend/controls/LoginBox.tpl b/app/frontend/controls/LoginBox.tpl
new file mode 100644
index 0000000..070ab86
--- /dev/null
+++ b/app/frontend/controls/LoginBox.tpl
@@ -0,0 +1,33 @@
+<%[ Username: ]%>
+<com:TTextBox ID="Login"
+ ValidationGroup="LoginGroup" />
+<com:TRequiredFieldValidator
+ ControlToValidate="Login"
+ Display="Dynamic"
+ ValidationGroup="LoginGroup">
+ <prop:ErrorMessage><%[ Username cannot be empty ]%></prop:ErrorMessage>
+</com:TRequiredFieldValidator>
+<br />
+<%[ Password: ]%>
+<com:TTextBox ID="Password"
+ TextMode="Password"
+ ValidationGroup="LoginGroup" />
+<com:TRequiredFieldValidator
+ ControlToValidate="Password"
+ Display="Dynamic"
+ ValidationGroup="LoginGroup">
+ <prop:ErrorMessage><%[ Password cannot be empty ]%></prop:ErrorMessage>
+</com:TRequiredFieldValidator>
+<com:TCustomValidator
+ ControlToValidate="Password"
+ OnServerValidate="validatePassword"
+ Display="Dynamic"
+ ValidationGroup="LoginGroup">
+ <prop:ErrorMessage><%[ Username and password don't match ]%></prop:ErrorMessage>
+</com:TCustomValidator>
+<br />
+<com:TButton
+ OnCommand="loginUser"
+ ValidationGroup="LoginGroup">
+ <prop:Text><%[ Login ]%></prop:Text>
+</com:TButton>
diff --git a/app/frontend/controls/PasswordChange.php b/app/frontend/controls/PasswordChange.php
new file mode 100644
index 0000000..45ce656
--- /dev/null
+++ b/app/frontend/controls/PasswordChange.php
@@ -0,0 +1,44 @@
+<?php
+
+Prado::using('Application.web.FacadeTemplateControl');
+
+Prado::using('Application.user.DbUser');
+Prado::using('Application.facades.UserFacade');
+
+class PasswordChange extends FacadeTemplateControl {
+
+ public function getUserToChange() {
+ return $this->getControlState('user');
+ }
+
+ public function setUserToChange(DbUser $user) {
+ if ($user->IsGuest && !$this->Page->IsCallBack) {
+ throw new TInvalidDataValueException(
+ Prado::localize(
+ 'Password change impossible for guest user'
+ )
+ );
+ }
+ $this->setControlState('user', $user);
+ }
+
+ public function checkPassword($sender, $param) {
+ $param->IsValid = $this->getFacade()->verifyUserPassword(
+ $this->Password->Text, $this->UserToChange
+ );
+ }
+
+ public function changePassword($sender, $param) {
+ $this->SuccessMessage->Visible = FALSE;
+ if ($this->Page->IsValid) {
+ $this->getFacade()->changePassword(
+ $this->UserToChange,
+ $this->NewPassword->Text
+ );
+ $this->SuccessMessage->Visible = TRUE;
+ }
+ }
+
+}
+
+?>
diff --git a/app/frontend/controls/PasswordChange.tpl b/app/frontend/controls/PasswordChange.tpl
new file mode 100644
index 0000000..4eb9a8e
--- /dev/null
+++ b/app/frontend/controls/PasswordChange.tpl
@@ -0,0 +1,59 @@
+<%[ Change password ]%><br />
+<%[ Current password: ]%>
+<com:TTextBox ID="Password"
+ TextMode="Password"
+ ValidationGroup="ChangePasswordGroup" />
+<com:TRequiredFieldValidator
+ ControlToValidate="Password"
+ Display="Dynamic"
+ ValidationGroup="ChangePasswordGroup">
+ <prop:ErrorMessage><%[ Current password cannot be empty ]%></prop:ErrorMessage>
+</com:TRequiredFieldValidator>
+<com:TCustomValidator
+ ControlToValidate="Password"
+ OnServerValidate="checkPassword"
+ Display="Dynamic"
+ ValidationGroup="ChangePasswordGroup">
+ <prop:ErrorMessage><%[ Password is incorrect ]%></prop:ErrorMessage>
+</com:TCustomValidator>
+<br />
+<%[ New password: ]%>
+<com:TTextBox ID="NewPassword"
+ TextMode="Password"
+ ValidationGroup="ChangePasswordGroup" />
+<com:TRequiredFieldValidator
+ ControlToValidate="NewPassword"
+ Display="Dynamic"
+ ValidationGroup="ChangePasswordGroup">
+ <prop:ErrorMessage><%[ New password cannot be empty ]%></prop:ErrorMessage>
+</com:TRequiredFieldValidator>
+<br />
+<%[ Repeat password: ]%>
+<com:TTextBox ID="ReNewPassword"
+ TextMode="Password"
+ ValidationGroup="ChangePasswordGroup" />
+<com:TRequiredFieldValidator
+ ControlToValidate="ReNewPassword"
+ Display="Dynamic"
+ ValidationGroup="ChangePasswordGroup">
+ <prop:ErrorMessage><%[ New password cannot be empty ]%></prop:ErrorMessage>
+</com:TRequiredFieldValidator>
+<com:TCompareValidator
+ ControlToValidate="ReNewPassword"
+ ControlToCompare="NewPassword"
+ DataType="String"
+ Operator="Equal"
+ Display="Dynamic"
+ ValidationGroup="ChangePasswordGroup">
+ <prop:ErrorMessage><%[ Passwords don't match ]%></prop:ErrorMessage>
+</com:TCompareValidator>
+<br />
+<com:TButton
+ OnCommand="changePassword"
+ ValidationGroup="ChangePasswordGroup">
+ <prop:Text><%[ Change password ]%></prop:Text>
+</com:TButton>
+<com:TLabel ID="SuccessMessage"
+ Visible="false">
+ <prop:Text><%[ Your password has been changed ]%></prop:Text>
+</com:TLabel>
diff --git a/app/frontend/controls/RegistrationForm.php b/app/frontend/controls/RegistrationForm.php
new file mode 100644
index 0000000..46494e3
--- /dev/null
+++ b/app/frontend/controls/RegistrationForm.php
@@ -0,0 +1,28 @@
+<?php
+
+Prado::using('Application.web.FacadeTemplateControl');
+
+Prado::using('Application.facades.UserFacade');
+
+class RegistrationForm extends FacadeTemplateControl {
+
+ public function checkUsername($sender, $param) {
+ $param->IsValid = $this->getFacade()->checkForUsername($this->Login->SafeText);
+ }
+
+ public function registerUser($sender, $param) {
+ if ($this->Page->IsValid) {
+ $this->getFacade()->registerUser(
+ $this->Login->SafeText,
+ $this->Password->Text,
+ $this->Admin->Checked
+ );
+ $this->Response->redirect(
+ $this->Service->constructUrl(NULL)
+ );
+ }
+ }
+
+}
+
+?>
diff --git a/app/frontend/controls/RegistrationForm.tpl b/app/frontend/controls/RegistrationForm.tpl
new file mode 100644
index 0000000..9defe54
--- /dev/null
+++ b/app/frontend/controls/RegistrationForm.tpl
@@ -0,0 +1,66 @@
+<%[ Username: ]%>
+<com:TTextBox ID="Login"
+ ValidationGroup="SignupGroup" />
+<com:TRequiredFieldValidator
+ ControlToValidate="Login"
+ Display="Dynamic"
+ ValidationGroup="SignupGroup">
+ <prop:ErrorMessage><%[ Username cannot be empty ]%></prop:ErrorMessage>
+</com:TRequiredFieldValidator>
+<com:TRegularExpressionValidator
+ ControlToValidate="Login"
+ RegularExpression="[a-zA-Z0-9_]{6,255}"
+ Display="Dynamic"
+ ValidationGroup="SignupGroup">
+ <prop:ErrorMessage><%[ Username must contain 6-255 characters, all Latin alphanumeric or underscore ]%></prop:ErrorMessage>
+</com:TRegularExpressionValidator>
+<com:TCustomValidator
+ ControlToValidate="Login"
+ OnServerValidate="checkUsername"
+ Display="Dynamic"
+ ValidationGroup="SignupGroup">
+ <prop:ErrorMessage><%[ Username already exists ]%></prop:ErrorMessage>
+</com:TCustomValidator>
+<br />
+<%[ Password: ]%>
+<com:TTextBox ID="Password"
+ TextMode="Password"
+ ValidationGroup="SignupGroup" />
+<com:TRequiredFieldValidator
+ ControlToValidate="Password"
+ Display="Dynamic"
+ ValidationGroup="SignupGroup">
+ <prop:ErrorMessage><%[ Password cannot be empty ]%></prop:ErrorMessage>
+</com:TRequiredFieldValidator>
+<br />
+<%[ Repeat password: ]%>
+<com:TTextBox ID="RePassword"
+ TextMode="Password"
+ ValidationGroup="SignupGroup" />
+<com:TRequiredFieldValidator
+ ControlToValidate="RePassword"
+ Display="Dynamic"
+ ValidationGroup="SignupGroup">
+ <prop:ErrorMessage><%[ Password cannot be empty ]%></prop:ErrorMessage>
+</com:TRequiredFieldValidator>
+<com:TCompareValidator
+ ControlToValidate="RePassword"
+ ControlToCompare="Password"
+ DataType="String"
+ Operator="Equal"
+ Display="Dynamic"
+ ValidationGroup="SignupGroup">
+ <prop:ErrorMessage><%[ Passwords don't match ]%></prop:ErrorMessage>
+</com:TCompareValidator>
+<br />
+<%[ Admin: ]%>
+<com:TCheckBox ID="Admin"
+ ValidationGroup="SignupGroup" />
+<br />
+<com:TButton
+ OnCommand="registerUser"
+ ValidationGroup="SignupGroup">
+ <prop:Text><%[ Create ]%></prop:Text>
+</com:TButton>
+<com:TValidationSummary
+ ValidationGroup="SignupGroup" />
diff --git a/app/frontend/controls/TimezoneSelect.php b/app/frontend/controls/TimezoneSelect.php
new file mode 100644
index 0000000..25af453
--- /dev/null
+++ b/app/frontend/controls/TimezoneSelect.php
@@ -0,0 +1,58 @@
+<?php
+
+Prado::using('Application.web.FacadeTemplateControl');
+
+Prado::using('Application.user.DbUser');
+Prado::using('Application.facades.UserFacade');
+
+Prado::using('Application.dto.TimezoneDTO');
+
+class TimezoneSelect extends FacadeTemplateControl {
+
+ public function getUserToChange() {
+ return $this->getControlState('user');
+ }
+
+ public function setUserToChange(DbUser $user) {
+ if ($user->IsGuest && !$this->Page->IsCallBack) {
+ throw new TInvalidDataValueException(
+ Prado::localize(
+ '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->getFacade()->getTimezonePreference($this->UserToChange)->Name
+ );
+ }
+
+ public function saveTimezone($sender, $param) {
+ $this->getFacade()->setTimezonePreference(
+ $this->UserToChange,
+ $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/frontend/controls/TimezoneSelect.tpl b/app/frontend/controls/TimezoneSelect.tpl
new file mode 100644
index 0000000..ee703d6
--- /dev/null
+++ b/app/frontend/controls/TimezoneSelect.tpl
@@ -0,0 +1,5 @@
+<com:TDropDownList ID="Timezones" />
+<com:TButton
+ OnCommand="saveTimezone">
+ <prop:Text><%[ Save timezone ]%></prop:Text>
+</com:TButton>
diff --git a/app/frontend/controls/UpcomingEvents.php b/app/frontend/controls/UpcomingEvents.php
new file mode 100644
index 0000000..27fc723
--- /dev/null
+++ b/app/frontend/controls/UpcomingEvents.php
@@ -0,0 +1,33 @@
+<?php
+
+Prado::using('Application.web.FacadeTemplateControl');
+Prado::using('Application.user.DbUser');
+Prado::using('Application.facades.EventFacade');
+
+class UpcomingEvents extends FacadeTemplateControl {
+
+ public function getUserToDisplay() {
+ return $this->getControlState('user');
+ }
+
+ public function setUserToDisplay(DbUser $user) {
+ $this->setControlState('user', $user);
+ }
+
+ public function getEvents() {
+ return $this->_getEventsForUser($this->UserToDisplay);
+ }
+
+ private function _getEventsForUser(DbUser $user) {
+ $utc = new DateTimeZone('UTC');
+ $dateFrom = new DateTime('now', $utc);
+ $dateTo = new DateTime('+7 days', $utc);
+ return $this->getFacade()->getTimeframeListForUser(
+ $user,
+ $dateFrom, $dateTo
+ );
+ }
+
+}
+
+?>
diff --git a/app/frontend/controls/UpcomingEvents.tpl b/app/frontend/controls/UpcomingEvents.tpl
new file mode 100644
index 0000000..ac5f60c
--- /dev/null
+++ b/app/frontend/controls/UpcomingEvents.tpl
@@ -0,0 +1,5 @@
+<%[ Upcoming events: ]%>
+<br />
+<com:EventRepeater ID="Events">
+ <prop:Events><%= $this->getEvents() %></prop:Events>
+</com:EventRepeater>
diff --git a/app/frontend/controls/UrlBasedCalendarControl.php b/app/frontend/controls/UrlBasedCalendarControl.php
new file mode 100644
index 0000000..a5be82e
--- /dev/null
+++ b/app/frontend/controls/UrlBasedCalendarControl.php
@@ -0,0 +1,40 @@
+<?php
+
+Prado::using('Application.web.FacadeTemplateControl');
+Prado::using('Application.facades.CalendarFacade');
+
+class UrlBasedCalendarControl extends FacadeTemplateControl {
+
+ public function setCalendarUrl(string $url = NULL) {
+ if ($url) {
+ $calendar = $this->getFacade()->resolveUrl($url);
+ if ($calendar) {
+ $this->setControlState('Calendar', $calendar);
+ return;
+ }
+ }
+ if ($this->getRaiseException()) {
+ throw new THttpException(
+ 404,
+ Prado::localize('Page not found')
+ );
+ } else {
+ $this->Visible = FALSE;
+ }
+ }
+
+ public function getCalendar() {
+ return $this->getControlState('Calendar');
+ }
+
+ public function setRaiseException($value) {
+ $this->setControlState('RaiseException', TPropertyValue::ensureBoolean($value));
+ }
+
+ public function getRaiseException() {
+ return $this->getControlState('RaiseException', FALSE);
+ }
+
+}
+
+?>
diff --git a/app/frontend/controls/UserSelection.php b/app/frontend/controls/UserSelection.php
new file mode 100644
index 0000000..233f0bf
--- /dev/null
+++ b/app/frontend/controls/UserSelection.php
@@ -0,0 +1,45 @@
+<?php
+
+Prado::using('Application.web.FacadeTemplateControl');
+Prado::using('Application.user.DbUser');
+Prado::using('Application.facades.CalendarFacade');
+
+class UserSelection extends FacadeTemplateControl {
+
+ public function getUserToDisplay() {
+ return $this->getControlState('user');
+ }
+
+ public function setUserToDisplay(DbUser $user = NULL) {
+ $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) {
+ return $this->getFacade()->removeFromPreference(
+ $this->UserToDisplay,
+ $param->CommandParameter
+ );
+ }
+ }
+
+ private function _getUserSelection(DbUser $user) {
+ return $this->getFacade()->getPreferenceList($user);
+ }
+
+}
+
+?>
diff --git a/app/frontend/controls/UserSelection.tpl b/app/frontend/controls/UserSelection.tpl
new file mode 100644
index 0000000..8d20365
--- /dev/null
+++ b/app/frontend/controls/UserSelection.tpl
@@ -0,0 +1,29 @@
+<%[ 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>
+ <prop:Text><%[ (details) ]%></prop:Text>
+ <prop:NavigateUrl><%# $this->Service->constructUrl('Calendar', ['calendar' => $this->Data->Url]) %></prop:NavigateUrl>
+ </com:THyperLink>
+ <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/frontend/controls/config.xml b/app/frontend/controls/config.xml
new file mode 100644
index 0000000..61d7e5b
--- /dev/null
+++ b/app/frontend/controls/config.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+ <paths>
+ <using namespace="Application.controls.*" />
+ </paths>
+</configuration>
diff --git a/app/frontend/controls/scripts/AddToFilter.js b/app/frontend/controls/scripts/AddToFilter.js
new file mode 100644
index 0000000..e6d7b39
--- /dev/null
+++ b/app/frontend/controls/scripts/AddToFilter.js
@@ -0,0 +1,5 @@
+$(document).on('ready', function() {
+ $('input.addToFilterBox').on('change', function() {
+ $(this).attr('disabled', true);
+ });
+});
diff --git a/app/frontend/controls/scripts/CalendarGroupFilter.js b/app/frontend/controls/scripts/CalendarGroupFilter.js
new file mode 100644
index 0000000..e6c1d73
--- /dev/null
+++ b/app/frontend/controls/scripts/CalendarGroupFilter.js
@@ -0,0 +1,29 @@
+$(document).on('ready', function() {
+ var selectBoxes = $('.selectGroup input.box');
+ var selectAllBox = $('.selectAllGroups input.box');
+ selectBoxes.on('change', function() {
+ var groups = [];
+ var allSelected = true;
+ $('.selectGroup input.box').each(function() {
+ var box = $(this);
+ if (box.is(':checked')) {
+ groups.push(box.val());
+ } else {
+ allSelected = false;
+ }
+ });
+ if (allSelected) {
+ selectAllBox.prop('checked', true);
+ } else {
+ selectAllBox.removeAttr('checked');
+ }
+ $(document).trigger('Application.calendarGroupFilterChanged', {groups: groups});
+ });
+ selectAllBox.on('change', function() {
+ if ($(this).is(':checked')) {
+ selectBoxes.prop('checked', true).trigger('change');
+ } else {
+ selectBoxes.removeAttr('checked').trigger('change');
+ }
+ });
+});
diff --git a/app/frontend/controls/scripts/CalendarLabel.js b/app/frontend/controls/scripts/CalendarLabel.js
new file mode 100644
index 0000000..8193e56
--- /dev/null
+++ b/app/frontend/controls/scripts/CalendarLabel.js
@@ -0,0 +1,11 @@
+$(document).on('Application.calendarGroupFilterChanged', function(event, args) {
+ var selectedGroups = args.groups || [];
+ $('.calendar').each(function() {
+ var label = $(this);
+ if (selectedGroups.indexOf(label.attr('data-group')) >= 0) {
+ label.show();
+ } else {
+ label.hide();
+ }
+ });
+});
diff --git a/app/frontend/controls/scripts/CalendarScaffold.js b/app/frontend/controls/scripts/CalendarScaffold.js
new file mode 100644
index 0000000..d4b8ec5
--- /dev/null
+++ b/app/frontend/controls/scripts/CalendarScaffold.js
@@ -0,0 +1,8 @@
+$('body').on(
+ 'click',
+ 'main .calendarScaffold tbody a[href^="javascript:;//"], main .calendarScaffold tbody input.visibilityToggle',
+ function(e) {
+ var loader = $('<div>').addClass('calendarScaffoldLoader');
+ $('main .calendarScaffold div[id$="_Container"]').append(loader);
+ }
+);
diff --git a/app/frontend/controls/styles/CalendarGrid.css b/app/frontend/controls/styles/CalendarGrid.css
new file mode 100644
index 0000000..4710993
--- /dev/null
+++ b/app/frontend/controls/styles/CalendarGrid.css
@@ -0,0 +1,16 @@
+div.gridWeek {
+ clear: both;
+ display: flex;
+ flex-flow: row nowrap;
+}
+div.gridDay {
+ width: 14%;
+ min-height: 8em;
+ flex: 1 1 auto;
+}
+div.gridEvent, div.gridItem { height: 1.5em; padding: 0.3em 0.5em; margin: 0.1em 0; }
+div.gridEvent { overflow: hidden; white-space: nowrap; background: #ddd }
+div.gridEvent.beginDate { border-top-left-radius: 1.5em;
+ border-bottom-left-radius: 1.5em }
+div.gridEvent.endDate { border-top-right-radius: 1.5em;
+ border-bottom-right-radius: 1.5em }
diff --git a/app/frontend/controls/styles/CalendarScaffold.css b/app/frontend/controls/styles/CalendarScaffold.css
new file mode 100644
index 0000000..071f672
--- /dev/null
+++ b/app/frontend/controls/styles/CalendarScaffold.css
@@ -0,0 +1,11 @@
+.calendarScaffold {
+ position: relative;
+}
+.calendarScaffold .calendarScaffoldLoader {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: url(preloader.gif) no-repeat center 5% rgba(255,255,255,0.8);
+}