This commit is contained in:
Michel Roux 2023-06-23 09:44:48 +02:00
parent 341825adab
commit 099013ec89
30 changed files with 22502 additions and 415 deletions

4
.gitignore vendored
View File

@ -3,5 +3,5 @@
/vendor/ /vendor/
/build/ /build/
node_modules/ node_modules/
/.php_cs.cache /.php-cs-fixer.cache
js/*hot-update.* js/

View File

@ -1,46 +1,10 @@
# Generic Makefile for building and packaging a Nextcloud app which uses npm and
# Composer.
#
# Dependencies:
# * make
# * which
# * curl: used if phpunit and composer are not installed to fetch them from the web
# * tar: for building the archive
# * npm: for building and testing everything JS
#
# If no composer.json is in the app root directory, the Composer step
# will be skipped. The same goes for the package.json which can be located in
# the app root or the js/ directory.
#
# The npm command by launches the npm build script:
#
# npm run build
#
# The npm test command launches the npm test script:
#
# npm run test
#
# The idea behind this is to be completely testing and build tool agnostic. All
# build tools and additional package managers should be installed locally in
# your project, since this won't pollute people's global namespace.
#
# The following npm scripts in your package.json install and update the bower
# and npm dependencies and use gulp as build system (notice how everything is
# run from the node_modules folder):
#
# "scripts": {
# "test": "node node_modules/gulp-cli/bin/gulp.js karma",
# "prebuild": "npm install && node_modules/bower/bin/bower install && node_modules/bower/bin/bower update",
# "build": "node node_modules/gulp-cli/bin/gulp.js"
# },
app_name=$(notdir $(CURDIR)) app_name=$(notdir $(CURDIR))
build_tools_directory=$(CURDIR)/build/tools build_tools_directory=$(CURDIR)/build/tools
source_build_directory=$(CURDIR)/build/artifacts/source source_build_directory=$(CURDIR)/build/artifacts/source
source_package_name=$(source_build_directory)/$(app_name) source_package_name=$(source_build_directory)/$(app_name)
appstore_build_directory=$(CURDIR)/build/artifacts/appstore appstore_build_directory=$(CURDIR)/build/artifacts/appstore
appstore_package_name=$(appstore_build_directory)/$(app_name) appstore_package_name=$(appstore_build_directory)/$(app_name)
npm=$(shell which yarn 2> /dev/null) npm=$(shell which npm 2> /dev/null)
composer=$(shell which composer 2> /dev/null) composer=$(shell which composer 2> /dev/null)
all: build all: build
@ -77,11 +41,8 @@ endif
# Installs npm dependencies # Installs npm dependencies
.PHONY: npm .PHONY: npm
npm: npm:
ifeq (,$(wildcard $(CURDIR)/package.json)) npm install
cd js && $(npm) run build
else
npm run build npm run build
endif
# Removes the appstore build # Removes the appstore build
.PHONY: clean .PHONY: clean
@ -96,55 +57,3 @@ distclean: clean
rm -rf node_modules rm -rf node_modules
rm -rf js/vendor rm -rf js/vendor
rm -rf js/node_modules rm -rf js/node_modules
# Builds the source and appstore package
.PHONY: dist
dist:
make source
make appstore
# Builds the source package
.PHONY: source
source:
rm -rf $(source_build_directory)
mkdir -p $(source_build_directory)
tar cvzf $(source_package_name).tar.gz ../$(app_name) \
--exclude-vcs \
--exclude="../$(app_name)/build" \
--exclude="../$(app_name)/js/node_modules" \
--exclude="../$(app_name)/node_modules" \
--exclude="../$(app_name)/*.log" \
--exclude="../$(app_name)/js/*.log" \
# Builds the source package for the app store, ignores php and js tests
.PHONY: appstore
appstore:
rm -rf $(appstore_build_directory)
mkdir -p $(appstore_build_directory)
tar cvzf $(appstore_package_name).tar.gz ../$(app_name) \
--exclude-vcs \
--exclude="../$(app_name)/build" \
--exclude="../$(app_name)/tests" \
--exclude="../$(app_name)/Makefile" \
--exclude="../$(app_name)/*.log" \
--exclude="../$(app_name)/phpunit*xml" \
--exclude="../$(app_name)/composer.*" \
--exclude="../$(app_name)/js/node_modules" \
--exclude="../$(app_name)/js/tests" \
--exclude="../$(app_name)/js/test" \
--exclude="../$(app_name)/js/*.log" \
--exclude="../$(app_name)/js/package.json" \
--exclude="../$(app_name)/js/bower.json" \
--exclude="../$(app_name)/js/karma.*" \
--exclude="../$(app_name)/js/protractor.*" \
--exclude="../$(app_name)/package.json" \
--exclude="../$(app_name)/bower.json" \
--exclude="../$(app_name)/karma.*" \
--exclude="../$(app_name)/protractor\.*" \
--exclude="../$(app_name)/.*" \
--exclude="../$(app_name)/js/.*" \
.PHONY: test
test: composer
$(CURDIR)/vendor/phpunit/phpunit/phpunit -c phpunit.xml
$(CURDIR)/vendor/phpunit/phpunit/phpunit -c phpunit.integration.xml

View File

@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
/** /**

View File

@ -3,34 +3,19 @@
"description": "🔊 Browse, manage and listen to podcasts", "description": "🔊 Browse, manage and listen to podcasts",
"type": "project", "type": "project",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"authors": [
{
"name": "Xéfir Destiny"
}
],
"require-dev": { "require-dev": {
"phpunit/phpunit": "^9", "nextcloud/ocp": "^27.0.0",
"sabre/dav": "^4.1", "psalm/phar": "^5.12.0",
"sabre/xml": "^2.2", "nextcloud/coding-standard": "^1.1.1"
"symfony/event-dispatcher": "^5.3.11",
"christophwurst/nextcloud": "dev-master@dev",
"psalm/phar": "^4.10",
"nextcloud/coding-standard": "^1.0"
}, },
"scripts": { "scripts": {
"lint": "find . -name \\*.php -not -path './vendor/*' -print0 | xargs -0 -n1 php -l", "lint": "find . -name \\*.php -not -path './vendor/*' -print0 | xargs -0 -n1 php -l",
"cs:check": "php-cs-fixer fix --dry-run --diff", "cs:check": "php-cs-fixer fix --dry-run --diff",
"cs:fix": "php-cs-fixer fix", "cs:fix": "php-cs-fixer fix",
"psalm": "psalm.phar --threads=1", "psalm:check": "psalm.phar --threads=1 --no-cache --show-info=true",
"psalm:update-baseline": "psalm.phar --threads=1 --update-baseline", "psalm:fix": "psalm.phar --no-cache --alter --issues=InvalidReturnType,InvalidNullableReturnType,MissingParamType,InvalidFalsableReturnType"
"psalm:update-baseline:force": "psalm.phar --threads=1 --update-baseline --set-baseline=tests/psalm-baseline.xml",
"psalm:clear": "psalm.phar --clear-cache && psalm --clear-global-cache",
"psalm:fix": "psalm.phar --alter --issues=InvalidReturnType,InvalidNullableReturnType,MissingParamType,InvalidFalsableReturnType"
}, },
"config": { "config": {
"allow-plugins": {
"composer/package-versions-deprecated": true
},
"platform": { "platform": {
"php": "7.4" "php": "7.4"
} }

395
composer.lock generated Normal file
View File

@ -0,0 +1,395 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "9de82c8d48d25264d6223d576d21996d",
"packages": [],
"packages-dev": [
{
"name": "nextcloud/coding-standard",
"version": "v1.1.1",
"source": {
"type": "git",
"url": "https://github.com/nextcloud/coding-standard.git",
"reference": "55def702fb9a37a219511e1d8c6fe8e37164c1fb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nextcloud/coding-standard/zipball/55def702fb9a37a219511e1d8c6fe8e37164c1fb",
"reference": "55def702fb9a37a219511e1d8c6fe8e37164c1fb",
"shasum": ""
},
"require": {
"php": "^7.3|^8.0",
"php-cs-fixer/shim": "^3.17"
},
"type": "library",
"autoload": {
"psr-4": {
"Nextcloud\\CodingStandard\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Christoph Wurst",
"email": "christoph@winzerhof-wurst.at"
}
],
"description": "Nextcloud coding standards for the php cs fixer",
"support": {
"issues": "https://github.com/nextcloud/coding-standard/issues",
"source": "https://github.com/nextcloud/coding-standard/tree/v1.1.1"
},
"time": "2023-06-01T12:05:01+00:00"
},
{
"name": "nextcloud/ocp",
"version": "v27.0.0",
"source": {
"type": "git",
"url": "https://github.com/nextcloud-deps/ocp.git",
"reference": "c374aa2d4c3491812c19a53ce14063127daea08e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/c374aa2d4c3491812c19a53ce14063127daea08e",
"reference": "c374aa2d4c3491812c19a53ce14063127daea08e",
"shasum": ""
},
"require": {
"php": "^7.4 || ~8.0 || ~8.1",
"psr/clock": "^1.0",
"psr/container": "^2.0.2",
"psr/event-dispatcher": "^1.0",
"psr/log": "^1.1.4"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-stable27": "27.0.0-dev"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"AGPL-3.0-or-later"
],
"authors": [
{
"name": "Christoph Wurst",
"email": "christoph@winzerhof-wurst.at"
}
],
"description": "Composer package containing Nextcloud's public API (classes, interfaces)",
"support": {
"issues": "https://github.com/nextcloud-deps/ocp/issues",
"source": "https://github.com/nextcloud-deps/ocp/tree/v27.0.0"
},
"time": "2023-06-16T07:25:09+00:00"
},
{
"name": "php-cs-fixer/shim",
"version": "v3.18.0",
"source": {
"type": "git",
"url": "https://github.com/PHP-CS-Fixer/shim.git",
"reference": "a517e03dd0727336e502e071cc08e406ac878dba"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/shim/zipball/a517e03dd0727336e502e071cc08e406ac878dba",
"reference": "a517e03dd0727336e502e071cc08e406ac878dba",
"shasum": ""
},
"require": {
"ext-json": "*",
"ext-tokenizer": "*",
"php": "^7.4 || ^8.0"
},
"replace": {
"friendsofphp/php-cs-fixer": "self.version"
},
"suggest": {
"ext-dom": "For handling output formats in XML",
"ext-mbstring": "For handling non-UTF8 characters."
},
"bin": [
"php-cs-fixer",
"php-cs-fixer.phar"
],
"type": "application",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Dariusz Rumiński",
"email": "dariusz.ruminski@gmail.com"
}
],
"description": "A tool to automatically fix PHP code style",
"support": {
"issues": "https://github.com/PHP-CS-Fixer/shim/issues",
"source": "https://github.com/PHP-CS-Fixer/shim/tree/v3.18.0"
},
"time": "2023-06-18T22:26:36+00:00"
},
{
"name": "psalm/phar",
"version": "5.12.0",
"source": {
"type": "git",
"url": "https://github.com/psalm/phar.git",
"reference": "e7f9306ec83c706b4dba451f6adfb865ce4688e4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/psalm/phar/zipball/e7f9306ec83c706b4dba451f6adfb865ce4688e4",
"reference": "e7f9306ec83c706b4dba451f6adfb865ce4688e4",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0"
},
"conflict": {
"vimeo/psalm": "*"
},
"bin": [
"psalm.phar"
],
"type": "library",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Composer-based Psalm Phar",
"support": {
"issues": "https://github.com/psalm/phar/issues",
"source": "https://github.com/psalm/phar/tree/5.12.0"
},
"time": "2023-05-22T21:30:41+00:00"
},
{
"name": "psr/clock",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/clock.git",
"reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d",
"reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d",
"shasum": ""
},
"require": {
"php": "^7.0 || ^8.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Psr\\Clock\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for reading the clock.",
"homepage": "https://github.com/php-fig/clock",
"keywords": [
"clock",
"now",
"psr",
"psr-20",
"time"
],
"support": {
"issues": "https://github.com/php-fig/clock/issues",
"source": "https://github.com/php-fig/clock/tree/1.0.0"
},
"time": "2022-11-25T14:36:26+00:00"
},
{
"name": "psr/container",
"version": "2.0.2",
"source": {
"type": "git",
"url": "https://github.com/php-fig/container.git",
"reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963",
"reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963",
"shasum": ""
},
"require": {
"php": ">=7.4.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Container\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common Container Interface (PHP FIG PSR-11)",
"homepage": "https://github.com/php-fig/container",
"keywords": [
"PSR-11",
"container",
"container-interface",
"container-interop",
"psr"
],
"support": {
"issues": "https://github.com/php-fig/container/issues",
"source": "https://github.com/php-fig/container/tree/2.0.2"
},
"time": "2021-11-05T16:47:00+00:00"
},
{
"name": "psr/event-dispatcher",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/event-dispatcher.git",
"reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0",
"reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0",
"shasum": ""
},
"require": {
"php": ">=7.2.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\EventDispatcher\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Standard interfaces for event handling.",
"keywords": [
"events",
"psr",
"psr-14"
],
"support": {
"issues": "https://github.com/php-fig/event-dispatcher/issues",
"source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0"
},
"time": "2019-01-08T18:20:26+00:00"
},
{
"name": "psr/log",
"version": "1.1.4",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
"reference": "d49695b909c3b7628b6289db5479a1c204601f11"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
"reference": "d49695b909c3b7628b6289db5479a1c204601f11",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.1.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Log\\": "Psr/Log/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for logging libraries",
"homepage": "https://github.com/php-fig/log",
"keywords": [
"log",
"psr",
"psr-3"
],
"support": {
"source": "https://github.com/php-fig/log/tree/1.1.4"
},
"time": "2021-05-03T11:20:27+00:00"
}
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
"platform-overrides": {
"php": "7.4"
},
"plugin-api-version": "2.3.0"
}

View File

@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
namespace OCA\RePod\AppInfo; namespace OCA\RePod\AppInfo;

View File

@ -1,14 +1,15 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
namespace OCA\RePod\Controller; namespace OCA\RePod\Controller;
use Closure; use Closure;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCA\RePod\Service\NoteNotFound; use OCA\RePod\Service\NoteNotFound;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
trait Errors { trait Errors {
protected function handleNotFound(Closure $callback): DataResponse { protected function handleNotFound(Closure $callback): DataResponse {

View File

@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
namespace OCA\RePod\Controller; namespace OCA\RePod\Controller;
@ -16,8 +17,8 @@ class NoteApiController extends ApiController {
use Errors; use Errors;
public function __construct(IRequest $request, public function __construct(IRequest $request,
NoteService $service, NoteService $service,
?string $userId) { ?string $userId) {
parent::__construct(Application::APP_ID, $request); parent::__construct(Application::APP_ID, $request);
$this->service = $service; $this->service = $service;
$this->userId = $userId; $this->userId = $userId;
@ -59,7 +60,7 @@ class NoteApiController extends ApiController {
* @NoAdminRequired * @NoAdminRequired
*/ */
public function update(int $id, string $title, public function update(int $id, string $title,
string $content): DataResponse { string $content): DataResponse {
return $this->handleNotFound(function () use ($id, $title, $content) { return $this->handleNotFound(function () use ($id, $title, $content) {
return $this->service->update($id, $title, $content, $this->userId); return $this->service->update($id, $title, $content, $this->userId);
}); });

View File

@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
namespace OCA\RePod\Controller; namespace OCA\RePod\Controller;
@ -16,8 +17,8 @@ class NoteController extends Controller {
use Errors; use Errors;
public function __construct(IRequest $request, public function __construct(IRequest $request,
NoteService $service, NoteService $service,
?string $userId) { ?string $userId) {
parent::__construct(Application::APP_ID, $request); parent::__construct(Application::APP_ID, $request);
$this->service = $service; $this->service = $service;
$this->userId = $userId; $this->userId = $userId;
@ -51,7 +52,7 @@ class NoteController extends Controller {
* @NoAdminRequired * @NoAdminRequired
*/ */
public function update(int $id, string $title, public function update(int $id, string $title,
string $content): DataResponse { string $content): DataResponse {
return $this->handleNotFound(function () use ($id, $title, $content) { return $this->handleNotFound(function () use ($id, $title, $content) {
return $this->service->update($id, $title, $content, $this->userId); return $this->service->update($id, $title, $content, $this->userId);
}); });

View File

@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
namespace OCA\RePod\Controller; namespace OCA\RePod\Controller;

View File

@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
namespace OCA\RePod\Db; namespace OCA\RePod\Db;

View File

@ -1,10 +1,10 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
namespace OCA\RePod\Db; namespace OCA\RePod\Db;
use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\Entity;
use OCP\AppFramework\Db\QBMapper; use OCP\AppFramework\Db\QBMapper;
use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection; use OCP\IDBConnection;

View File

@ -1,12 +1,13 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
namespace OCA\RePod\Migration; namespace OCA\RePod\Migration;
use Closure; use Closure;
use OCP\DB\ISchemaWrapper; use OCP\DB\ISchemaWrapper;
use OCP\Migration\SimpleMigrationStep;
use OCP\Migration\IOutput; use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
class Version000000Date20181013124731 extends SimpleMigrationStep { class Version000000Date20181013124731 extends SimpleMigrationStep {

View File

@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
namespace OCA\RePod\Service; namespace OCA\RePod\Service;

View File

@ -1,16 +1,17 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
namespace OCA\RePod\Service; namespace OCA\RePod\Service;
use Exception; use Exception;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
use OCA\RePod\Db\Note; use OCA\RePod\Db\Note;
use OCA\RePod\Db\NoteMapper; use OCA\RePod\Db\NoteMapper;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
class NoteService { class NoteService {
private NoteMapper $mapper; private NoteMapper $mapper;
@ -42,9 +43,9 @@ class NoteService {
return $this->mapper->find($id, $userId); return $this->mapper->find($id, $userId);
// in order to be able to plug in different storage backends like files // in order to be able to plug in different storage backends like files
// for instance it is a good idea to turn storage related exceptions // for instance it is a good idea to turn storage related exceptions
// into service related exceptions so controllers and service users // into service related exceptions so controllers and service users
// have to deal with only one type of exception // have to deal with only one type of exception
} catch (Exception $e) { } catch (Exception $e) {
$this->handleException($e); $this->handleException($e);
} }

22023
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2,8 +2,6 @@
"name": "repod", "name": "repod",
"description": "🔊 Browse, manage and listen to podcasts", "description": "🔊 Browse, manage and listen to podcasts",
"version": "0.0.1", "version": "0.0.1",
"author": "Xéfir Destiny <xefir@crystalyx.net>",
"contributors": [],
"bugs": { "bugs": {
"url": "https://git.crystalyx.net/Xefir/RePod/issues" "url": "https://git.crystalyx.net/Xefir/RePod/issues"
}, },
@ -20,11 +18,11 @@
"stylelint:fix": "stylelint css/*.css css/*.scss src/**/*.scss src/**/*.vue --fix" "stylelint:fix": "stylelint css/*.css css/*.scss src/**/*.scss src/**/*.vue --fix"
}, },
"dependencies": { "dependencies": {
"@nextcloud/axios": "^1.10.0", "@nextcloud/axios": "2.3.0",
"@nextcloud/dialogs": "^3.1.4", "@nextcloud/dialogs": "4.0.1",
"@nextcloud/router": "^2.0.0", "@nextcloud/router": "2.1.2",
"@nextcloud/vue": "^5.4.0", "@nextcloud/vue": "7.0.1",
"vue": "^2.7.0" "vue": "2.7.14"
}, },
"browserslist": [ "browserslist": [
"extends @nextcloud/browserslist-config" "extends @nextcloud/browserslist-config"
@ -35,9 +33,9 @@
}, },
"devDependencies": { "devDependencies": {
"@nextcloud/babel-config": "^1.0.0", "@nextcloud/babel-config": "^1.0.0",
"@nextcloud/browserslist-config": "^2.2.0", "@nextcloud/browserslist-config": "^2.3.0",
"@nextcloud/eslint-config": "^8.0.0", "@nextcloud/eslint-config": "^8.2.1",
"@nextcloud/stylelint-config": "^2.1.2", "@nextcloud/stylelint-config": "^2.3.0",
"@nextcloud/webpack-vue-config": "^5.2.1" "@nextcloud/webpack-vue-config": "^5.5.1"
} }
} }

View File

@ -2,10 +2,11 @@
<psalm <psalm
errorLevel="4" errorLevel="4"
resolveFromConfigFile="true" resolveFromConfigFile="true"
findUnusedBaselineEntry="true"
findUnusedCode="false"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config" xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd" xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
errorBaseline="tests/psalm-baseline.xml"
> >
<projectFiles> <projectFiles>
<directory name="lib" /> <directory name="lib" />
@ -16,7 +17,6 @@
<extraFiles> <extraFiles>
<directory name="vendor" /> <directory name="vendor" />
<ignoreFiles> <ignoreFiles>
<directory name="vendor/phpunit/php-code-coverage" />
<directory name="vendor/psalm" /> <directory name="vendor/psalm" />
</ignoreFiles> </ignoreFiles>
</extraFiles> </extraFiles>

View File

@ -1,36 +1,36 @@
<template> <template>
<div id="content" class="app-repod"> <div id="content" class="app-repod">
<AppNavigation> <NcAppNavigation>
<AppNavigationNew v-if="!loading" <NcAppNavigationNew v-if="!loading"
:text="t('repod', 'New note')" :text="t('repod', 'New note')"
:disabled="false" :disabled="false"
button-id="new-repod-button" button-id="new-repod-button"
button-class="icon-add" button-class="icon-add"
@click="newNote" /> @click="newNote" />
<ul> <ul>
<AppNavigationItem v-for="note in notes" <NcAppNavigationItem v-for="note in notes"
:key="note.id" :key="note.id"
:title="note.title ? note.title : t('repod', 'New note')" :title="note.title ? note.title : t('repod', 'New note')"
:class="{active: currentNoteId === note.id}" :class="{active: currentNoteId === note.id}"
@click="openNote(note)"> @click="openNote(note)">
<template slot="actions"> <template slot="actions">
<ActionButton v-if="note.id === -1" <NcActionButton v-if="note.id === -1"
icon="icon-close" icon="icon-close"
@click="cancelNewNote(note)"> @click="cancelNewNote(note)">
{{ {{
t('repod', 'Cancel note creation') }} t('repod', 'Cancel note creation') }}
</ActionButton> </NcActionButton>
<ActionButton v-else <NcActionButton v-else
icon="icon-delete" icon="icon-delete"
@click="deleteNote(note)"> @click="deleteNote(note)">
{{ {{
t('repod', 'Delete note') }} t('repod', 'Delete note') }}
</ActionButton> </NcActionButton>
</template> </template>
</AppNavigationItem> </NcAppNavigationItem>
</ul> </ul>
</AppNavigation> </NcAppNavigation>
<AppContent> <NcAppContent>
<div v-if="currentNote"> <div v-if="currentNote">
<input ref="title" <input ref="title"
v-model="currentNote.title" v-model="currentNote.title"
@ -45,21 +45,19 @@
</div> </div>
<div v-else id="emptycontent"> <div v-else id="emptycontent">
<div class="icon-file" /> <div class="icon-file" />
<h2>{{ <h2>
t('repod', 'Create a note to get started') }}</h2> {{
t('repod', 'Create a note to get started') }}
</h2>
</div> </div>
</AppContent> </NcAppContent>
</div> </div>
</template> </template>
<script> <script>
import ActionButton from '@nextcloud/vue/dist/Components/ActionButton' import { NcActionButton, NcAppContent, NcAppNavigation, NcAppNavigationItem, NcAppNavigationNew } from '@nextcloud/vue'
import AppContent from '@nextcloud/vue/dist/Components/AppContent'
import AppNavigation from '@nextcloud/vue/dist/Components/AppNavigation'
import AppNavigationItem from '@nextcloud/vue/dist/Components/AppNavigationItem'
import AppNavigationNew from '@nextcloud/vue/dist/Components/AppNavigationNew'
import '@nextcloud/dialogs/styles/toast.scss' import '@nextcloud/dialogs/dist/index.css'
import { generateUrl } from '@nextcloud/router' import { generateUrl } from '@nextcloud/router'
import { showError, showSuccess } from '@nextcloud/dialogs' import { showError, showSuccess } from '@nextcloud/dialogs'
import axios from '@nextcloud/axios' import axios from '@nextcloud/axios'
@ -67,11 +65,11 @@ import axios from '@nextcloud/axios'
export default { export default {
name: 'App', name: 'App',
components: { components: {
ActionButton, NcActionButton,
AppContent, NcAppContent,
AppNavigation, NcAppNavigation,
AppNavigationItem, NcAppNavigationItem,
AppNavigationNew, NcAppNavigationNew,
}, },
data() { data() {
return { return {
@ -84,7 +82,8 @@ export default {
computed: { computed: {
/** /**
* Return the currently selected note object * Return the currently selected note object
* @returns {Object|null} *
* @return {object | null}
*/ */
currentNote() { currentNote() {
if (this.currentNoteId === null) { if (this.currentNoteId === null) {
@ -95,7 +94,8 @@ export default {
/** /**
* Returns true if a note is selected and its title is not empty * Returns true if a note is selected and its title is not empty
* @returns {Boolean} *
* @return {boolean}
*/ */
savePossible() { savePossible() {
return this.currentNote && this.currentNote.title !== '' return this.currentNote && this.currentNote.title !== ''
@ -118,7 +118,8 @@ export default {
methods: { methods: {
/** /**
* Create a new note and focus the note content field automatically * Create a new note and focus the note content field automatically
* @param {Object} note Note object *
* @param {object} note Note object
*/ */
openNote(note) { openNote(note) {
if (this.updating) { if (this.updating) {
@ -167,7 +168,8 @@ export default {
}, },
/** /**
* Create a new note by sending the information to the server * Create a new note by sending the information to the server
* @param {Object} note Note object *
* @param {object} note Note object
*/ */
async createNote(note) { async createNote(note) {
this.updating = true this.updating = true
@ -184,7 +186,8 @@ export default {
}, },
/** /**
* Update an existing note on the server * Update an existing note on the server
* @param {Object} note Note object *
* @param {object} note Note object
*/ */
async updateNote(note) { async updateNote(note) {
this.updating = true this.updating = true
@ -198,7 +201,8 @@ export default {
}, },
/** /**
* Delete a note, remove it from the frontend and show a hint * Delete a note, remove it from the frontend and show a hint
* @param {Object} note Note object *
* @param {object} note Note object
*/ */
async deleteNote(note) { async deleteNote(note) {
try { try {

View File

@ -1,7 +1,7 @@
import { generateFilePath } from '@nextcloud/router' import { generateFilePath } from '@nextcloud/router'
import Vue from 'vue' import Vue from 'vue'
import App from './App' import App from './App.vue'
// eslint-disable-next-line // eslint-disable-next-line
__webpack_public_path__ = generateFilePath(appName, '', 'js/') __webpack_public_path__ = generateFilePath(appName, '', 'js/')

View File

@ -1,61 +0,0 @@
<?php
declare(strict_types=1);
namespace OCA\RePod\Tests\Integration\Controller;
use OCP\AppFramework\App;
use OCP\IRequest;
use PHPUnit\Framework\TestCase;
use OCA\RePod\Db\Note;
use OCA\RePod\Db\NoteMapper;
use OCA\RePod\Controller\NoteController;
class NoteIntegrationTest extends TestCase {
private NoteController $controller;
private QBMapper $mapper;
private string $userId = 'john';
public function setUp(): void {
$app = new App('repod');
$container = $app->getContainer();
// only replace the user id
$container->registerService('userId', function () {
return $this->userId;
});
// we do not care about the request but the controller needs it
$container->registerService(IRequest::class, function () {
return $this->createMock(IRequest::class);
});
$this->controller = $container->get(NoteController::class);
$this->mapper = $container->get(NoteMapper::class);
}
public function testUpdate(): void {
// create a new note that should be updated
$note = new Note();
$note->setTitle('old_title');
$note->setContent('old_content');
$note->setUserId($this->userId);
$id = $this->mapper->insert($note)->getId();
// fromRow does not set the fields as updated
$updatedNote = Note::fromRow([
'id' => $id,
'user_id' => $this->userId
]);
$updatedNote->setContent('content');
$updatedNote->setTitle('title');
$result = $this->controller->update($id, 'title', 'content');
$this->assertEquals($updatedNote, $result->getData());
// clean up
$this->mapper->delete($result->getData());
}
}

View File

@ -1,13 +0,0 @@
<?php
declare(strict_types=1);
namespace OCA\RePod\Tests\Unit\Controller;
use OCA\RePod\Controller\NoteApiController;
class NoteApiControllerTest extends NoteControllerTest {
public function setUp(): void {
parent::setUp();
$this->controller = new NoteApiController($this->request, $this->service, $this->userId);
}
}

View File

@ -1,55 +0,0 @@
<?php
declare(strict_types=1);
namespace OCA\RePod\Tests\Unit\Controller;
use PHPUnit\Framework\TestCase;
use OCP\AppFramework\Http;
use OCP\IRequest;
use OCA\RePod\Service\NoteNotFound;
use OCA\RePod\Service\NoteService;
use OCA\RePod\Controller\NoteController;
class NoteControllerTest extends TestCase {
protected NoteController $controller;
protected string $userId = 'john';
protected $service;
protected $request;
public function setUp(): void {
$this->request = $this->getMockBuilder(IRequest::class)->getMock();
$this->service = $this->getMockBuilder(NoteService::class)
->disableOriginalConstructor()
->getMock();
$this->controller = new NoteController($this->request, $this->service, $this->userId);
}
public function testUpdate(): void {
$note = 'just check if this value is returned correctly';
$this->service->expects($this->once())
->method('update')
->with($this->equalTo(3),
$this->equalTo('title'),
$this->equalTo('content'),
$this->equalTo($this->userId))
->will($this->returnValue($note));
$result = $this->controller->update(3, 'title', 'content');
$this->assertEquals($note, $result->getData());
}
public function testUpdateNotFound(): void {
// test the correct status code if no note is found
$this->service->expects($this->once())
->method('update')
->will($this->throwException(new NoteNotFound()));
$result = $this->controller->update(3, 'title', 'content');
$this->assertEquals(Http::STATUS_NOT_FOUND, $result->getStatus());
}
}

View File

@ -1,25 +0,0 @@
<?php
declare(strict_types=1);
namespace OCA\RePod\Tests\Unit\Controller;
use PHPUnit\Framework\TestCase;
use OCP\AppFramework\Http\TemplateResponse;
class PageControllerTest extends TestCase {
private PageController $controller;
public function setUp(): void {
$request = $this->getMockBuilder(\OCP\IRequest::class)->getMock();
$this->controller = new PageController($request);
}
public function testIndex(): void {
$result = $this->controller->index();
$this->assertEquals('main', $result->getTemplateName());
$this->assertTrue($result instanceof TemplateResponse);
}
}

View File

@ -1,63 +0,0 @@
<?php
declare(strict_types=1);
namespace OCA\RePod\Tests\Unit\Service;
use OCA\RePod\Service\NoteNotFound;
use PHPUnit\Framework\TestCase;
use OCP\AppFramework\Db\DoesNotExistException;
use OCA\RePod\Db\Note;
use OCA\RePod\Service\NoteService;
use OCA\RePod\Db\NoteMapper;
class NoteServiceTest extends TestCase {
private NoteService $service;
private string $userId = 'john';
private $mapper;
public function setUp(): void {
$this->mapper = $this->getMockBuilder(NoteMapper::class)
->disableOriginalConstructor()
->getMock();
$this->service = new NoteService($this->mapper);
}
public function testUpdate(): void {
// the existing note
$note = Note::fromRow([
'id' => 3,
'title' => 'yo',
'content' => 'nope'
]);
$this->mapper->expects($this->once())
->method('find')
->with($this->equalTo(3))
->will($this->returnValue($note));
// the note when updated
$updatedNote = Note::fromRow(['id' => 3]);
$updatedNote->setTitle('title');
$updatedNote->setContent('content');
$this->mapper->expects($this->once())
->method('update')
->with($this->equalTo($updatedNote))
->will($this->returnValue($updatedNote));
$result = $this->service->update(3, 'title', 'content', $this->userId);
$this->assertEquals($updatedNote, $result);
}
public function testUpdateNotFound(): void {
$this->expectException(NoteNotFound::class);
// test the correct status code if no note is found
$this->mapper->expects($this->once())
->method('find')
->with($this->equalTo(3))
->will($this->throwException(new DoesNotExistException('')));
$this->service->update(3, 'title', 'content', $this->userId);
}
}

View File

@ -1,4 +0,0 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../../tests/bootstrap.php';

View File

@ -1,7 +0,0 @@
<phpunit bootstrap="bootstrap.php" colors="true">
<testsuites>
<testsuite name="integration">
<directory>./Integration</directory>
</testsuite>
</testsuites>
</phpunit>

View File

@ -1,7 +0,0 @@
<phpunit bootstrap="bootstrap.php" colors="true">
<testsuites>
<testsuite name="unit">
<directory>./Unit</directory>
</testsuite>
</testsuites>
</phpunit>

View File

@ -1,2 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="4.x-dev@">