Skip to content

Commit

Permalink
Merge pull request #4474 from nextcloud/enh/4451/implement-new-dashbo…
Browse files Browse the repository at this point in the history
…ard-apis

Implement new dashboard APIs
  • Loading branch information
miaulalala authored Oct 17, 2022
2 parents 544db52 + 9768191 commit 946f6ed
Show file tree
Hide file tree
Showing 8 changed files with 284 additions and 39 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
3 changes: 3 additions & 0 deletions css/dashboard.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.app-icon-calendar {
background-image: url('../img/calendar-dark.svg');
}
6 changes: 6 additions & 0 deletions img/calendar-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions img/calendar.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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;
}
}
136 changes: 112 additions & 24 deletions lib/Dashboard/CalendarWidget.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,41 +25,57 @@

namespace OCA\Calendar\Dashboard;

use DateInterval;
use DateTime;
use DateTimeImmutable;
use OCA\Calendar\AppInfo\Application;
use OCA\Calendar\Service\JSDataService;
use OCP\Dashboard\IWidget;
use OCP\IInitialStateService;
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 IWidget {

/**
* @var IL10N
*/
private $l10n;

/**
* @var IInitialStateService
*/
private $initialStateService;

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

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

/**
Expand Down Expand Up @@ -87,7 +103,7 @@ public function getOrder(): int {
* @inheritDoc
*/
public function getIconClass(): string {
return 'icon-calendar-dark';
return 'app-icon-calendar';
}

/**
Expand All @@ -97,14 +113,86 @@ 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
*/
public function load(): void {
\OCP\Util::addScript('calendar', 'calendar-dashboard');
Util::addScript(Application::APP_ID, 'calendar-dashboard');
Util::addStyle(Application::APP_ID, 'dashboard');

$this->initialStateService->provideLazyInitialState(Application::APP_ID, 'dashboard_data', function () {
$this->initialStateService->provideLazyInitialState('dashboard_data', function () {
return $this->dataService;
});
}

/**
* @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 = 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' => $dateTime,
'end' => $inTwoWeeks,
]
];
$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;
}
}
return $widgetItems;
}

/**
* @inheritDoc
*/
public function getWidgetButtons(string $userId): array {
return [
new WidgetButton(
WidgetButton::TYPE_MORE,
$this->urlGenerator->getAbsoluteURL(
$this->urlGenerator->linkToRoute(Application::APP_ID . '.view.index')
),
$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 946f6ed

Please sign in to comment.