Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@
'verb' => 'GET',
'postfix' => 'welcome',
],
[
'name' => 'page#create',
'url' => '/new',
'verb' => 'GET',
'postfix' => 'new',
],
[
'name' => 'page#index',
'url' => '/note/{id}',
Expand All @@ -28,6 +34,11 @@
'url' => '/notes',
'verb' => 'GET',
],
[
'name' => 'notes#dashboard',
'url' => '/notes/dashboard',
'verb' => 'GET',
],
[
'name' => 'notes#get',
'url' => '/notes/{id}',
Expand Down
1 change: 1 addition & 0 deletions css/global.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@include icon-black-white('notes', 'notes', 1);
6 changes: 6 additions & 0 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent;

class Application extends App implements IBootstrap {
public const APP_ID = 'notes';
Expand All @@ -19,6 +20,11 @@ public function __construct(array $urlParams = []) {

public function register(IRegistrationContext $context): void {
$context->registerCapability(Capabilities::class);
$context->registerDashboardWidget(DashboardWidget::class);
$context->registerEventListener(
BeforeTemplateRenderedEvent::class,
BeforeTemplateRenderedListener::class
);
}

public function boot(IBootContext $context): void {
Expand Down
21 changes: 21 additions & 0 deletions lib/AppInfo/BeforeTemplateRenderedListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace OCA\Notes\AppInfo;

use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;

class BeforeTemplateRenderedListener implements IEventListener {
public function handle(Event $event): void {
if (!($event instanceof BeforeTemplateRenderedEvent)) {
return;
}
if (!$event->isLoggedIn()) {
return;
}
\OCP\Util::addStyle('notes', 'global');
}
}
67 changes: 67 additions & 0 deletions lib/AppInfo/DashboardWidget.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

declare(strict_types=1);

namespace OCA\Notes\AppInfo;

use OCP\Dashboard\IWidget;
use OCP\IL10N;
use OCP\IURLGenerator;

class DashboardWidget implements IWidget {

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

public function __construct(
IURLGenerator $url,
IL10N $l10n
) {
$this->url = $url;
$this->l10n = $l10n;
}

/**
* @inheritDoc
*/
public function getId(): string {
return 'notes';
}

/**
* @inheritDoc
*/
public function getTitle(): string {
return $this->l10n->t('Notes');
}

/**
* @inheritDoc
*/
public function getOrder(): int {
return 30;
}

/**
* @inheritDoc
*/
public function getIconClass(): string {
return 'icon-notes';
}

/**
* @inheritDoc
*/
public function getUrl(): ?string {
return $this->url->linkToRouteAbsolute('notes.page.index');
}

/**
* @inheritDoc
*/
public function load(): void {
\OCP\Util::addScript('notes', 'notes-dashboard');
}
}
32 changes: 32 additions & 0 deletions lib/Controller/NotesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,38 @@ public function index(int $pruneBefore = 0) : JSONResponse {
}


/**
* @NoAdminRequired
*/
public function dashboard() : JSONResponse {
return $this->helper->handleErrorResponse(function () {
$maxItems = 7;
$userId = $this->helper->getUID();
$notes = $this->notesService->getTopNotes($userId, $maxItems + 1);
$hasMoreNotes = count($notes) > $maxItems;
$notes = array_slice($notes, 0, $maxItems);
$items = array_map(function ($note) {
$excerpt = '';
try {
$excerpt = $note->getExcerpt();
} catch (\Throwable $e) {
}
return [
'id' => $note->getId(),
'title' => $note->getTitle(),
'category' => $note->getCategory(),
'favorite' => $note->getFavorite(),
'excerpt' => $excerpt,
];
}, $notes);
return [
'items' => $items,
'hasMoreItems' => $hasMoreNotes,
];
});
}


/**
* @NoAdminRequired
*/
Expand Down
36 changes: 33 additions & 3 deletions lib/Controller/PageController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,41 @@

namespace OCA\Notes\Controller;

use OCA\Notes\Service\NotesService;

use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Http\ContentSecurityPolicy;
use OCP\AppFramework\Http\RedirectResponse;
use OCP\IRequest;
use OCP\IURLGenerator;
use OCP\IUserSession;

class PageController extends Controller {
public function __construct(string $AppName, IRequest $request) {
/** @NotesService */
private $notesService;
/** @var IUserSession */
private $userSession;
/** @IURLGenerator */
private $urlGenerator;

public function __construct(
string $AppName,
IRequest $request,
NotesService $notesService,
IUserSession $userSession,
IURLGenerator $urlGenerator
) {
parent::__construct($AppName, $request);
$this->notesService = $notesService;
$this->userSession = $userSession;
$this->urlGenerator = $urlGenerator;
}


/**
* @NoAdminRequired
* @NoCSRFRequired
*
* @return TemplateResponse
*/
public function index() : TemplateResponse {
$devMode = !is_file(dirname(__FILE__).'/../../js/notes-main.js');
Expand All @@ -35,4 +54,15 @@ public function index() : TemplateResponse {

return $response;
}

/**
* @NoAdminRequired
* @NoCSRFRequired
*/
public function create() : RedirectResponse {
$note = $this->notesService->create($this->userSession->getUser()->getUID(), '', '');
$note->setContent('');
$url = $this->urlGenerator->linkToRoute('notes.page.index', [ 'id' => $note->getId() ]);
return new RedirectResponse($url);
}
}
16 changes: 16 additions & 0 deletions lib/Service/Note.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,22 @@ public function getContent() : string {
return $content;
}

public function getExcerpt(int $maxlen = 100) : string {
$excerpt = trim($this->noteUtil->stripMarkdown($this->getContent()));
$title = $this->getTitle();
if (!empty($title)) {
$length = strlen($title);
if (strncasecmp($excerpt, $title, $length) === 0) {
$excerpt = substr($excerpt, $length);
}
}
$excerpt = trim($excerpt);
if (strlen($excerpt) > $maxlen) {
$excerpt = substr($excerpt, 0, $maxlen) . '…';
}
return str_replace("\n", "\u{2003}", $excerpt);
}

public function getModified() : int {
return $this->file->getMTime();
}
Expand Down
9 changes: 9 additions & 0 deletions lib/Service/NoteUtil.php
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,15 @@ private function sanitisePath(string $str) : string {
return trim($str);
}

public function stripMarkdown(string $str) : string {
// prepare content: remove markdown characters and empty spaces
$str = preg_replace("/^\s*[*+-]\s+/mu", "", $str); // list item
$str = preg_replace("/^#+\s+(.*?)\s*#*$/mu", "$1", $str); // headline
$str = preg_replace("/^(=+|-+)$/mu", "", $str); // separate line for headline
$str = preg_replace("/(\*+|_+)(.*?)\\1/mu", "$2", $str); // emphasis
return $str;
}

/**
* Finds a folder and creates it if non-existent
* @param string $path path to the folder
Expand Down
20 changes: 15 additions & 5 deletions lib/Service/NotesService.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,20 @@ public function getAll(string $userId) : array {
return [ 'notes' => $notes, 'categories' => $data['categories'] ];
}

public function getTopNotes(string $userId, int $count) : array {
$notes = $this->getAll($userId)['notes'];
usort($notes, function (Note $a, Note $b) {
$favA = $a->getFavorite();
$favB = $b->getFavorite();
if ($favA === $favB) {
return $b->getModified() - $a->getModified();
} else {
return $favA > $favB ? -1 : 1;
}
});
return array_slice($notes, 0, $count);
}

public function get(string $userId, int $id) : Note {
$notesFolder = $this->getNotesFolder($userId);
$note = new Note($this->getFileById($notesFolder, $id), $notesFolder, $this->noteUtil);
Expand Down Expand Up @@ -89,11 +103,7 @@ public function delete(string $userId, int $id) {
}

public function getTitleFromContent(string $content) : string {
// prepare content: remove markdown characters and empty spaces
$content = preg_replace("/^\s*[*+-]\s+/mu", "", $content); // list item
$content = preg_replace("/^#+\s+(.*?)\s*#*$/mu", "$1", $content); // headline
$content = preg_replace("/^(=+|-+)$/mu", "", $content); // separate line for headline
$content = preg_replace("/(\*+|_+)(.*?)\\1/mu", "$2", $content); // emphasis
$content = $this->noteUtil->stripMarkdown($content);
return $this->noteUtil->getSafeTitle($content);
}

Expand Down
26 changes: 26 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"@nextcloud/moment": "^1.1.1",
"@nextcloud/router": "^1.2.0",
"@nextcloud/vue": "^2.6.6",
"@nextcloud/vue-dashboard": "^1.0.1",
"@nextcloud/webpack-vue-config": "^1.4.1",
"easymde": "^2.12.0",
"markdown-it": "^11.0.1",
Expand Down Expand Up @@ -65,6 +66,7 @@
"vue-loader": "^15.9.3",
"vue-template-compiler": "^2.6.12",
"webpack": "^4.44.2",
"webpack-cli": "^3.3.12"
"webpack-cli": "^3.3.12",
"webpack-merge": "^5.1.4"
}
}
13 changes: 13 additions & 0 deletions src/NotesService.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,19 @@ export const setSettings = settings => {
})
}

export const getDashboardData = () => {
return axios
.get(url('/notes/dashboard'))
.then(response => {
return response.data
})
.catch(err => {
console.error(err)
handleSyncError(t('notes', 'Fetching notes for dashboard has failed.'), err)
throw err
})
}

export const fetchNotes = () => {
const lastETag = store.state.sync.etag
const lastModified = store.state.sync.lastModified
Expand Down
Loading