Initial commit
This commit is contained in:
7
.eslintrc.js
Normal file
7
.eslintrc.js
Normal file
@@ -0,0 +1,7 @@
|
||||
// SPDX-FileCopyrightText: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
module.exports = {
|
||||
extends: [
|
||||
'@nextcloud',
|
||||
]
|
||||
}
|
||||
3
.gitattributes
vendored
Normal file
3
.gitattributes
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# SPDX-FileCopyrightText: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
/js/* binary
|
||||
47
.github/workflows/lint-eslint.yml
vendored
Normal file
47
.github/workflows/lint-eslint.yml
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
# This workflow is provided via the organization template repository
|
||||
#
|
||||
# https://github.com/nextcloud/.github
|
||||
# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization
|
||||
# SPDX-FileCopyrightText: Nextcloud contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
name: Lint
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
- stable*
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
name: eslint
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Read package.json node and npm engines version
|
||||
uses: skjnldsv/read-package-engines-version-actions@v1.2
|
||||
id: versions
|
||||
with:
|
||||
fallbackNode: '^16'
|
||||
fallbackNpm: '^7'
|
||||
|
||||
- name: Set up node ${{ steps.versions.outputs.nodeVersion }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ steps.versions.outputs.nodeVersion }}
|
||||
|
||||
- name: Set up npm ${{ steps.versions.outputs.npmVersion }}
|
||||
run: npm i -g npm@"${{ steps.versions.outputs.npmVersion }}"
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Lint
|
||||
run: npm run lint
|
||||
33
.github/workflows/lint-info-xml.yml
vendored
Normal file
33
.github/workflows/lint-info-xml.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
# This workflow is provided via the organization template repository
|
||||
#
|
||||
# https://github.com/nextcloud/.github
|
||||
# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization
|
||||
# SPDX-FileCopyrightText: Nextcloud contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
name: Lint
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- stable*
|
||||
|
||||
jobs:
|
||||
xml-linters:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
name: info.xml lint
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Download schema
|
||||
run: wget https://raw.githubusercontent.com/nextcloud/appstore/master/nextcloudappstore/api/v1/release/info.xsd
|
||||
|
||||
- name: Lint info.xml
|
||||
uses: ChristophWurst/xmllint-action@v1
|
||||
with:
|
||||
xml-file: ./appinfo/info.xml
|
||||
xml-schema-file: ./info.xsd
|
||||
37
.github/workflows/lint-php-cs.yml
vendored
Normal file
37
.github/workflows/lint-php-cs.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
# This workflow is provided via the organization template repository
|
||||
#
|
||||
# https://github.com/nextcloud/.github
|
||||
# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization
|
||||
# SPDX-FileCopyrightText: Nextcloud contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
name: Lint
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- stable*
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
name: php-cs
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up php ${{ matrix.php-versions }}
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: "7.4"
|
||||
coverage: none
|
||||
|
||||
- name: Install dependencies
|
||||
run: composer i
|
||||
|
||||
- name: Lint
|
||||
run: composer run cs:check || ( echo 'Please run `composer run cs:fix` to format your code' && exit 1 )
|
||||
49
.github/workflows/lint-php.yml
vendored
Normal file
49
.github/workflows/lint-php.yml
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
# This workflow is provided via the organization template repository
|
||||
#
|
||||
# https://github.com/nextcloud/.github
|
||||
# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization
|
||||
# SPDX-FileCopyrightText: Nextcloud contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
name: Lint
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- stable*
|
||||
|
||||
jobs:
|
||||
php-lint:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
php-versions: ["7.4", "8.0", "8.1"]
|
||||
|
||||
name: php-lint
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up php ${{ matrix.php-versions }}
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
coverage: none
|
||||
|
||||
- name: Lint
|
||||
run: composer run lint
|
||||
|
||||
summary:
|
||||
runs-on: ubuntu-latest
|
||||
needs: php-lint
|
||||
|
||||
if: always()
|
||||
|
||||
name: php-lint-summary
|
||||
|
||||
steps:
|
||||
- name: Summary status
|
||||
run: if ${{ needs.php-lint.result != 'success' && needs.php-lint.result != 'skipped' }}; then exit 1; fi
|
||||
46
.github/workflows/lint-stylelint.yml
vendored
Normal file
46
.github/workflows/lint-stylelint.yml
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
# This workflow is provided via the organization template repository
|
||||
#
|
||||
# https://github.com/nextcloud/.github
|
||||
# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization
|
||||
# SPDX-FileCopyrightText: Nextcloud contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
name: Lint
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- stable*
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
name: stylelint
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Read package.json node and npm engines version
|
||||
uses: skjnldsv/read-package-engines-version-actions@v1.1
|
||||
id: versions
|
||||
with:
|
||||
fallbackNode: '^16'
|
||||
fallbackNpm: '^7'
|
||||
|
||||
- name: Set up node ${{ steps.versions.outputs.nodeVersion }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ steps.versions.outputs.nodeVersion }}
|
||||
|
||||
- name: Set up npm ${{ steps.versions.outputs.npmVersion }}
|
||||
run: npm i -g npm@"${{ steps.versions.outputs.npmVersion }}"
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Lint
|
||||
run: npm run stylelint
|
||||
115
.github/workflows/phpunit-mysql.yml
vendored
Normal file
115
.github/workflows/phpunit-mysql.yml
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
# SPDX-FileCopyrightText: Nextcloud contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
name: PHPUnit
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- stable*
|
||||
|
||||
env:
|
||||
# Location of the phpunit.xml and phpunit.integration.xml files
|
||||
PHPUNIT_CONFIG: ./tests/phpunit.xml
|
||||
PHPUNIT_INTEGRATION_CONFIG: ./tests/phpunit.integration.xml
|
||||
|
||||
jobs:
|
||||
phpunit-mysql:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-versions: ['7.4', '8.0', '8.1']
|
||||
server-versions: ['master']
|
||||
|
||||
services:
|
||||
mysql:
|
||||
image: mariadb:10.5
|
||||
ports:
|
||||
- 4444:3306/tcp
|
||||
env:
|
||||
MYSQL_ROOT_PASSWORD: rootpassword
|
||||
options: --health-cmd="mysqladmin ping" --health-interval 5s --health-timeout 2s --health-retries 5
|
||||
|
||||
steps:
|
||||
- name: Set app env
|
||||
run: |
|
||||
# Split and keep last
|
||||
echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV
|
||||
|
||||
- name: Enable ONLY_FULL_GROUP_BY MySQL option
|
||||
run: |
|
||||
echo "SET GLOBAL sql_mode=(SELECT CONCAT(@@sql_mode,',ONLY_FULL_GROUP_BY'));" | mysql -h 127.0.0.1 -P 4444 -u root -prootpassword
|
||||
echo "SELECT @@sql_mode;" | mysql -h 127.0.0.1 -P 4444 -u root -prootpassword
|
||||
|
||||
- name: Checkout server
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
repository: nextcloud/server
|
||||
ref: ${{ matrix.server-versions }}
|
||||
|
||||
- name: Checkout app
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: apps/${{ env.APP_NAME }}
|
||||
|
||||
- name: Set up php ${{ matrix.php-versions }}
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
tools: phpunit
|
||||
extensions: mbstring, iconv, fileinfo, intl, mysql, pdo_mysql
|
||||
coverage: none
|
||||
|
||||
- name: Set up PHPUnit
|
||||
working-directory: apps/${{ env.APP_NAME }}
|
||||
run: composer i
|
||||
|
||||
- name: Set up Nextcloud
|
||||
env:
|
||||
DB_PORT: 4444
|
||||
run: |
|
||||
mkdir data
|
||||
./occ maintenance:install --verbose --database=mysql --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass password
|
||||
./occ app:enable ${{ env.APP_NAME }}
|
||||
|
||||
- name: Check PHPUnit config file existence
|
||||
id: check_phpunit
|
||||
uses: andstor/file-existence-action@v1
|
||||
with:
|
||||
files: apps/${{ env.APP_NAME }}/${{ env.PHPUNIT_CONFIG }}
|
||||
|
||||
- name: Run Nextcloud
|
||||
run: php -S localhost:8080 &
|
||||
|
||||
- name: PHPUnit
|
||||
# Only run if phpunit config file exists
|
||||
if: steps.check_phpunit.outputs.files_exists == 'true'
|
||||
working-directory: apps/${{ env.APP_NAME }}
|
||||
run: ./vendor/phpunit/phpunit/phpunit -c ${{ env.PHPUNIT_CONFIG }}
|
||||
|
||||
- name: Check PHPUnit integration config file existence
|
||||
id: check_integration
|
||||
uses: andstor/file-existence-action@v1
|
||||
with:
|
||||
files: apps/${{ env.APP_NAME }}/${{ env.PHPUNIT_INTEGRATION_CONFIG }}
|
||||
|
||||
- name: PHPUnit integration
|
||||
# Only run if phpunit integration config file exists
|
||||
if: steps.check_integration.outputs.files_exists == 'true'
|
||||
working-directory: apps/${{ env.APP_NAME }}
|
||||
run: ./vendor/phpunit/phpunit/phpunit -c ${{ env.PHPUNIT_INTEGRATION_CONFIG }}
|
||||
|
||||
summary:
|
||||
runs-on: ubuntu-latest
|
||||
needs: phpunit-mysql
|
||||
|
||||
if: always()
|
||||
|
||||
name: phpunit-mysql-summary
|
||||
|
||||
steps:
|
||||
- name: Summary status
|
||||
run: if ${{ needs.phpunit-mysql.result != 'success' }}; then exit 1; fi
|
||||
109
.github/workflows/phpunit-oci.yml
vendored
Normal file
109
.github/workflows/phpunit-oci.yml
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
# SPDX-FileCopyrightText: Nextcloud contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
name: PHPUnit
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- stable*
|
||||
|
||||
env:
|
||||
# Location of the phpunit.xml and phpunit.integration.xml files
|
||||
PHPUNIT_CONFIG: ./tests/phpunit.xml
|
||||
PHPUNIT_INTEGRATION_CONFIG: ./tests/phpunit.integration.xml
|
||||
|
||||
jobs:
|
||||
phpunit-oci:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-versions: ['8.0']
|
||||
server-versions: ['master']
|
||||
|
||||
services:
|
||||
oracle:
|
||||
image: deepdiver/docker-oracle-xe-11g # 'wnameless/oracle-xe-11g-r2'
|
||||
ports:
|
||||
- 1521:1521/tcp
|
||||
|
||||
steps:
|
||||
- name: Set app env
|
||||
run: |
|
||||
# Split and keep last
|
||||
echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV
|
||||
|
||||
- name: Checkout server
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
repository: nextcloud/server
|
||||
ref: ${{ matrix.server-versions }}
|
||||
|
||||
- name: Checkout app
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: apps/${{ env.APP_NAME }}
|
||||
|
||||
- name: Set up php ${{ matrix.php-versions }}
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
extensions: mbstring, fileinfo, intl, sqlite, pdo_sqlite, oci8
|
||||
tools: phpunit
|
||||
coverage: none
|
||||
|
||||
- name: Set up PHPUnit
|
||||
working-directory: apps/${{ env.APP_NAME }}
|
||||
run: composer i
|
||||
|
||||
- name: Set up Nextcloud
|
||||
env:
|
||||
DB_PORT: 1521
|
||||
run: |
|
||||
mkdir data
|
||||
./occ maintenance:install --verbose --database=oci --database-name=XE --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=autotest --database-pass=owncloud --admin-user admin --admin-pass admin
|
||||
./occ app:enable ${{ env.APP_NAME }}
|
||||
|
||||
- name: Check PHPUnit config file existence
|
||||
id: check_phpunit
|
||||
uses: andstor/file-existence-action@v1
|
||||
with:
|
||||
files: apps/${{ env.APP_NAME }}/${{ env.PHPUNIT_CONFIG }}
|
||||
|
||||
- name: PHPUnit
|
||||
# Only run if phpunit config file exists
|
||||
if: steps.check_phpunit.outputs.files_exists == 'true'
|
||||
working-directory: apps/${{ env.APP_NAME }}
|
||||
run: ./vendor/phpunit/phpunit/phpunit -c ${{ env.PHPUNIT_CONFIG }}
|
||||
|
||||
- name: Check PHPUnit integration config file existence
|
||||
id: check_integration
|
||||
uses: andstor/file-existence-action@v1
|
||||
with:
|
||||
files: apps/${{ env.APP_NAME }}/${{ env.PHPUNIT_INTEGRATION_CONFIG }}
|
||||
|
||||
- name: Run Nextcloud
|
||||
# Only run if phpunit integration config file exists
|
||||
if: steps.check_integration.outputs.files_exists == 'true'
|
||||
run: php -S localhost:8080 &
|
||||
|
||||
- name: PHPUnit integration
|
||||
# Only run if phpunit integration config file exists
|
||||
if: steps.check_integration.outputs.files_exists == 'true'
|
||||
working-directory: apps/${{ env.APP_NAME }}
|
||||
run: ./vendor/phpunit/phpunit/phpunit -c ${{ env.PHPUNIT_INTEGRATION_CONFIG }}
|
||||
|
||||
summary:
|
||||
runs-on: ubuntu-latest
|
||||
needs: phpunit-oci
|
||||
|
||||
if: always()
|
||||
|
||||
name: phpunit-oci-summary
|
||||
|
||||
steps:
|
||||
- name: Summary status
|
||||
run: if ${{ needs.phpunit-oci.result != 'success' }}; then exit 1; fi
|
||||
114
.github/workflows/phpunit-pgsql.yml
vendored
Normal file
114
.github/workflows/phpunit-pgsql.yml
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
# SPDX-FileCopyrightText: Nextcloud contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
name: PHPUnit
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- stable*
|
||||
|
||||
env:
|
||||
# Location of the phpunit.xml and phpunit.integration.xml files
|
||||
PHPUNIT_CONFIG: ./tests/phpunit.xml
|
||||
PHPUNIT_INTEGRATION_CONFIG: ./tests/phpunit.integration.xml
|
||||
|
||||
jobs:
|
||||
phpunit-pgsql:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-versions: ['8.0']
|
||||
server-versions: ['master']
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres
|
||||
ports:
|
||||
- 4444:5432/tcp
|
||||
env:
|
||||
POSTGRES_USER: root
|
||||
POSTGRES_PASSWORD: rootpassword
|
||||
POSTGRES_DB: nextcloud
|
||||
options: --health-cmd pg_isready --health-interval 5s --health-timeout 2s --health-retries 5
|
||||
|
||||
steps:
|
||||
- name: Set app env
|
||||
run: |
|
||||
# Split and keep last
|
||||
echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV
|
||||
|
||||
- name: Checkout server
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
repository: nextcloud/server
|
||||
ref: ${{ matrix.server-versions }}
|
||||
|
||||
- name: Checkout app
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: apps/${{ env.APP_NAME }}
|
||||
|
||||
- name: Set up php ${{ matrix.php-versions }}
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
tools: phpunit
|
||||
extensions: mbstring, iconv, fileinfo, intl, pgsql, pdo_pgsql
|
||||
coverage: none
|
||||
|
||||
- name: Set up PHPUnit
|
||||
working-directory: apps/${{ env.APP_NAME }}
|
||||
run: composer i
|
||||
|
||||
- name: Set up Nextcloud
|
||||
env:
|
||||
DB_PORT: 4444
|
||||
run: |
|
||||
mkdir data
|
||||
./occ maintenance:install --verbose --database=pgsql --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass password
|
||||
./occ app:enable ${{ env.APP_NAME }}
|
||||
|
||||
- name: Check PHPUnit config file existence
|
||||
id: check_phpunit
|
||||
uses: andstor/file-existence-action@v1
|
||||
with:
|
||||
files: apps/${{ env.APP_NAME }}/${{ env.PHPUNIT_CONFIG }}
|
||||
|
||||
- name: PHPUnit
|
||||
# Only run if phpunit config file exists
|
||||
if: steps.check_phpunit.outputs.files_exists == 'true'
|
||||
working-directory: apps/${{ env.APP_NAME }}
|
||||
run: ./vendor/phpunit/phpunit/phpunit -c ${{ env.PHPUNIT_CONFIG }}
|
||||
|
||||
- name: Check PHPUnit integration config file existence
|
||||
id: check_integration
|
||||
uses: andstor/file-existence-action@v1
|
||||
with:
|
||||
files: apps/${{ env.APP_NAME }}/${{ env.PHPUNIT_INTEGRATION_CONFIG }}
|
||||
|
||||
- name: Run Nextcloud
|
||||
# Only run if phpunit integration config file exists
|
||||
if: steps.check_integration.outputs.files_exists == 'true'
|
||||
run: php -S localhost:8080 &
|
||||
|
||||
- name: PHPUnit integration
|
||||
# Only run if phpunit integration config file exists
|
||||
if: steps.check_integration.outputs.files_exists == 'true'
|
||||
working-directory: apps/${{ env.APP_NAME }}
|
||||
run: ./vendor/phpunit/phpunit/phpunit -c ${{ env.PHPUNIT_INTEGRATION_CONFIG }}
|
||||
|
||||
summary:
|
||||
runs-on: ubuntu-latest
|
||||
needs: phpunit-pgsql
|
||||
|
||||
if: always()
|
||||
|
||||
name: phpunit-pgsql-summary
|
||||
|
||||
steps:
|
||||
- name: Summary status
|
||||
run: if ${{ needs.phpunit-pgsql.result != 'success' }}; then exit 1; fi
|
||||
103
.github/workflows/phpunit-sqlite.yml
vendored
Normal file
103
.github/workflows/phpunit-sqlite.yml
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
# SPDX-FileCopyrightText: Nextcloud contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
name: PHPUnit
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- stable*
|
||||
|
||||
env:
|
||||
# Location of the phpunit.xml and phpunit.integration.xml files
|
||||
PHPUNIT_CONFIG: ./tests/phpunit.xml
|
||||
PHPUNIT_INTEGRATION_CONFIG: ./tests/phpunit.integration.xml
|
||||
|
||||
jobs:
|
||||
phpunit-sqlite:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-versions: ['8.0']
|
||||
server-versions: ['master']
|
||||
|
||||
steps:
|
||||
- name: Set app env
|
||||
run: |
|
||||
# Split and keep last
|
||||
echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV
|
||||
|
||||
- name: Checkout server
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
repository: nextcloud/server
|
||||
ref: ${{ matrix.server-versions }}
|
||||
|
||||
- name: Checkout app
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: apps/${{ env.APP_NAME }}
|
||||
|
||||
- name: Set up php ${{ matrix.php-versions }}
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
tools: phpunit
|
||||
extensions: mbstring, iconv, fileinfo, intl, sqlite, pdo_sqlite
|
||||
coverage: none
|
||||
|
||||
- name: Set up PHPUnit
|
||||
working-directory: apps/${{ env.APP_NAME }}
|
||||
run: composer i
|
||||
|
||||
- name: Set up Nextcloud
|
||||
env:
|
||||
DB_PORT: 4444
|
||||
run: |
|
||||
mkdir data
|
||||
./occ maintenance:install --verbose --database=sqlite --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass password
|
||||
./occ app:enable ${{ env.APP_NAME }}
|
||||
|
||||
- name: Check PHPUnit config file existence
|
||||
id: check_phpunit
|
||||
uses: andstor/file-existence-action@v1
|
||||
with:
|
||||
files: apps/${{ env.APP_NAME }}/${{ env.PHPUNIT_CONFIG }}
|
||||
|
||||
- name: PHPUnit
|
||||
# Only run if phpunit config file exists
|
||||
if: steps.check_phpunit.outputs.files_exists == 'true'
|
||||
working-directory: apps/${{ env.APP_NAME }}
|
||||
run: ./vendor/phpunit/phpunit/phpunit -c ${{ env.PHPUNIT_CONFIG }}
|
||||
|
||||
- name: Check PHPUnit integration config file existence
|
||||
id: check_integration
|
||||
uses: andstor/file-existence-action@v1
|
||||
with:
|
||||
files: apps/${{ env.APP_NAME }}/${{ env.PHPUNIT_INTEGRATION_CONFIG }}
|
||||
|
||||
- name: Run Nextcloud
|
||||
# Only run if phpunit integration config file exists
|
||||
if: steps.check_integration.outputs.files_exists == 'true'
|
||||
run: php -S localhost:8080 &
|
||||
|
||||
- name: PHPUnit integration
|
||||
# Only run if phpunit integration config file exists
|
||||
if: steps.check_integration.outputs.files_exists == 'true'
|
||||
working-directory: apps/${{ env.APP_NAME }}
|
||||
run: ./vendor/phpunit/phpunit/phpunit -c ${{ env.PHPUNIT_INTEGRATION_CONFIG }}
|
||||
|
||||
summary:
|
||||
runs-on: ubuntu-latest
|
||||
needs: phpunit-sqlite
|
||||
|
||||
if: always()
|
||||
|
||||
name: phpunit-sqlite-summary
|
||||
|
||||
steps:
|
||||
- name: Summary status
|
||||
run: if ${{ needs.phpunit-sqlite.result != 'success' }}; then exit 1; fi
|
||||
15
.github/workflows/reuse.yml
vendored
Normal file
15
.github/workflows/reuse.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# SPDX-FileCopyrightText: 2022 Free Software Foundation Europe e.V. <https://fsfe.org>
|
||||
#
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
name: REUSE Compliance Check
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: REUSE Compliance Check
|
||||
uses: fsfe/reuse-action@v1
|
||||
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# SPDX-FileCopyrightText: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*.iml
|
||||
.idea
|
||||
/.php-cs-fixer.cache
|
||||
/.php_cs.cache
|
||||
/build/
|
||||
/vendor/
|
||||
js/*hot-update.*
|
||||
node_modules/
|
||||
20
.php-cs-fixer.dist.php
Normal file
20
.php-cs-fixer.dist.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
// SPDX-FileCopyrightText: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
require_once './vendor/autoload.php';
|
||||
|
||||
use Nextcloud\CodingStandard\Config;
|
||||
|
||||
$config = new Config();
|
||||
$config
|
||||
->getFinder()
|
||||
->ignoreVCSIgnored(true)
|
||||
->notPath('build')
|
||||
->notPath('l10n')
|
||||
->notPath('src')
|
||||
->notPath('vendor')
|
||||
->in(__DIR__);
|
||||
return $config;
|
||||
12
.reuse/dep5
Normal file
12
.reuse/dep5
Normal file
@@ -0,0 +1,12 @@
|
||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: Photo Reduce
|
||||
Upstream-Contact: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
Source: https://github.com/nextcloud/profiler
|
||||
|
||||
Files: package-lock.json package.json composer.json composer.lock
|
||||
Copyright: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
License: AGPL-3.0-or-later
|
||||
|
||||
Files: l10n/*.js l10n/*.json
|
||||
Copyright: Nextcloud translators <https://www.transifex.com/nextcloud/>
|
||||
License: AGPL-3.0-or-later
|
||||
@@ -219,8 +219,8 @@ If you develop a new program, and you want it to be of the greatest possible use
|
||||
|
||||
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
ncc_photo_reduce
|
||||
Copyright (C) 2023 YFinGames
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
|
||||
121
LICENSES/CC0-1.0.txt
Normal file
121
LICENSES/CC0-1.0.txt
Normal file
@@ -0,0 +1,121 @@
|
||||
Creative Commons Legal Code
|
||||
|
||||
CC0 1.0 Universal
|
||||
|
||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||
HEREUNDER.
|
||||
|
||||
Statement of Purpose
|
||||
|
||||
The laws of most jurisdictions throughout the world automatically confer
|
||||
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||
authorship and/or a database (each, a "Work").
|
||||
|
||||
Certain owners wish to permanently relinquish those rights to a Work for
|
||||
the purpose of contributing to a commons of creative, cultural and
|
||||
scientific works ("Commons") that the public can reliably and without fear
|
||||
of later claims of infringement build upon, modify, incorporate in other
|
||||
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||
and for any purposes, including without limitation commercial purposes.
|
||||
These owners may contribute to the Commons to promote the ideal of a free
|
||||
culture and the further production of creative, cultural and scientific
|
||||
works, or to gain reputation or greater distribution for their Work in
|
||||
part through the use and efforts of others.
|
||||
|
||||
For these and/or other purposes and motivations, and without any
|
||||
expectation of additional consideration or compensation, the person
|
||||
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||
|
||||
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||
protected by copyright and related or neighboring rights ("Copyright and
|
||||
Related Rights"). Copyright and Related Rights include, but are not
|
||||
limited to, the following:
|
||||
|
||||
i. the right to reproduce, adapt, distribute, perform, display,
|
||||
communicate, and translate a Work;
|
||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||
iii. publicity and privacy rights pertaining to a person's image or
|
||||
likeness depicted in a Work;
|
||||
iv. rights protecting against unfair competition in regards to a Work,
|
||||
subject to the limitations in paragraph 4(a), below;
|
||||
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||
in a Work;
|
||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||
European Parliament and of the Council of 11 March 1996 on the legal
|
||||
protection of databases, and under any national implementation
|
||||
thereof, including any amended or successor version of such
|
||||
directive); and
|
||||
vii. other similar, equivalent or corresponding rights throughout the
|
||||
world based on applicable law or treaty, and any national
|
||||
implementations thereof.
|
||||
|
||||
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||
of action, whether now known or unknown (including existing as well as
|
||||
future claims and causes of action), in the Work (i) in all territories
|
||||
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||
treaty (including future time extensions), (iii) in any current or future
|
||||
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||
including without limitation commercial, advertising or promotional
|
||||
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||
member of the public at large and to the detriment of Affirmer's heirs and
|
||||
successors, fully intending that such Waiver shall not be subject to
|
||||
revocation, rescission, cancellation, termination, or any other legal or
|
||||
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||
as contemplated by Affirmer's express Statement of Purpose.
|
||||
|
||||
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||
be judged legally invalid or ineffective under applicable law, then the
|
||||
Waiver shall be preserved to the maximum extent permitted taking into
|
||||
account Affirmer's express Statement of Purpose. In addition, to the
|
||||
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||
maximum duration provided by applicable law or treaty (including future
|
||||
time extensions), (iii) in any current or future medium and for any number
|
||||
of copies, and (iv) for any purpose whatsoever, including without
|
||||
limitation commercial, advertising or promotional purposes (the
|
||||
"License"). The License shall be deemed effective as of the date CC0 was
|
||||
applied by Affirmer to the Work. Should any part of the License for any
|
||||
reason be judged legally invalid or ineffective under applicable law, such
|
||||
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||
of the License, and in such case Affirmer hereby affirms that he or she
|
||||
will not (i) exercise any of his or her remaining Copyright and Related
|
||||
Rights in the Work or (ii) assert any associated claims and causes of
|
||||
action with respect to the Work, in either case contrary to Affirmer's
|
||||
express Statement of Purpose.
|
||||
|
||||
4. Limitations and Disclaimers.
|
||||
|
||||
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||
surrendered, licensed or otherwise affected by this document.
|
||||
b. Affirmer offers the Work as-is and makes no representations or
|
||||
warranties of any kind concerning the Work, express, implied,
|
||||
statutory or otherwise, including without limitation warranties of
|
||||
title, merchantability, fitness for a particular purpose, non
|
||||
infringement, or the absence of latent or other defects, accuracy, or
|
||||
the present or absence of errors, whether or not discoverable, all to
|
||||
the greatest extent permissible under applicable law.
|
||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||
that may apply to the Work or any use thereof, including without
|
||||
limitation any person's Copyright and Related Rights in the Work.
|
||||
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||
consents, permissions or other rights required for any use of the
|
||||
Work.
|
||||
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||
party to this document and has no duty or obligation with respect to
|
||||
this CC0 or use of the Work.
|
||||
163
Makefile
Normal file
163
Makefile
Normal file
@@ -0,0 +1,163 @@
|
||||
# SPDX-FileCopyrightText: Bernhard Posselt <dev@bernhard-posselt.com>
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
# 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))
|
||||
build_tools_directory=$(CURDIR)/build/tools
|
||||
source_build_directory=$(CURDIR)/build/artifacts/source
|
||||
source_package_name=$(source_build_directory)/$(app_name)
|
||||
appstore_build_directory=$(CURDIR)/build/artifacts
|
||||
appstore_package_name=$(appstore_build_directory)/$(app_name)
|
||||
npm=$(shell which npm 2> /dev/null)
|
||||
composer=$(shell which composer 2> /dev/null)
|
||||
|
||||
all: build
|
||||
|
||||
# Fetches the PHP and JS dependencies and compiles the JS. If no composer.json
|
||||
# is present, the composer step is skipped, if no package.json or js/package.json
|
||||
# is present, the npm step is skipped
|
||||
.PHONY: build
|
||||
build:
|
||||
ifneq (,$(wildcard $(CURDIR)/composer.json))
|
||||
make composer
|
||||
endif
|
||||
ifneq (,$(wildcard $(CURDIR)/package.json))
|
||||
make npm
|
||||
endif
|
||||
ifneq (,$(wildcard $(CURDIR)/js/package.json))
|
||||
make npm
|
||||
endif
|
||||
|
||||
# Installs and updates the composer dependencies. If composer is not installed
|
||||
# a copy is fetched from the web
|
||||
.PHONY: composer
|
||||
composer:
|
||||
ifeq (, $(composer))
|
||||
@echo "No composer command available, downloading a copy from the web"
|
||||
mkdir -p $(build_tools_directory)
|
||||
curl -sS https://getcomposer.org/installer | php
|
||||
mv composer.phar $(build_tools_directory)
|
||||
php $(build_tools_directory)/composer.phar install --prefer-dist
|
||||
else
|
||||
composer install --prefer-dist
|
||||
endif
|
||||
|
||||
# Installs npm dependencies
|
||||
.PHONY: npm
|
||||
npm:
|
||||
ifeq (,$(wildcard $(CURDIR)/package.json))
|
||||
cd js && $(npm) run build
|
||||
else
|
||||
npm run build
|
||||
endif
|
||||
|
||||
# Removes the appstore build
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf ./build
|
||||
|
||||
# Same as clean but also removes dependencies installed by composer, bower and
|
||||
# npm
|
||||
.PHONY: distclean
|
||||
distclean: clean
|
||||
rm -rf vendor
|
||||
rm -rf node_modules
|
||||
rm -rf js/vendor
|
||||
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 \
|
||||
--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" \
|
||||
../$(app_name) \
|
||||
|
||||
# Builds the source package for the app store, ignores php tests, js tests
|
||||
# and build related folders that are unnecessary for an appstore release
|
||||
.PHONY: appstore
|
||||
appstore:
|
||||
rm -rf $(appstore_build_directory)
|
||||
mkdir -p $(appstore_build_directory)
|
||||
tar cvzf $(appstore_package_name).tar.gz \
|
||||
--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)/node_modules" \
|
||||
--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/.*" \
|
||||
--exclude="../$(app_name)/webpack.config.js" \
|
||||
--exclude="../$(app_name)/stylelint.config.js" \
|
||||
--exclude="../$(app_name)/CHANGELOG.md" \
|
||||
--exclude="../$(app_name)/README.md" \
|
||||
--exclude="../$(app_name)/package-lock.json" \
|
||||
--exclude="../$(app_name)/LICENSES" \
|
||||
../$(app_name) \
|
||||
|
||||
.PHONY: test
|
||||
test: composer
|
||||
$(CURDIR)/vendor/phpunit/phpunit/phpunit -c phpunit.xml
|
||||
$(CURDIR)/vendor/phpunit/phpunit/phpunit -c phpunit.integration.xml
|
||||
57
README.md
57
README.md
@@ -1,2 +1,57 @@
|
||||
# ncc_photo_reduce
|
||||
<!--
|
||||
SPDX-FileCopyrightText: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
SPDX-License-Identifier: CC0-1.0
|
||||
-->
|
||||
|
||||
# Photo Reduce
|
||||
Place this app in **nextcloud/apps/**
|
||||
|
||||
## Building the app
|
||||
|
||||
The app can be built by using the provided Makefile by running:
|
||||
|
||||
make
|
||||
|
||||
This requires the following things to be present:
|
||||
* make
|
||||
* which
|
||||
* tar: for building the archive
|
||||
* curl: used if phpunit and composer are not installed to fetch them from the web
|
||||
* npm: for building and testing everything JS, only required if a package.json is placed inside the **js/** folder
|
||||
|
||||
The make command will install or update Composer dependencies if a composer.json is present and also **npm run build** if a package.json is present in the **js/** folder. The npm **build** script should use local paths for build systems and package managers, so people that simply want to build the app won't need to install npm libraries globally, e.g.:
|
||||
|
||||
**package.json**:
|
||||
```json
|
||||
"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"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Publish to App Store
|
||||
|
||||
First get an account for the [App Store](http://apps.nextcloud.com/) then run:
|
||||
|
||||
make && make appstore
|
||||
|
||||
The archive is located in build/artifacts/appstore and can then be uploaded to the App Store.
|
||||
|
||||
## Running tests
|
||||
You can use the provided Makefile to run all tests by using:
|
||||
|
||||
make test
|
||||
|
||||
This will run the PHP unit and integration tests and if a package.json is present in the **js/** folder will execute **npm run test**
|
||||
|
||||
Of course you can also install [PHPUnit](http://phpunit.de/getting-started.html) and use the configurations directly:
|
||||
|
||||
phpunit -c phpunit.xml
|
||||
|
||||
or:
|
||||
|
||||
phpunit -c phpunit.integration.xml
|
||||
|
||||
for integration tests
|
||||
|
||||
27
appinfo/info.xml
Normal file
27
appinfo/info.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0"?>
|
||||
<info xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
|
||||
<!--
|
||||
SPDX-FileCopyrightText: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
SPDX-License-Identifier: CC0-1.0
|
||||
-->
|
||||
<id>photoreduce</id>
|
||||
<name>Photo Reduce</name>
|
||||
<summary>Automatically reduce size of photos and organize folders</summary>
|
||||
<description><![CDATA[Automatically reduce size of photos and organize folders]]></description>
|
||||
<version>0.0.1</version>
|
||||
<licence>agpl</licence>
|
||||
<author mail="andrea.rgn@gmail.com" >Andrea Rigoni Garola</author>
|
||||
<namespace>PhotoReduce</namespace>
|
||||
<category>multimedia</category>
|
||||
<bugs>https://gitea.mildstone.org/YFinGames/ncc_photo_reduce</bugs>
|
||||
<dependencies>
|
||||
<nextcloud min-version="27" max-version="27"/>
|
||||
</dependencies>
|
||||
<navigations>
|
||||
<navigation>
|
||||
<name>Photo Reduce</name>
|
||||
<route>photoreduce.page.index</route>
|
||||
</navigation>
|
||||
</navigations>
|
||||
</info>
|
||||
25
appinfo/routes.php
Normal file
25
appinfo/routes.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
// SPDX-FileCopyrightText: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* Create your routes in here. The name is the lowercase name of the controller
|
||||
* without the controller part, the stuff after the hash is the method.
|
||||
* e.g. page#index -> OCA\PhotoReduce\Controller\PageController->index()
|
||||
*
|
||||
* The controller class has to be registered in the application.php file since
|
||||
* it's instantiated in there
|
||||
*/
|
||||
return [
|
||||
'resources' => [
|
||||
'note' => ['url' => '/notes'],
|
||||
'note_api' => ['url' => '/api/0.1/notes']
|
||||
],
|
||||
'routes' => [
|
||||
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
|
||||
['name' => 'note_api#preflighted_cors', 'url' => '/api/0.1/{path}',
|
||||
'verb' => 'OPTIONS', 'requirements' => ['path' => '.+']]
|
||||
]
|
||||
];
|
||||
5
babel.config.js
Normal file
5
babel.config.js
Normal file
@@ -0,0 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
const babelConfig = require('@nextcloud/babel-config')
|
||||
|
||||
module.exports = babelConfig
|
||||
38
composer.json
Normal file
38
composer.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "nextcloud/photoreduce",
|
||||
"description": "Automatically reduce size of photos and organize folders",
|
||||
"type": "project",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Andrea Rigoni Garola"
|
||||
}
|
||||
],
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9",
|
||||
"sabre/dav": "^4.1",
|
||||
"sabre/xml": "^2.2",
|
||||
"symfony/event-dispatcher": "^5.3.11",
|
||||
"nextcloud/ocp": "dev-stable27",
|
||||
"psalm/phar": "^5.17.0",
|
||||
"nextcloud/coding-standard": "^v1.1.1"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "find . -name \\*.php -not -path './vendor/*' -print0 | xargs -0 -n1 php -l",
|
||||
"cs:check": "php-cs-fixer fix --dry-run --diff",
|
||||
"cs:fix": "php-cs-fixer fix",
|
||||
"psalm": "psalm.phar --threads=1",
|
||||
"psalm:update-baseline": "psalm.phar --threads=1 --update-baseline",
|
||||
"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": {
|
||||
"allow-plugins": {
|
||||
"composer/package-versions-deprecated": true
|
||||
},
|
||||
"platform": {
|
||||
"php": "8.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
56
img/app.svg
Normal file
56
img/app.svg
Normal file
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
height="32"
|
||||
width="32"
|
||||
version="1"
|
||||
viewBox="0 0 32 32"
|
||||
id="svg4"
|
||||
sodipodi:docname="app.svg"
|
||||
inkscape:version="0.92.1 r">
|
||||
<metadata
|
||||
id="metadata10">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs8" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="789"
|
||||
inkscape:window-height="480"
|
||||
id="namedview6"
|
||||
showgrid="false"
|
||||
inkscape:zoom="7.375"
|
||||
inkscape:cx="-8.3389831"
|
||||
inkscape:cy="16"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg4" />
|
||||
<path
|
||||
d="M13.733 0a.915.915 0 0 0-.933.934V3.6c-1.182.304-2.243.794-3.267 1.4L7.6 3.068a.93.93 0 0 0-1.334 0l-3.2 3.2a.93.93 0 0 0 0 1.334L5 9.535c-.607 1.024-1.097 2.085-1.4 3.267H.933a.915.915 0 0 0-.933.934v4.533c0 .53.403.934.933.934H3.6c.303 1.182.793 2.243 1.4 3.267l-1.934 1.935a.93.93 0 0 0 0 1.333l3.2 3.2a.93.93 0 0 0 1.333 0L9.532 27c1.024.61 2.085 1.097 3.266 1.4v2.667c0 .53.402.933.932.933h4.534c.53 0 .933-.403.933-.935V28.4c1.18-.305 2.24-.795 3.265-1.4L24.4 28.93a.93.93 0 0 0 1.332 0l3.2-3.2a.93.93 0 0 0 0-1.333L27 22.465c.607-1.024 1.096-2.085 1.4-3.266h2.665a.915.915 0 0 0 .935-.933v-4.534a.915.915 0 0 0-.934-.933H28.4c-.304-1.182-.792-2.243-1.4-3.267L28.932 7.6a.93.93 0 0 0 0-1.334l-3.2-3.2a.93.93 0 0 0-1.333 0L22.465 5c-1.024-.607-2.084-1.097-3.266-1.4V.933A.915.915 0 0 0 18.267 0zM16 8.87A7.134 7.134 0 0 1 23.13 16 7.134 7.134 0 0 1 16 23.133c-3.936 0-7.13-3.196-7.13-7.132S12.063 8.87 16 8.87z"
|
||||
display="block"
|
||||
fill="#fff"
|
||||
id="path2"
|
||||
style="fill:#ffffff" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
2
img/app.svg.license
Normal file
2
img/app.svg.license
Normal file
@@ -0,0 +1,2 @@
|
||||
SPDX-FileCopyrightText: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
37
lib/AppInfo/Application.php
Normal file
37
lib/AppInfo/Application.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
// SPDX-FileCopyrightText: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
namespace OCA\PhotoReduce\AppInfo;
|
||||
|
||||
use OCP\AppFramework\App;
|
||||
use OCP\AppFramework\Bootstrap\IBootContext;
|
||||
use OCP\AppFramework\Bootstrap\IBootstrap;
|
||||
use OCP\AppFramework\Bootstrap\IRegistrationContext;
|
||||
|
||||
class Application extends App implements IBootstrap {
|
||||
public const APP_ID = 'photoreduce';
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct(self::APP_ID);
|
||||
}
|
||||
|
||||
|
||||
public function register(IRegistrationContext $context): void {
|
||||
/*
|
||||
* For further information about the app bootstrapping, please refer to our documentation:
|
||||
* https://docs.nextcloud.com/server/latest/developer_manual/app_development/bootstrap.html
|
||||
*/
|
||||
// Register your services, event listeners, etc.
|
||||
}
|
||||
|
||||
public function boot(IBootContext $context): void {
|
||||
/*
|
||||
* For further information about the app bootstrapping, please refer to our documentation:
|
||||
* https://docs.nextcloud.com/server/latest/developer_manual/app_development/bootstrap.html
|
||||
*/
|
||||
// Prepare your app.
|
||||
}
|
||||
}
|
||||
25
lib/Controller/Errors.php
Normal file
25
lib/Controller/Errors.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
// SPDX-FileCopyrightText: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
namespace OCA\PhotoReduce\Controller;
|
||||
|
||||
use Closure;
|
||||
|
||||
use OCA\PhotoReduce\Service\NoteNotFound;
|
||||
use OCP\AppFramework\Http;
|
||||
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
|
||||
trait Errors {
|
||||
protected function handleNotFound(Closure $callback): DataResponse {
|
||||
try {
|
||||
return new DataResponse($callback());
|
||||
} catch (NoteNotFound $e) {
|
||||
$message = ['message' => $e->getMessage()];
|
||||
return new DataResponse($message, Http::STATUS_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
81
lib/Controller/NoteApiController.php
Normal file
81
lib/Controller/NoteApiController.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
// SPDX-FileCopyrightText: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
namespace OCA\PhotoReduce\Controller;
|
||||
|
||||
use OCA\PhotoReduce\AppInfo\Application;
|
||||
use OCA\PhotoReduce\Service\NoteService;
|
||||
use OCP\AppFramework\ApiController;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\IRequest;
|
||||
|
||||
class NoteApiController extends ApiController {
|
||||
private NoteService $service;
|
||||
private ?string $userId;
|
||||
|
||||
use Errors;
|
||||
|
||||
public function __construct(IRequest $request,
|
||||
NoteService $service,
|
||||
?string $userId) {
|
||||
parent::__construct(Application::APP_ID, $request);
|
||||
$this->service = $service;
|
||||
$this->userId = $userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @CORS
|
||||
* @NoCSRFRequired
|
||||
* @NoAdminRequired
|
||||
*/
|
||||
public function index(): DataResponse {
|
||||
return new DataResponse($this->service->findAll($this->userId));
|
||||
}
|
||||
|
||||
/**
|
||||
* @CORS
|
||||
* @NoCSRFRequired
|
||||
* @NoAdminRequired
|
||||
*/
|
||||
public function show(int $id): DataResponse {
|
||||
return $this->handleNotFound(function () use ($id) {
|
||||
return $this->service->find($id, $this->userId);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @CORS
|
||||
* @NoCSRFRequired
|
||||
* @NoAdminRequired
|
||||
*/
|
||||
public function create(string $title, string $content): DataResponse {
|
||||
return new DataResponse($this->service->create($title, $content,
|
||||
$this->userId));
|
||||
}
|
||||
|
||||
/**
|
||||
* @CORS
|
||||
* @NoCSRFRequired
|
||||
* @NoAdminRequired
|
||||
*/
|
||||
public function update(int $id, string $title,
|
||||
string $content): DataResponse {
|
||||
return $this->handleNotFound(function () use ($id, $title, $content) {
|
||||
return $this->service->update($id, $title, $content, $this->userId);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @CORS
|
||||
* @NoCSRFRequired
|
||||
* @NoAdminRequired
|
||||
*/
|
||||
public function destroy(int $id): DataResponse {
|
||||
return $this->handleNotFound(function () use ($id) {
|
||||
return $this->service->delete($id, $this->userId);
|
||||
});
|
||||
}
|
||||
}
|
||||
71
lib/Controller/NoteController.php
Normal file
71
lib/Controller/NoteController.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
// SPDX-FileCopyrightText: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
namespace OCA\PhotoReduce\Controller;
|
||||
|
||||
use OCA\PhotoReduce\AppInfo\Application;
|
||||
use OCA\PhotoReduce\Service\NoteService;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\IRequest;
|
||||
|
||||
class NoteController extends Controller {
|
||||
private NoteService $service;
|
||||
private ?string $userId;
|
||||
|
||||
use Errors;
|
||||
|
||||
public function __construct(IRequest $request,
|
||||
NoteService $service,
|
||||
?string $userId) {
|
||||
parent::__construct(Application::APP_ID, $request);
|
||||
$this->service = $service;
|
||||
$this->userId = $userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
*/
|
||||
public function index(): DataResponse {
|
||||
return new DataResponse($this->service->findAll($this->userId));
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
*/
|
||||
public function show(int $id): DataResponse {
|
||||
return $this->handleNotFound(function () use ($id) {
|
||||
return $this->service->find($id, $this->userId);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
*/
|
||||
public function create(string $title, string $content): DataResponse {
|
||||
return new DataResponse($this->service->create($title, $content,
|
||||
$this->userId));
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
*/
|
||||
public function update(int $id, string $title,
|
||||
string $content): DataResponse {
|
||||
return $this->handleNotFound(function () use ($id, $title, $content) {
|
||||
return $this->service->update($id, $title, $content, $this->userId);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
*/
|
||||
public function destroy(int $id): DataResponse {
|
||||
return $this->handleNotFound(function () use ($id) {
|
||||
return $this->service->delete($id, $this->userId);
|
||||
});
|
||||
}
|
||||
}
|
||||
29
lib/Controller/PageController.php
Normal file
29
lib/Controller/PageController.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
// SPDX-FileCopyrightText: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
namespace OCA\PhotoReduce\Controller;
|
||||
|
||||
use OCA\PhotoReduce\AppInfo\Application;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\AppFramework\Http\TemplateResponse;
|
||||
use OCP\IRequest;
|
||||
use OCP\Util;
|
||||
|
||||
class PageController extends Controller {
|
||||
public function __construct(IRequest $request) {
|
||||
parent::__construct(Application::APP_ID, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
* @NoCSRFRequired
|
||||
*/
|
||||
public function index(): TemplateResponse {
|
||||
Util::addScript(Application::APP_ID, 'photoreduce-main');
|
||||
|
||||
return new TemplateResponse(Application::APP_ID, 'main');
|
||||
}
|
||||
}
|
||||
34
lib/Db/Note.php
Normal file
34
lib/Db/Note.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
// SPDX-FileCopyrightText: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
namespace OCA\PhotoReduce\Db;
|
||||
|
||||
use JsonSerializable;
|
||||
|
||||
use OCP\AppFramework\Db\Entity;
|
||||
|
||||
/**
|
||||
* @method getId(): int
|
||||
* @method getTitle(): string
|
||||
* @method setTitle(string $title): void
|
||||
* @method getContent(): string
|
||||
* @method setContent(string $content): void
|
||||
* @method getUserId(): string
|
||||
* @method setUserId(string $userId): void
|
||||
*/
|
||||
class Note extends Entity implements JsonSerializable {
|
||||
protected string $title = '';
|
||||
protected string $content = '';
|
||||
protected string $userId = '';
|
||||
|
||||
public function jsonSerialize(): array {
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'title' => $this->title,
|
||||
'content' => $this->content
|
||||
];
|
||||
}
|
||||
}
|
||||
48
lib/Db/NoteMapper.php
Normal file
48
lib/Db/NoteMapper.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
// SPDX-FileCopyrightText: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
namespace OCA\PhotoReduce\Db;
|
||||
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Db\QBMapper;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\IDBConnection;
|
||||
|
||||
/**
|
||||
* @template-extends QBMapper<Note>
|
||||
*/
|
||||
class NoteMapper extends QBMapper {
|
||||
public function __construct(IDBConnection $db) {
|
||||
parent::__construct($db, 'photoreduce', Note::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
|
||||
* @throws DoesNotExistException
|
||||
*/
|
||||
public function find(int $id, string $userId): Note {
|
||||
/* @var $qb IQueryBuilder */
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('*')
|
||||
->from('photoreduce')
|
||||
->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)))
|
||||
->andWhere($qb->expr()->eq('user_id', $qb->createNamedParameter($userId)));
|
||||
return $this->findEntity($qb);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $userId
|
||||
* @return array
|
||||
*/
|
||||
public function findAll(string $userId): array {
|
||||
/* @var $qb IQueryBuilder */
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('*')
|
||||
->from('photoreduce')
|
||||
->where($qb->expr()->eq('user_id', $qb->createNamedParameter($userId)));
|
||||
return $this->findEntities($qb);
|
||||
}
|
||||
}
|
||||
50
lib/Migration/Version000000Date20181013124731.php
Normal file
50
lib/Migration/Version000000Date20181013124731.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
// SPDX-FileCopyrightText: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
namespace OCA\PhotoReduce\Migration;
|
||||
|
||||
use Closure;
|
||||
use OCP\DB\ISchemaWrapper;
|
||||
use OCP\Migration\IOutput;
|
||||
use OCP\Migration\SimpleMigrationStep;
|
||||
|
||||
class Version000000Date20181013124731 extends SimpleMigrationStep {
|
||||
|
||||
/**
|
||||
* @param IOutput $output
|
||||
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
|
||||
* @param array $options
|
||||
* @return null|ISchemaWrapper
|
||||
*/
|
||||
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) {
|
||||
/** @var ISchemaWrapper $schema */
|
||||
$schema = $schemaClosure();
|
||||
|
||||
if (!$schema->hasTable('photoreduce')) {
|
||||
$table = $schema->createTable('photoreduce');
|
||||
$table->addColumn('id', 'integer', [
|
||||
'autoincrement' => true,
|
||||
'notnull' => true,
|
||||
]);
|
||||
$table->addColumn('title', 'string', [
|
||||
'notnull' => true,
|
||||
'length' => 200
|
||||
]);
|
||||
$table->addColumn('user_id', 'string', [
|
||||
'notnull' => true,
|
||||
'length' => 200,
|
||||
]);
|
||||
$table->addColumn('content', 'text', [
|
||||
'notnull' => true,
|
||||
'default' => ''
|
||||
]);
|
||||
|
||||
$table->setPrimaryKey(['id']);
|
||||
$table->addIndex(['user_id'], 'photoreduce_user_id_index');
|
||||
}
|
||||
return $schema;
|
||||
}
|
||||
}
|
||||
10
lib/Service/NoteNotFound.php
Normal file
10
lib/Service/NoteNotFound.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
// SPDX-FileCopyrightText: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
namespace OCA\PhotoReduce\Service;
|
||||
|
||||
class NoteNotFound extends \Exception {
|
||||
}
|
||||
84
lib/Service/NoteService.php
Normal file
84
lib/Service/NoteService.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
// SPDX-FileCopyrightText: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
namespace OCA\PhotoReduce\Service;
|
||||
|
||||
use Exception;
|
||||
|
||||
use OCA\PhotoReduce\Db\Note;
|
||||
use OCA\PhotoReduce\Db\NoteMapper;
|
||||
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
||||
|
||||
class NoteService {
|
||||
private NoteMapper $mapper;
|
||||
|
||||
public function __construct(NoteMapper $mapper) {
|
||||
$this->mapper = $mapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<Note>
|
||||
*/
|
||||
public function findAll(string $userId): array {
|
||||
return $this->mapper->findAll($userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return never
|
||||
*/
|
||||
private function handleException(Exception $e) {
|
||||
if ($e instanceof DoesNotExistException ||
|
||||
$e instanceof MultipleObjectsReturnedException) {
|
||||
throw new NoteNotFound($e->getMessage());
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
public function find(int $id, string $userId): Note {
|
||||
try {
|
||||
return $this->mapper->find($id, $userId);
|
||||
|
||||
// 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
|
||||
// into service related exceptions so controllers and service users
|
||||
// have to deal with only one type of exception
|
||||
} catch (Exception $e) {
|
||||
$this->handleException($e);
|
||||
}
|
||||
}
|
||||
|
||||
public function create(string $title, string $content, string $userId): Note {
|
||||
$note = new Note();
|
||||
$note->setTitle($title);
|
||||
$note->setContent($content);
|
||||
$note->setUserId($userId);
|
||||
return $this->mapper->insert($note);
|
||||
}
|
||||
|
||||
public function update(int $id, string $title, string $content, string $userId): Note {
|
||||
try {
|
||||
$note = $this->mapper->find($id, $userId);
|
||||
$note->setTitle($title);
|
||||
$note->setContent($content);
|
||||
return $this->mapper->update($note);
|
||||
} catch (Exception $e) {
|
||||
$this->handleException($e);
|
||||
}
|
||||
}
|
||||
|
||||
public function delete(int $id, string $userId): Note {
|
||||
try {
|
||||
$note = $this->mapper->find($id, $userId);
|
||||
$this->mapper->delete($note);
|
||||
return $note;
|
||||
} catch (Exception $e) {
|
||||
$this->handleException($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
43
package.json
Normal file
43
package.json
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "photoreduce",
|
||||
"description": "Automatically reduce size of photos and organize folders",
|
||||
"version": "0.0.1",
|
||||
"author": "Andrea Rigoni Garola <andrea.rgn@gmail.com>",
|
||||
"contributors": [],
|
||||
"bugs": {
|
||||
"url": "https://gitea.mildstone.org/YFinGames/ncc_photo_reduce"
|
||||
},
|
||||
"license": "agpl",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "webpack --node-env production --progress",
|
||||
"dev": "webpack --node-env development --progress",
|
||||
"watch": "webpack --node-env development --progress --watch",
|
||||
"serve": "webpack --node-env development serve --progress",
|
||||
"lint": "eslint --ext .js,.vue src",
|
||||
"lint:fix": "eslint --ext .js,.vue src --fix",
|
||||
"stylelint": "stylelint css/*.css css/*.scss src/**/*.scss src/**/*.vue",
|
||||
"stylelint:fix": "stylelint css/*.css css/*.scss src/**/*.scss src/**/*.vue --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nextcloud/axios": "^1.10.0",
|
||||
"@nextcloud/dialogs": "^3.1.4",
|
||||
"@nextcloud/router": "^2.0.0",
|
||||
"@nextcloud/vue": "^5.4.0",
|
||||
"vue": "^2.7.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"extends @nextcloud/browserslist-config"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^16.0.0",
|
||||
"npm": "^7.0.0 || ^8.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nextcloud/babel-config": "^1.0.0",
|
||||
"@nextcloud/browserslist-config": "^2.2.0",
|
||||
"@nextcloud/eslint-config": "^8.0.0",
|
||||
"@nextcloud/stylelint-config": "^2.1.2",
|
||||
"@nextcloud/webpack-vue-config": "^5.2.1"
|
||||
}
|
||||
}
|
||||
38
psalm.xml
Normal file
38
psalm.xml
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0"?>
|
||||
<psalm
|
||||
errorLevel="4"
|
||||
resolveFromConfigFile="true"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="https://getpsalm.org/schema/config"
|
||||
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
|
||||
errorBaseline="tests/psalm-baseline.xml"
|
||||
>
|
||||
<!--
|
||||
SPDX-FileCopyrightText: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
SPDX-License-Identifier: CC0-1.0
|
||||
-->
|
||||
<projectFiles>
|
||||
<directory name="lib" />
|
||||
<ignoreFiles>
|
||||
<directory name="vendor" />
|
||||
</ignoreFiles>
|
||||
</projectFiles>
|
||||
<extraFiles>
|
||||
<directory name="vendor" />
|
||||
<ignoreFiles>
|
||||
<directory name="vendor/phpunit/php-code-coverage" />
|
||||
<directory name="vendor/psalm" />
|
||||
</ignoreFiles>
|
||||
</extraFiles>
|
||||
<issueHandlers>
|
||||
<UndefinedDocblockClass>
|
||||
<errorLevel type="suppress">
|
||||
<referencedClass name="OC\AppFramework\OCS\BaseResponse"/>
|
||||
<referencedClass name="Doctrine\DBAL\Schema\Schema" />
|
||||
<referencedClass name="Doctrine\DBAL\Schema\SchemaException" />
|
||||
<referencedClass name="Doctrine\DBAL\Driver\Statement" />
|
||||
<referencedClass name="Doctrine\DBAL\Schema\Table" />
|
||||
</errorLevel>
|
||||
</UndefinedDocblockClass>
|
||||
</issueHandlers>
|
||||
</psalm>
|
||||
241
src/App.vue
Normal file
241
src/App.vue
Normal file
@@ -0,0 +1,241 @@
|
||||
<template>
|
||||
<!--
|
||||
SPDX-FileCopyrightText: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
<div id="content" class="app-photoreduce">
|
||||
<AppNavigation>
|
||||
<AppNavigationNew v-if="!loading"
|
||||
:text="t('photoreduce', 'New note')"
|
||||
:disabled="false"
|
||||
button-id="new-photoreduce-button"
|
||||
button-class="icon-add"
|
||||
@click="newNote" />
|
||||
<ul>
|
||||
<AppNavigationItem v-for="note in notes"
|
||||
:key="note.id"
|
||||
:title="note.title ? note.title : t('photoreduce', 'New note')"
|
||||
:class="{active: currentNoteId === note.id}"
|
||||
@click="openNote(note)">
|
||||
<template slot="actions">
|
||||
<ActionButton v-if="note.id === -1"
|
||||
icon="icon-close"
|
||||
@click="cancelNewNote(note)">
|
||||
{{
|
||||
t('photoreduce', 'Cancel note creation') }}
|
||||
</ActionButton>
|
||||
<ActionButton v-else
|
||||
icon="icon-delete"
|
||||
@click="deleteNote(note)">
|
||||
{{
|
||||
t('photoreduce', 'Delete note') }}
|
||||
</ActionButton>
|
||||
</template>
|
||||
</AppNavigationItem>
|
||||
</ul>
|
||||
</AppNavigation>
|
||||
<AppContent>
|
||||
<div v-if="currentNote">
|
||||
<input ref="title"
|
||||
v-model="currentNote.title"
|
||||
type="text"
|
||||
:disabled="updating">
|
||||
<textarea ref="content" v-model="currentNote.content" :disabled="updating" />
|
||||
<input type="button"
|
||||
class="primary"
|
||||
:value="t('photoreduce', 'Save')"
|
||||
:disabled="updating || !savePossible"
|
||||
@click="saveNote">
|
||||
</div>
|
||||
<div v-else id="emptycontent">
|
||||
<div class="icon-file" />
|
||||
<h2>{{
|
||||
t('photoreduce', 'Create a note to get started') }}</h2>
|
||||
</div>
|
||||
</AppContent>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ActionButton from '@nextcloud/vue/dist/Components/ActionButton'
|
||||
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 { generateUrl } from '@nextcloud/router'
|
||||
import { showError, showSuccess } from '@nextcloud/dialogs'
|
||||
import axios from '@nextcloud/axios'
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
components: {
|
||||
ActionButton,
|
||||
AppContent,
|
||||
AppNavigation,
|
||||
AppNavigationItem,
|
||||
AppNavigationNew,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
notes: [],
|
||||
currentNoteId: null,
|
||||
updating: false,
|
||||
loading: true,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
/**
|
||||
* Return the currently selected note object
|
||||
* @returns {Object|null}
|
||||
*/
|
||||
currentNote() {
|
||||
if (this.currentNoteId === null) {
|
||||
return null
|
||||
}
|
||||
return this.notes.find((note) => note.id === this.currentNoteId)
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if a note is selected and its title is not empty
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
savePossible() {
|
||||
return this.currentNote && this.currentNote.title !== ''
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Fetch list of notes when the component is loaded
|
||||
*/
|
||||
async mounted() {
|
||||
try {
|
||||
const response = await axios.get(generateUrl('/apps/photoreduce/notes'))
|
||||
this.notes = response.data
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
showError(t('notestutorial', 'Could not fetch notes'))
|
||||
}
|
||||
this.loading = false
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* Create a new note and focus the note content field automatically
|
||||
* @param {Object} note Note object
|
||||
*/
|
||||
openNote(note) {
|
||||
if (this.updating) {
|
||||
return
|
||||
}
|
||||
this.currentNoteId = note.id
|
||||
this.$nextTick(() => {
|
||||
this.$refs.content.focus()
|
||||
})
|
||||
},
|
||||
/**
|
||||
* Action tiggered when clicking the save button
|
||||
* create a new note or save
|
||||
*/
|
||||
saveNote() {
|
||||
if (this.currentNoteId === -1) {
|
||||
this.createNote(this.currentNote)
|
||||
} else {
|
||||
this.updateNote(this.currentNote)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Create a new note and focus the note content field automatically
|
||||
* The note is not yet saved, therefore an id of -1 is used until it
|
||||
* has been persisted in the backend
|
||||
*/
|
||||
newNote() {
|
||||
if (this.currentNoteId !== -1) {
|
||||
this.currentNoteId = -1
|
||||
this.notes.push({
|
||||
id: -1,
|
||||
title: '',
|
||||
content: '',
|
||||
})
|
||||
this.$nextTick(() => {
|
||||
this.$refs.title.focus()
|
||||
})
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Abort creating a new note
|
||||
*/
|
||||
cancelNewNote() {
|
||||
this.notes.splice(this.notes.findIndex((note) => note.id === -1), 1)
|
||||
this.currentNoteId = null
|
||||
},
|
||||
/**
|
||||
* Create a new note by sending the information to the server
|
||||
* @param {Object} note Note object
|
||||
*/
|
||||
async createNote(note) {
|
||||
this.updating = true
|
||||
try {
|
||||
const response = await axios.post(generateUrl('/apps/photoreduce/notes'), note)
|
||||
const index = this.notes.findIndex((match) => match.id === this.currentNoteId)
|
||||
this.$set(this.notes, index, response.data)
|
||||
this.currentNoteId = response.data.id
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
showError(t('notestutorial', 'Could not create the note'))
|
||||
}
|
||||
this.updating = false
|
||||
},
|
||||
/**
|
||||
* Update an existing note on the server
|
||||
* @param {Object} note Note object
|
||||
*/
|
||||
async updateNote(note) {
|
||||
this.updating = true
|
||||
try {
|
||||
await axios.put(generateUrl(`/apps/photoreduce/notes/${note.id}`), note)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
showError(t('notestutorial', 'Could not update the note'))
|
||||
}
|
||||
this.updating = false
|
||||
},
|
||||
/**
|
||||
* Delete a note, remove it from the frontend and show a hint
|
||||
* @param {Object} note Note object
|
||||
*/
|
||||
async deleteNote(note) {
|
||||
try {
|
||||
await axios.delete(generateUrl(`/apps/photoreduce/notes/${note.id}`))
|
||||
this.notes.splice(this.notes.indexOf(note), 1)
|
||||
if (this.currentNoteId === note.id) {
|
||||
this.currentNoteId = null
|
||||
}
|
||||
showSuccess(t('photoreduce', 'Note deleted'))
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
showError(t('photoreduce', 'Could not delete the note'))
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
#app-content > div {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
input[type='text'] {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
textarea {
|
||||
flex-grow: 1;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
19
src/main.js
Normal file
19
src/main.js
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2018 John Molakvoæ <skjnldsv@protonmail.com>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { generateFilePath } from '@nextcloud/router'
|
||||
|
||||
import Vue from 'vue'
|
||||
import App from './App'
|
||||
|
||||
// eslint-disable-next-line
|
||||
__webpack_public_path__ = generateFilePath(appName, '', 'js/')
|
||||
|
||||
Vue.mixin({ methods: { t, n } })
|
||||
|
||||
export default new Vue({
|
||||
el: '#content',
|
||||
render: h => h(App),
|
||||
})
|
||||
5
stylelint.config.js
Normal file
5
stylelint.config.js
Normal file
@@ -0,0 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Nextcloud contributors
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
const stylelintConfig = require('@nextcloud/stylelint-config')
|
||||
|
||||
module.exports = stylelintConfig
|
||||
6
templates/main.php
Normal file
6
templates/main.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
// SPDX-FileCopyrightText: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
?>
|
||||
<div id="content"></div>
|
||||
64
tests/Integration/NoteIntegrationTest.php
Normal file
64
tests/Integration/NoteIntegrationTest.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
// SPDX-FileCopyrightText: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
namespace OCA\PhotoReduce\Tests\Integration\Controller;
|
||||
|
||||
use OCA\PhotoReduce\Controller\NoteController;
|
||||
use OCA\PhotoReduce\Db\Note;
|
||||
use OCA\PhotoReduce\Db\NoteMapper;
|
||||
|
||||
use OCP\AppFramework\App;
|
||||
use OCP\IRequest;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class NoteIntegrationTest extends TestCase {
|
||||
private NoteController $controller;
|
||||
private QBMapper $mapper;
|
||||
private string $userId = 'john';
|
||||
|
||||
public function setUp(): void {
|
||||
$app = new App('photoreduce');
|
||||
$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());
|
||||
}
|
||||
}
|
||||
16
tests/Unit/Controller/NoteApiControllerTest.php
Normal file
16
tests/Unit/Controller/NoteApiControllerTest.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
// SPDX-FileCopyrightText: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
namespace OCA\PhotoReduce\Tests\Unit\Controller;
|
||||
|
||||
use OCA\PhotoReduce\Controller\NoteApiController;
|
||||
|
||||
class NoteApiControllerTest extends NoteControllerTest {
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->controller = new NoteApiController($this->request, $this->service, $this->userId);
|
||||
}
|
||||
}
|
||||
58
tests/Unit/Controller/NoteControllerTest.php
Normal file
58
tests/Unit/Controller/NoteControllerTest.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
// SPDX-FileCopyrightText: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
namespace OCA\PhotoReduce\Tests\Unit\Controller;
|
||||
|
||||
use OCA\PhotoReduce\Controller\NoteController;
|
||||
|
||||
use OCA\PhotoReduce\Service\NoteNotFound;
|
||||
use OCA\PhotoReduce\Service\NoteService;
|
||||
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\IRequest;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
27
tests/Unit/Controller/PageControllerTest.php
Normal file
27
tests/Unit/Controller/PageControllerTest.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
// SPDX-FileCopyrightText: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
namespace OCA\PhotoReduce\Tests\Unit\Controller;
|
||||
|
||||
use OCP\AppFramework\Http\TemplateResponse;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
66
tests/Unit/Service/NoteServiceTest.php
Normal file
66
tests/Unit/Service/NoteServiceTest.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
// SPDX-FileCopyrightText: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
namespace OCA\PhotoReduce\Tests\Unit\Service;
|
||||
|
||||
use OCA\PhotoReduce\Db\Note;
|
||||
use OCA\PhotoReduce\Db\NoteMapper;
|
||||
|
||||
use OCA\PhotoReduce\Service\NoteNotFound;
|
||||
|
||||
use OCA\PhotoReduce\Service\NoteService;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
7
tests/bootstrap.php
Normal file
7
tests/bootstrap.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
// SPDX-FileCopyrightText: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
require_once __DIR__ . '/../../../tests/bootstrap.php';
|
||||
11
tests/phpunit.integration.xml
Normal file
11
tests/phpunit.integration.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<phpunit bootstrap="bootstrap.php" colors="true">
|
||||
<!--
|
||||
SPDX-FileCopyrightText: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
<testsuites>
|
||||
<testsuite name="integration">
|
||||
<directory>./Integration</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
</phpunit>
|
||||
11
tests/phpunit.xml
Normal file
11
tests/phpunit.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<phpunit bootstrap="bootstrap.php" colors="true">
|
||||
<!--
|
||||
SPDX-FileCopyrightText: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
<testsuites>
|
||||
<testsuite name="unit">
|
||||
<directory>./Unit</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
</phpunit>
|
||||
3
tests/psalm-baseline.xml
Normal file
3
tests/psalm-baseline.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<files psalm-version="4.x-dev@">
|
||||
</files>
|
||||
2
tests/psalm-baseline.xml.license
Normal file
2
tests/psalm-baseline.xml.license
Normal file
@@ -0,0 +1,2 @@
|
||||
SPDX-FileCopyrightText: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
SPDX-License-Identifier: CC0-1.0
|
||||
5
webpack.config.js
Normal file
5
webpack.config.js
Normal file
@@ -0,0 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Andrea Rigoni Garola <andrea.rgn@gmail.com>
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
const webpackConfig = require('@nextcloud/webpack-vue-config')
|
||||
|
||||
module.exports = webpackConfig
|
||||
Reference in New Issue
Block a user