Skip to content

Commit

Permalink
implement IButtonWidget and IIconWidget
Browse files Browse the repository at this point in the history
Signed-off-by: Julien Veyssier <eneiluj@posteo.net>
  • Loading branch information
Julien Veyssier authored and miaulalala committed Oct 17, 2022
1 parent 5cc1531 commit 9768191
Show file tree
Hide file tree
Showing 6 changed files with 232 additions and 53 deletions.
1 change: 1 addition & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
['name' => 'view#index', 'url' => '/{view}/{timeRange}', 'verb' => 'GET', 'requirements' => ['view' => 'timeGridDay|timeGridWeek|dayGridMonth|listMonth'], 'postfix' => 'view.timerange'],
['name' => 'view#index', 'url' => '/{view}/{timeRange}/new/{mode}/{isAllDay}/{dtStart}/{dtEnd}', 'verb' => 'GET', 'requirements' => ['view' => 'timeGridDay|timeGridWeek|dayGridMonth|listMonth'], 'postfix' => 'view.timerange.new'],
['name' => 'view#index', 'url' => '/{view}/{timeRange}/edit/{mode}/{objectId}/{recurrenceId}', 'verb' => 'GET', 'requirements' => ['view' => 'timeGridDay|timeGridWeek|dayGridMonth|listMonth'], 'postfix' => 'view.timerange.edit'],
['name' => 'view#getCalendarDotSvg', 'url' => '/public/getCalendarDotSvg/{color}.svg', 'verb' => 'GET'],
// Appointments
['name' => 'appointment#index', 'url' => '/appointments/{userId}', 'verb' => 'GET'],
['name' => 'appointment#show', 'url' => '/appointment/{token}', 'verb' => 'GET'],
Expand Down
5 changes: 0 additions & 5 deletions css/dashboard.css
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
.app-icon-calendar {
background-image: url('../img/calendar-dark.svg');
}

/* for NC <= 24 */
body.theme--dark .app-icon-calendar {
background-image: url('../img/calendar.svg');
}
42 changes: 41 additions & 1 deletion lib/Controller/ViewController.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,12 @@
use OCA\Calendar\Service\Appointments\AppointmentConfigService;
use OCP\App\IAppManager;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\FileDisplayResponse;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Services\IInitialState;
use OCP\Files\IAppData;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use OCP\IConfig;
use OCP\IRequest;
use function in_array;
Expand All @@ -49,19 +53,23 @@ class ViewController extends Controller {
/** @var string */
private $userId;

private IAppData $appData;

public function __construct(string $appName,
IRequest $request,
IConfig $config,
AppointmentConfigService $appointmentConfigService,
IInitialState $initialStateService,
IAppManager $appManager,
?string $userId) {
?string $userId,
IAppData $appData) {
parent::__construct($appName, $request);
$this->config = $config;
$this->appointmentConfigService = $appointmentConfigService;
$this->initialStateService = $initialStateService;
$this->appManager = $appManager;
$this->userId = $userId;
$this->appData = $appData;
}

/**
Expand Down Expand Up @@ -148,4 +156,36 @@ private function getView(string $view): string {
return $view;
}
}

/**
* @NoAdminRequired
* @NoCSRFRequired
*
* This function makes the colour dots work for mobile widgets
*
* Returns an SVG with size32x32 if the hex colour is valid
* or a Nextcloud blue svg if the colour is not
*
* @param string $color - url encoded HEX colour
* @return FileDisplayResponse
* @throws NotFoundException
* @throws NotPermittedException
*/
public function getCalendarDotSvg(string $color = "#0082c9"): FileDisplayResponse {
$validColor = '#0082c9';
$color = trim(urldecode($color), '#');
if (preg_match('/^([0-9a-f]{3}|[0-9a-f]{6})$/i', $color)) {
$validColor = '#' . $color;
}
$svg = '<svg height="32" width="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><rect width="100%" height="100%" fill="' . $validColor . '"/></svg>';
$folderName = implode('_', [
'calendar',
$this->userId
]);
$folder = $this->appData->getFolder($folderName);
$file = $folder->newFile($color . '.svg', $svg);
$response = new FileDisplayResponse($file);
$response->cacheFor(24 * 3600); // 1 day
return $response;
}
}
106 changes: 71 additions & 35 deletions lib/Dashboard/CalendarWidget.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,44 +30,52 @@
use DateTimeImmutable;
use OCA\Calendar\AppInfo\Application;
use OCA\Calendar\Service\JSDataService;
use OCA\DAV\CalDAV\CalDavBackend;
use OCP\AppFramework\Services\IInitialState;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Calendar\IManager;
use OCP\Dashboard\IAPIWidget;
use OCP\Dashboard\IButtonWidget;
use OCP\Dashboard\IIconWidget;
use OCP\Dashboard\Model\WidgetButton;
use OCP\Dashboard\Model\WidgetItem;
use OCP\IDateTimeFormatter;
use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\Util;

class CalendarWidget implements IAPIWidget {
class CalendarWidget implements IAPIWidget, IButtonWidget, IIconWidget {
private IL10N $l10n;
private IInitialState $initialStateService;
private JSDataService $dataService;
private IDateTimeFormatter $dateTimeFormatter;
private IURLGenerator $urlGenerator;
private CalDavBackend $calDavBackend;
private IManager $calendarManager;
private ITimeFactory $timeFactory;

/**
* CalendarWidget constructor.
*
* @param IL10N $l10n
* @param IInitialState $initialStateService
* @param JSDataService $dataService
* @param IDateTimeFormatter $dateTimeFormatter
* @param IURLGenerator $urlGenerator
* @param CalDavBackend $calDavBackend
* @param IManager $calendarManager
*/
public function __construct(IL10N $l10n,
IInitialState $initialStateService,
JSDataService $dataService,
IDateTimeFormatter $dateTimeFormatter,
IURLGenerator $urlGenerator,
CalDavBackend $calDavBackend) {
IManager $calendarManager,
ITimeFactory $timeFactory) {
$this->l10n = $l10n;
$this->initialStateService = $initialStateService;
$this->dataService = $dataService;
$this->dateTimeFormatter = $dateTimeFormatter;
$this->urlGenerator = $urlGenerator;
$this->calDavBackend = $calDavBackend;
$this->calendarManager = $calendarManager;
$this->timeFactory = $timeFactory;
}

/**
Expand Down Expand Up @@ -105,6 +113,15 @@ public function getUrl(): ?string {
return null;
}

/**
* @inheritDoc
*/
public function getIconUrl(): string {
return $this->urlGenerator->getAbsoluteURL(
$this->urlGenerator->imagePath(Application::APP_ID, 'calendar-dark.svg')
);
}

/**
* @inheritDoc
*/
Expand All @@ -119,44 +136,63 @@ public function load(): void {

/**
* @inheritDoc
*
* @param string|null $since Use any PHP DateTime allowed values to get future dates
* @param int $limit Max 14 items is the default
*/
public function getItems(string $userId, ?string $since = null, int $limit = 7): array {
$calendars = $this->calDavBackend->getCalendarsForUser('principals/users/' . $userId);
$dateTimeNow = new DateTimeImmutable();
$inTwoWeeks = $dateTimeNow->add(new DateInterval('P14D'));
$events = [];
public function getItems(string $userId, ?string $since = null, int $limit = 14): array {
$calendars = $this->calendarManager->getCalendarsForPrincipal('principals/users/' . $userId);
$count = count($calendars);
if ($count === 0) {
return [];
}
$limitPerCalendar = (int)round(($limit / $count)) ?? 1;
$dateTime = (new DateTimeImmutable())->setTimestamp($this->timeFactory->getTime());
if ($since !== null) {
try {
$dateTime = new DateTimeImmutable($since);
} catch (\Exception $e) {
// silently drop the exception and proceed with "now"
}
}
$inTwoWeeks = $dateTime->add(new DateInterval('P14D'));
$options = [
'timerange' => [
'start' => $dateTimeNow,
'start' => $dateTime,
'end' => $inTwoWeeks,
]
];
$searchLimit = null;
foreach ($calendars as $calKey => $calendar) {
$searchResult = array_map(static function($event) use ($calendar, $dateTimeNow) {
$event['calendar_color'] = $calendar['{http://apple.com/ns/ical/}calendar-color'];
return $event;
}, $this->calDavBackend->search($calendar, '', [], $options, $searchLimit, 0));
array_push($events, ...$searchResult);
$widgetItems = [];
foreach ($calendars as $calendar) {
$searchResult = $calendar->search('', [], $options, $limitPerCalendar);
foreach ($searchResult as $calendarEvent) {
/** @var DateTimeImmutable $startDate */
$startDate = $calendarEvent['objects'][0]['DTSTART'][0];
$widget = new WidgetItem(
$calendarEvent['objects'][0]['SUMMARY'][0] ?? 'New Event',
$this->dateTimeFormatter->formatTimeSpan(DateTime::createFromImmutable($startDate)),
$this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('calendar.view.index', ['objectId' => $calendarEvent['uid']])),
$this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('calendar.view.getCalendarDotSvg', ['color' => $calendar->getDisplayColor() ?? '#0082c9'])), // default NC blue fallback
(string) $startDate->getTimestamp(),
);
$widgetItems[] = $widget;
}
}
// for each event, is there a simple way to get the first occurrence in the future?
return $widgetItems;
}

return array_map(function(array $event) {
/** @var DateTimeImmutable $startDate */
$startDate = $event['objects'][0]['DTSTART'][0];
return new WidgetItem(
$event['objects'][0]['SUMMARY'][0] ?? '',
$this->dateTimeFormatter->formatTimeSpan(DateTime::createFromImmutable($startDate)),
// TODO fix this route and get the correct objectId
/**
* @inheritDoc
*/
public function getWidgetButtons(string $userId): array {
return [
new WidgetButton(
WidgetButton::TYPE_MORE,
$this->urlGenerator->getAbsoluteURL(
$this->urlGenerator->linkToRoute('calendar.view.index', ['objectId' => $event['uid']])
$this->urlGenerator->linkToRoute(Application::APP_ID . '.view.index')
),
// TODO find or implement and endpoint providing colored dots images
// reminder: we can use the event color and the calendar color as a fallback
'',
// TODO this should be the next occurence date
(string) $startDate->getTimestamp(),
);
}, $events);
$this->l10n->t('More events')
),
];
}
}
6 changes: 6 additions & 0 deletions tests/php/unit/Controller/ViewControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
use OCP\App\IAppManager;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Services\IInitialState;
use OCP\Files\IAppData;
use OCP\IConfig;
use OCP\IRequest;
use ChristophWurst\Nextcloud\Testing\TestCase;
Expand Down Expand Up @@ -61,6 +62,9 @@ class ViewControllerTest extends TestCase {
/** @var ViewController */
private $controller;

/** @var IAppData|MockObject */
private $appData;

protected function setUp(): void {
$this->appName = 'calendar';
$this->request = $this->createMock(IRequest::class);
Expand All @@ -69,6 +73,7 @@ protected function setUp(): void {
$this->appointmentContfigService = $this->createMock(AppointmentConfigService::class);
$this->initialStateService = $this->createMock(IInitialState::class);
$this->userId = 'user123';
$this->appData = $this->createMock(IAppData::class);

$this->controller = new ViewController(
$this->appName,
Expand All @@ -78,6 +83,7 @@ protected function setUp(): void {
$this->initialStateService,
$this->appManager,
$this->userId,
$this->appData,
);
}

Expand Down
Loading

0 comments on commit 9768191

Please sign in to comment.