Switch to vue.js tutorial app
from https://github.com/nextcloud/app-tutorial
This commit is contained in:
parent
87b671a21a
commit
f4bf5cb042
5
.eslintrc.js
Normal file
5
.eslintrc.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
module.exports = {
|
||||||
|
extends: [
|
||||||
|
'@nextcloud',
|
||||||
|
]
|
||||||
|
}
|
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/js/* binary
|
24
.github/dependabot.yml
vendored
Normal file
24
.github/dependabot.yml
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: composer
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
day: saturday
|
||||||
|
time: "03:00"
|
||||||
|
timezone: Europe/Paris
|
||||||
|
open-pull-requests-limit: 10
|
||||||
|
labels:
|
||||||
|
- 3. to review
|
||||||
|
- dependencies
|
||||||
|
- package-ecosystem: npm
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
day: saturday
|
||||||
|
time: "03:00"
|
||||||
|
timezone: Europe/Paris
|
||||||
|
open-pull-requests-limit: 10
|
||||||
|
labels:
|
||||||
|
- 3. to review
|
||||||
|
- dependencies
|
117
.github/workflows/command-compile.yml
vendored
Normal file
117
.github/workflows/command-compile.yml
vendored
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
name: Compile Command
|
||||||
|
on:
|
||||||
|
issue_comment:
|
||||||
|
types: [created]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
init:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
# On pull requests and if the comment starts with `/compile`
|
||||||
|
if: github.event.issue.pull_request != '' && startsWith(github.event.comment.body, '/compile')
|
||||||
|
|
||||||
|
outputs:
|
||||||
|
git_path: ${{ steps.git-path.outputs.path }}
|
||||||
|
arg1: ${{ steps.command.outputs.arg1 }}
|
||||||
|
arg2: ${{ steps.command.outputs.arg2 }}
|
||||||
|
head_ref: ${{ steps.comment-branch.outputs.head_ref }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check actor permission
|
||||||
|
uses: skjnldsv/check-actor-permission@v2
|
||||||
|
with:
|
||||||
|
require: write
|
||||||
|
|
||||||
|
- name: Add reaction on start
|
||||||
|
uses: peter-evans/create-or-update-comment@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.COMMAND_BOT_PAT }}
|
||||||
|
repository: ${{ github.event.repository.full_name }}
|
||||||
|
comment-id: ${{ github.event.comment.id }}
|
||||||
|
reaction-type: "+1"
|
||||||
|
|
||||||
|
- name: Parse command
|
||||||
|
uses: skjnldsv/parse-command-comment@master
|
||||||
|
id: command
|
||||||
|
|
||||||
|
# Init path depending on which command is run
|
||||||
|
- name: Init path
|
||||||
|
id: git-path
|
||||||
|
run: |
|
||||||
|
if ${{ startsWith(steps.command.outputs.arg1, '/') }}; then
|
||||||
|
echo "::set-output name=path::${{ github.workspace }}${{steps.command.outputs.arg1}}"
|
||||||
|
else
|
||||||
|
echo "::set-output name=path::${{ github.workspace }}${{steps.command.outputs.arg2}}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Init branch
|
||||||
|
uses: xt0rted/pull-request-comment-branch@v1
|
||||||
|
id: comment-branch
|
||||||
|
|
||||||
|
process:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: init
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout ${{ needs.init.outputs.head_ref }}
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.COMMAND_BOT_PAT }}
|
||||||
|
fetch-depth: 0
|
||||||
|
ref: ${{ needs.init.outputs.head_ref }}
|
||||||
|
|
||||||
|
- name: Setup git
|
||||||
|
run: |
|
||||||
|
git config --local user.email "nextcloud-command@users.noreply.github.com"
|
||||||
|
git config --local user.name "nextcloud-command"
|
||||||
|
|
||||||
|
- name: Read package.json node and npm engines version
|
||||||
|
uses: skjnldsv/read-package-engines-version-actions@v1
|
||||||
|
id: package-engines-versions
|
||||||
|
with:
|
||||||
|
fallbackNode: '^12'
|
||||||
|
fallbackNpm: '^6'
|
||||||
|
|
||||||
|
- name: Set up node ${{ steps.package-engines-versions.outputs.nodeVersion }}
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: ${{ steps.package-engines-versions.outputs.nodeVersion }}
|
||||||
|
cache: npm
|
||||||
|
|
||||||
|
- name: Set up npm ${{ steps.package-engines-versions.outputs.npmVersion }}
|
||||||
|
run: npm i -g npm@"${{ steps.package-engines-versions.outputs.npmVersion }}"
|
||||||
|
|
||||||
|
- name: Install dependencies & build
|
||||||
|
run: |
|
||||||
|
npm ci
|
||||||
|
npm run build --if-present
|
||||||
|
|
||||||
|
- name: Commit and push default
|
||||||
|
if: ${{ needs.init.outputs.arg1 != 'fixup' && needs.init.outputs.arg1 != 'amend' }}
|
||||||
|
run: |
|
||||||
|
git add ${{ needs.init.outputs.git_path }}
|
||||||
|
git commit --signoff -m 'Compile assets'
|
||||||
|
git push origin ${{ needs.init.outputs.head_ref }}
|
||||||
|
|
||||||
|
- name: Commit and push fixup
|
||||||
|
if: ${{ needs.init.outputs.arg1 == 'fixup' }}
|
||||||
|
run: |
|
||||||
|
git add ${{ needs.init.outputs.git_path }}
|
||||||
|
git commit --fixup=HEAD --signoff
|
||||||
|
git push origin ${{ needs.init.outputs.head_ref }}
|
||||||
|
|
||||||
|
- name: Commit and push amend
|
||||||
|
if: ${{ needs.init.outputs.arg1 == 'amend' }}
|
||||||
|
run: |
|
||||||
|
git add ${{ needs.init.outputs.git_path }}
|
||||||
|
git commit --amend --no-edit --signoff
|
||||||
|
git push --force origin ${{ needs.init.outputs.head_ref }}
|
||||||
|
|
||||||
|
- name: Add reaction on failure
|
||||||
|
uses: peter-evans/create-or-update-comment@v1
|
||||||
|
if: failure()
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.COMMAND_BOT_PAT }}
|
||||||
|
repository: ${{ github.event.repository.full_name }}
|
||||||
|
comment-id: ${{ github.event.comment.id }}
|
||||||
|
reaction-type: "-1"
|
46
.github/workflows/command-rebase.yml
vendored
Normal file
46
.github/workflows/command-rebase.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
|
||||||
|
|
||||||
|
name: Rebase command
|
||||||
|
|
||||||
|
on:
|
||||||
|
issue_comment:
|
||||||
|
types: created
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
rebase:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
# On pull requests and if the comment starts with `/rebase`
|
||||||
|
if: github.event.issue.pull_request != '' && startsWith(github.event.comment.body, '/rebase')
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Add reaction on start
|
||||||
|
uses: peter-evans/create-or-update-comment@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.COMMAND_BOT_PAT }}
|
||||||
|
repository: ${{ github.event.repository.full_name }}
|
||||||
|
comment-id: ${{ github.event.comment.id }}
|
||||||
|
reaction-type: "+1"
|
||||||
|
|
||||||
|
- name: Checkout the latest code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
token: ${{ secrets.COMMAND_BOT_PAT }}
|
||||||
|
|
||||||
|
- name: Automatic Rebase
|
||||||
|
uses: cirrus-actions/rebase@1.5
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.COMMAND_BOT_PAT }}
|
||||||
|
|
||||||
|
- name: Add reaction on failure
|
||||||
|
uses: peter-evans/create-or-update-comment@v1
|
||||||
|
if: failure()
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.COMMAND_BOT_PAT }}
|
||||||
|
repository: ${{ github.event.repository.full_name }}
|
||||||
|
comment-id: ${{ github.event.comment.id }}
|
||||||
|
reaction-type: "-1"
|
29
.github/workflows/dependabot-approve-merge.yml
vendored
Normal file
29
.github/workflows/dependabot-approve-merge.yml
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
# 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
|
||||||
|
|
||||||
|
name: Dependabot
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- stable*
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
auto-merge:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
# Default github action approve
|
||||||
|
- uses: hmarr/auto-approve-action@v2
|
||||||
|
if: github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]'
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
# Nextcloud bot approve and merge request
|
||||||
|
- uses: ahmadnassri/action-dependabot-auto-merge@v2
|
||||||
|
if: github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]'
|
||||||
|
with:
|
||||||
|
target: minor
|
||||||
|
github-token: ${{ secrets.DEPENDABOT_AUTOMERGE_TOKEN }}
|
12
.github/workflows/fixup.yml
vendored
Normal file
12
.github/workflows/fixup.yml
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
name: Pull request checks
|
||||||
|
on: pull_request
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
commit-message-check:
|
||||||
|
name: Block fixup and squash commits
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Run check
|
||||||
|
uses: xt0rted/block-autosquash-commits-action@main
|
||||||
|
with:
|
||||||
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
111
.github/workflows/lint.yml
vendored
Normal file
111
.github/workflows/lint.yml
vendored
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
name: Lint
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- stable*
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
php:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
php-versions: ["7.3", "7.4", "8.0"]
|
||||||
|
|
||||||
|
name: php${{ matrix.php-versions }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- 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
|
||||||
|
|
||||||
|
php-cs-fixer:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
php-versions: ["7.4"]
|
||||||
|
|
||||||
|
name: cs php${{ matrix.php-versions }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Set up php
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: ${{ matrix.php-versions }}
|
||||||
|
coverage: none
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: composer i
|
||||||
|
|
||||||
|
- name: Run coding standards check
|
||||||
|
run: composer run cs:check || ( echo 'Please run `composer run cs:fix` to format your code' && exit 1 )
|
||||||
|
|
||||||
|
node:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
name: eslint node
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Set up node
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: 14
|
||||||
|
|
||||||
|
- name: Set up npm7
|
||||||
|
run: npm i -g npm@7
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Lint
|
||||||
|
run: npm run lint
|
||||||
|
|
||||||
|
stylelint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
name: stylelint node
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Set up node
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: 14
|
||||||
|
|
||||||
|
- name: Set up npm7
|
||||||
|
run: npm i -g npm@7
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Lint
|
||||||
|
run: npm run stylelint
|
||||||
|
|
||||||
|
xml-linters:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@master
|
||||||
|
- name: Download schema
|
||||||
|
run: wget https://apps.nextcloud.com/schema/apps/info.xsd
|
||||||
|
- name: Lint info.xml
|
||||||
|
uses: ChristophWurst/xmllint-action@v1
|
||||||
|
with:
|
||||||
|
xml-file: ./appinfo/info.xml
|
||||||
|
xml-schema-file: ./info.xsd
|
52
.github/workflows/node.yml
vendored
Normal file
52
.github/workflows/node.yml
vendored
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
# 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
|
||||||
|
|
||||||
|
name: Node
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- stable*
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
name: node
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Read package.json node and npm engines version
|
||||||
|
uses: skjnldsv/read-package-engines-version-actions@v1.1
|
||||||
|
id: versions
|
||||||
|
with:
|
||||||
|
fallbackNode: '^12'
|
||||||
|
fallbackNpm: '^6'
|
||||||
|
|
||||||
|
- 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 & build
|
||||||
|
run: |
|
||||||
|
npm ci
|
||||||
|
npm run build --if-present
|
||||||
|
|
||||||
|
- name: Check webpack build changes
|
||||||
|
run: |
|
||||||
|
bash -c "[[ ! \"`git status --porcelain `\" ]] || exit 1"
|
||||||
|
|
||||||
|
- name: Show changes on failure
|
||||||
|
if: failure()
|
||||||
|
run: |
|
||||||
|
git status
|
||||||
|
git --no-pager diff
|
215
.github/workflows/phpunit.yml
vendored
Normal file
215
.github/workflows/phpunit.yml
vendored
Normal file
|
@ -0,0 +1,215 @@
|
||||||
|
name: PHPUnit
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- stable*
|
||||||
|
|
||||||
|
env:
|
||||||
|
APP_NAME: notestutorial
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
php:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
# do not stop on another job's failure
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
php-versions: ['7.4']
|
||||||
|
databases: ['sqlite']
|
||||||
|
server-versions: ['master']
|
||||||
|
|
||||||
|
name: php${{ matrix.php-versions }}-${{ matrix.databases }}-${{ matrix.server-versions }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout server
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
repository: nextcloud/server
|
||||||
|
ref: ${{ matrix.server-versions }}
|
||||||
|
|
||||||
|
- name: Checkout submodules
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
auth_header="$(git config --local --get http.https://github.com/.extraheader)"
|
||||||
|
git submodule sync --recursive
|
||||||
|
git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1
|
||||||
|
|
||||||
|
- name: Checkout app
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
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=${{ matrix.databases }} --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 --force ${{ env.APP_NAME }}
|
||||||
|
php -S localhost:8080 &
|
||||||
|
|
||||||
|
- name: PHPUnit
|
||||||
|
working-directory: apps/${{ env.APP_NAME }}
|
||||||
|
run: ./vendor/phpunit/phpunit/phpunit -c phpunit.xml
|
||||||
|
|
||||||
|
- name: PHPUnit integration
|
||||||
|
working-directory: apps/${{ env.APP_NAME }}
|
||||||
|
run: ./vendor/phpunit/phpunit/phpunit -c phpunit.integration.xml
|
||||||
|
|
||||||
|
mysql:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
# do not stop on another job's failure
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
php-versions: ['7.3', '7.4']
|
||||||
|
databases: ['mysql']
|
||||||
|
server-versions: ['master']
|
||||||
|
|
||||||
|
name: php${{ matrix.php-versions }}-${{ matrix.databases }}-${{ matrix.server-versions }}
|
||||||
|
|
||||||
|
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: Checkout server
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
repository: nextcloud/server
|
||||||
|
ref: ${{ matrix.server-versions }}
|
||||||
|
|
||||||
|
- name: Checkout submodules
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
auth_header="$(git config --local --get http.https://github.com/.extraheader)"
|
||||||
|
git submodule sync --recursive
|
||||||
|
git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1
|
||||||
|
|
||||||
|
- name: Checkout app
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
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=${{ matrix.databases }} --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 --force ${{ env.APP_NAME }}
|
||||||
|
php -S localhost:8080 &
|
||||||
|
|
||||||
|
- name: PHPUnit
|
||||||
|
working-directory: apps/${{ env.APP_NAME }}
|
||||||
|
run: ./vendor/phpunit/phpunit/phpunit -c phpunit.xml
|
||||||
|
|
||||||
|
- name: PHPUnit integration
|
||||||
|
working-directory: apps/${{ env.APP_NAME }}
|
||||||
|
run: ./vendor/phpunit/phpunit/phpunit -c phpunit.integration.xml
|
||||||
|
|
||||||
|
pgsql:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
# do not stop on another job's failure
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
php-versions: ['7.4']
|
||||||
|
databases: ['pgsql']
|
||||||
|
server-versions: ['master']
|
||||||
|
|
||||||
|
name: php${{ matrix.php-versions }}-${{ matrix.databases }}-${{ matrix.server-versions }}
|
||||||
|
|
||||||
|
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: Checkout server
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
repository: nextcloud/server
|
||||||
|
ref: ${{ matrix.server-versions }}
|
||||||
|
|
||||||
|
- name: Checkout submodules
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
auth_header="$(git config --local --get http.https://github.com/.extraheader)"
|
||||||
|
git submodule sync --recursive
|
||||||
|
git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1
|
||||||
|
|
||||||
|
- name: Checkout app
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
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=${{ matrix.databases }} --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 --force ${{ env.APP_NAME }}
|
||||||
|
php -S localhost:8080 &
|
||||||
|
|
||||||
|
- name: PHPUnit
|
||||||
|
working-directory: apps/${{ env.APP_NAME }}
|
||||||
|
run: ./vendor/phpunit/phpunit/phpunit -c phpunit.xml
|
||||||
|
|
||||||
|
- name: PHPUnit integration
|
||||||
|
working-directory: apps/${{ env.APP_NAME }}
|
||||||
|
run: ./vendor/phpunit/phpunit/phpunit -c phpunit.integration.xml
|
10
.gitignore
vendored
10
.gitignore
vendored
|
@ -1,6 +1,10 @@
|
||||||
/.idea
|
/.idea/
|
||||||
/build
|
*.iml
|
||||||
|
/build/
|
||||||
|
node_modules/
|
||||||
|
/.php_cs.cache
|
||||||
|
js/*hot-update.*
|
||||||
|
|
||||||
### Composer ###
|
### Composer ###
|
||||||
composer.phar
|
composer.phar
|
||||||
/vendor
|
/vendor/
|
||||||
|
|
17
.php_cs.dist
Normal file
17
.php_cs.dist
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
require_once './vendor/autoload.php';
|
||||||
|
|
||||||
|
use Nextcloud\CodingStandard\Config;
|
||||||
|
|
||||||
|
$config = new Config();
|
||||||
|
$config
|
||||||
|
->getFinder()
|
||||||
|
->notPath('build')
|
||||||
|
->notPath('l10n')
|
||||||
|
->notPath('src')
|
||||||
|
->notPath('vendor')
|
||||||
|
->in(__DIR__);
|
||||||
|
return $config;
|
64
.travis.yml
64
.travis.yml
|
@ -1,64 +0,0 @@
|
||||||
sudo: false
|
|
||||||
dist: trusty
|
|
||||||
language: php
|
|
||||||
php:
|
|
||||||
- 5.6
|
|
||||||
- 7
|
|
||||||
- 7.1
|
|
||||||
env:
|
|
||||||
global:
|
|
||||||
- CORE_BRANCH=stable15
|
|
||||||
matrix:
|
|
||||||
- DB=pgsql
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
allow_failures:
|
|
||||||
- env: DB=pgsql CORE_BRANCH=master
|
|
||||||
include:
|
|
||||||
- php: 5.6
|
|
||||||
env: DB=sqlite
|
|
||||||
- php: 5.6
|
|
||||||
env: DB=mysql
|
|
||||||
- php: 5.6
|
|
||||||
env: DB=pgsql CORE_BRANCH=master
|
|
||||||
fast_finish: true
|
|
||||||
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
# enable a display for running JavaScript tests
|
|
||||||
- export DISPLAY=:99.0
|
|
||||||
- sh -e /etc/init.d/xvfb start
|
|
||||||
- nvm install 8
|
|
||||||
- npm install -g npm@latest
|
|
||||||
- make
|
|
||||||
- make appstore
|
|
||||||
# install core
|
|
||||||
- cd ../
|
|
||||||
- git clone https://github.com/nextcloud/server.git --recursive --depth 1 -b $CORE_BRANCH nextcloud
|
|
||||||
- mv "$TRAVIS_BUILD_DIR" nextcloud/apps/upschooling
|
|
||||||
|
|
||||||
before_script:
|
|
||||||
- if [[ "$DB" == 'pgsql' ]]; then createuser -U travis -s oc_autotest; fi
|
|
||||||
- if [[ "$DB" == 'mysql' ]]; then mysql -u root -e 'create database oc_autotest;'; fi
|
|
||||||
- if [[ "$DB" == 'mysql' ]]; then mysql -u root -e "CREATE USER 'oc_autotest'@'localhost' IDENTIFIED BY '';"; fi
|
|
||||||
- if [[ "$DB" == 'mysql' ]]; then mysql -u root -e "grant all on oc_autotest.* to 'oc_autotest'@'localhost';"; fi
|
|
||||||
- cd nextcloud
|
|
||||||
- mkdir data
|
|
||||||
- ./occ maintenance:install --database-name oc_autotest --database-user oc_autotest --admin-user admin --admin-pass admin --database $DB --database-pass=''
|
|
||||||
- ./occ app:enable upschooling
|
|
||||||
- php -S localhost:8080 &
|
|
||||||
- cd apps/upschooling
|
|
||||||
|
|
||||||
script:
|
|
||||||
- make test
|
|
||||||
|
|
||||||
after_failure:
|
|
||||||
- cat ../../data/nextcloud.log
|
|
||||||
|
|
||||||
addons:
|
|
||||||
firefox: 'latest'
|
|
||||||
mariadb: '10.1'
|
|
||||||
|
|
||||||
services:
|
|
||||||
- postgresql
|
|
||||||
- mariadb
|
|
12
CHANGELOG.md
Normal file
12
CHANGELOG.md
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# Changelog
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||||
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
|
||||||
|
## [0.0.2] - 2017-07-31
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- First release
|
2
COPYING
2
COPYING
|
@ -658,4 +658,4 @@ specific requirements.
|
||||||
You should also get your employer (if you work as a programmer) or school,
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||||
<http://www.gnu.org/licenses/>.
|
<http://www.gnu.org/licenses/>.
|
172
Makefile
172
Makefile
|
@ -1,73 +1,17 @@
|
||||||
# This file is licensed under the Affero General Public License version 3 or
|
# This file is licensed under the Affero General Public License version 3 or
|
||||||
# later. See the COPYING file.
|
# later. See the COPYING file.
|
||||||
# @author Bernhard Posselt <dev@bernhard-posselt.com>
|
|
||||||
# @copyright Bernhard Posselt 2016
|
|
||||||
|
|
||||||
# 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_package_name=$(source_build_directory)/$(app_name)
|
|
||||||
appstore_build_directory=$(CURDIR)/build/artifacts/appstore
|
|
||||||
appstore_package_name=$(appstore_build_directory)/$(app_name)
|
|
||||||
npm=$(shell which npm 2> /dev/null)
|
|
||||||
composer=$(shell which composer 2> /dev/null)
|
composer=$(shell which composer 2> /dev/null)
|
||||||
|
|
||||||
all: build
|
all: dev-setup lint build-js-production test
|
||||||
|
|
||||||
|
# Dev env management
|
||||||
|
dev-setup: clean clean-dev composer npm-init
|
||||||
|
|
||||||
# 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
|
# Installs and updates the composer dependencies. If composer is not installed
|
||||||
# a copy is fetched from the web
|
# a copy is fetched from the web
|
||||||
.PHONY: composer
|
|
||||||
composer:
|
composer:
|
||||||
ifeq (, $(composer))
|
ifeq (, $(composer))
|
||||||
@echo "No composer command available, downloading a copy from the web"
|
@echo "No composer command available, downloading a copy from the web"
|
||||||
|
@ -75,81 +19,53 @@ ifeq (, $(composer))
|
||||||
curl -sS https://getcomposer.org/installer | php
|
curl -sS https://getcomposer.org/installer | php
|
||||||
mv composer.phar $(build_tools_directory)
|
mv composer.phar $(build_tools_directory)
|
||||||
php $(build_tools_directory)/composer.phar install --prefer-dist
|
php $(build_tools_directory)/composer.phar install --prefer-dist
|
||||||
|
php $(build_tools_directory)/composer.phar update --prefer-dist
|
||||||
else
|
else
|
||||||
composer install --prefer-dist
|
composer install --prefer-dist
|
||||||
|
composer update --prefer-dist
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Installs npm dependencies
|
npm-init:
|
||||||
.PHONY: npm
|
npm ci
|
||||||
npm:
|
|
||||||
ifeq (,$(wildcard $(CURDIR)/package.json))
|
npm-update:
|
||||||
cd js && $(npm) run build
|
npm update
|
||||||
else
|
|
||||||
|
# Building
|
||||||
|
build-js:
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
build-js-production:
|
||||||
npm run build
|
npm run build
|
||||||
endif
|
|
||||||
|
|
||||||
# Removes the appstore build
|
watch-js:
|
||||||
.PHONY: clean
|
npm run watch
|
||||||
|
|
||||||
|
serve-js:
|
||||||
|
npm run serve
|
||||||
|
|
||||||
|
# Linting
|
||||||
|
lint:
|
||||||
|
npm run lint
|
||||||
|
|
||||||
|
lint-fix:
|
||||||
|
npm run lint:fix
|
||||||
|
|
||||||
|
# Style linting
|
||||||
|
stylelint:
|
||||||
|
npm run stylelint
|
||||||
|
|
||||||
|
stylelint-fix:
|
||||||
|
npm run stylelint:fix
|
||||||
|
|
||||||
|
# Cleaning
|
||||||
clean:
|
clean:
|
||||||
rm -rf ./build
|
rm -rf js/*
|
||||||
|
|
||||||
# Same as clean but also removes dependencies installed by composer, bower and
|
clean-dev:
|
||||||
# npm
|
|
||||||
.PHONY: distclean
|
|
||||||
distclean: clean
|
|
||||||
rm -rf vendor
|
|
||||||
rm -rf node_modules
|
rm -rf node_modules
|
||||||
rm -rf js/vendor
|
|
||||||
rm -rf js/node_modules
|
|
||||||
|
|
||||||
# Builds the source and appstore package
|
# Tests
|
||||||
.PHONY: dist
|
test:
|
||||||
dist:
|
./vendor/phpunit/phpunit/phpunit -c phpunit.xml
|
||||||
make source
|
./vendor/phpunit/phpunit/phpunit -c phpunit.integration.xml
|
||||||
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
|
|
||||||
|
|
49
README.md
49
README.md
|
@ -1,52 +1,31 @@
|
||||||
# U Pschooling
|
# UPschooling
|
||||||
|
|
||||||
Place this app in **nextcloud/apps/**
|
Place this app in **nextcloud/apps/**
|
||||||
|
|
||||||
## Building the app
|
[![PHPUnit GitHub Action](https://github.com/nextcloud/app-tutorial/workflows/PHPUnit/badge.svg)](https://github.com/nextcloud/app-tutorial/actions?query=workflow%3APHPUnit)
|
||||||
|
[![Node GitHub Action](https://github.com/nextcloud/app-tutorial/workflows/Node/badge.svg)](https://github.com/nextcloud/app-tutorial/actions?query=workflow%3ANode)
|
||||||
|
[![Lint GitHub Action](https://github.com/nextcloud/app-tutorial/workflows/Lint/badge.svg)](https://github.com/nextcloud/app-tutorial/actions?query=workflow%3ALint)
|
||||||
|
|
||||||
The app can be built by using the provided Makefile by running:
|
This is the [tutorial app](https://docs.nextcloud.com/server/latest/developer_manual/app_development/tutorial.html) which shows how to develop a very simple notes app.
|
||||||
|
|
||||||
make
|
## Installing dependencies
|
||||||
|
|
||||||
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
|
make composer
|
||||||
|
|
||||||
or:
|
## Frontend development
|
||||||
|
|
||||||
phpunit -c phpunit.integration.xml
|
The app tutorial also shows the very basic implementation of an app frontend using [Vue.js](https://vuejs.org/). To build the frontend code after doing changes to its source in `src/` requires to have Node and npm installed.
|
||||||
|
|
||||||
for integration tests
|
- 👩💻 Run `make dev-setup` to install the frontend dependencies
|
||||||
|
- 🏗 To build the Javascript whenever you make changes, run `make build-js`
|
||||||
|
|
||||||
|
To continuously run the build when editing source files you can make use of the `make watch-js` command.
|
|
@ -1,15 +1,13 @@
|
||||||
<?php
|
<?php
|
||||||
/**
|
|
||||||
* 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\UPschooling\Controller\PageController->index()
|
|
||||||
*
|
|
||||||
* The controller class has to be registered in the application.php file since
|
|
||||||
* it's instantiated in there
|
|
||||||
*/
|
|
||||||
return [
|
return [
|
||||||
'routes' => [
|
'resources' => [
|
||||||
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
|
'note' => ['url' => '/notes'],
|
||||||
['name' => 'page#do_echo', 'url' => '/echo', 'verb' => 'POST'],
|
'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' => '.+']]
|
||||||
|
]
|
||||||
];
|
];
|
||||||
|
|
3
babel.config.js
Normal file
3
babel.config.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
const babelConfig = require('@nextcloud/babel-config')
|
||||||
|
|
||||||
|
module.exports = babelConfig
|
|
@ -10,6 +10,19 @@
|
||||||
],
|
],
|
||||||
"require": {},
|
"require": {},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^5.4"
|
"phpunit/phpunit": "^8.5",
|
||||||
|
"nextcloud/coding-standard": "^0.5.0"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"optimize-autoloader": true,
|
||||||
|
"classmap-authoritative": true,
|
||||||
|
"platform": {
|
||||||
|
"php": "7.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"lint": "find . -name \\*.php -not -path './vendor/*' -not -path './build/*' -print0 | xargs -0 -n1 php -l",
|
||||||
|
"cs:check": "php-cs-fixer fix --dry-run --diff",
|
||||||
|
"cs:fix": "php-cs-fixer fix"
|
||||||
}
|
}
|
||||||
}
|
}
|
2323
composer.lock
generated
2323
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -1,3 +1,34 @@
|
||||||
#hello {
|
#app-content-wrapper {
|
||||||
color: red;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#editor {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#editor .input {
|
||||||
|
height: calc(100% - 51px);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#editor .save {
|
||||||
|
height: 50px;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
border-top: 1px solid #ccc;
|
||||||
|
background-color: #fafafa;
|
||||||
|
}
|
||||||
|
|
||||||
|
#editor textarea {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
border: 0;
|
||||||
|
margin: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#editor button {
|
||||||
|
height: 44px;
|
||||||
|
}
|
BIN
js/notestutorial-main.js
Normal file
BIN
js/notestutorial-main.js
Normal file
Binary file not shown.
BIN
js/notestutorial-main.js.LICENSE.txt
Normal file
BIN
js/notestutorial-main.js.LICENSE.txt
Normal file
Binary file not shown.
BIN
js/notestutorial-main.js.map
Normal file
BIN
js/notestutorial-main.js.map
Normal file
Binary file not shown.
BIN
js/script.js
BIN
js/script.js
Binary file not shown.
13
lib/AppInfo/Application.php
Normal file
13
lib/AppInfo/Application.php
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace OCA\NotesTutorial\AppInfo;
|
||||||
|
|
||||||
|
use OCP\AppFramework\App;
|
||||||
|
|
||||||
|
class Application extends App {
|
||||||
|
public const APP_ID = 'notestutorial';
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
parent::__construct(self::APP_ID);
|
||||||
|
}
|
||||||
|
}
|
21
lib/Controller/Errors.php
Normal file
21
lib/Controller/Errors.php
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace OCA\NotesTutorial\Controller;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
|
||||||
|
use OCP\AppFramework\Http;
|
||||||
|
use OCP\AppFramework\Http\DataResponse;
|
||||||
|
|
||||||
|
use OCA\NotesTutorial\Service\NoteNotFound;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
80
lib/Controller/NoteApiController.php
Normal file
80
lib/Controller/NoteApiController.php
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace OCA\NotesTutorial\Controller;
|
||||||
|
|
||||||
|
use OCA\NotesTutorial\AppInfo\Application;
|
||||||
|
use OCA\NotesTutorial\Service\NoteService;
|
||||||
|
use OCP\AppFramework\ApiController;
|
||||||
|
use OCP\AppFramework\Http\DataResponse;
|
||||||
|
use OCP\IRequest;
|
||||||
|
|
||||||
|
class NoteApiController extends ApiController {
|
||||||
|
/** @var NoteService */
|
||||||
|
private $service;
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
private $userId;
|
||||||
|
|
||||||
|
use Errors;
|
||||||
|
|
||||||
|
public function __construct(IRequest $request,
|
||||||
|
NoteService $service,
|
||||||
|
$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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
70
lib/Controller/NoteController.php
Normal file
70
lib/Controller/NoteController.php
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace OCA\NotesTutorial\Controller;
|
||||||
|
|
||||||
|
use OCA\NotesTutorial\AppInfo\Application;
|
||||||
|
use OCA\NotesTutorial\Service\NoteService;
|
||||||
|
use OCP\AppFramework\Controller;
|
||||||
|
use OCP\AppFramework\Http\DataResponse;
|
||||||
|
use OCP\IRequest;
|
||||||
|
|
||||||
|
class NoteController extends Controller {
|
||||||
|
/** @var NoteService */
|
||||||
|
private $service;
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
private $userId;
|
||||||
|
|
||||||
|
use Errors;
|
||||||
|
|
||||||
|
public function __construct(IRequest $request,
|
||||||
|
NoteService $service,
|
||||||
|
$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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,36 +1,27 @@
|
||||||
<?php
|
<?php
|
||||||
namespace OCA\UPschooling\Controller;
|
|
||||||
|
|
||||||
use OCP\AppFramework\Http\TextPlainResponse;
|
namespace OCA\NotesTutorial\Controller;
|
||||||
use OCP\IRequest;
|
|
||||||
use OCP\AppFramework\Http\TemplateResponse;
|
use OCA\NotesTutorial\AppInfo\Application;
|
||||||
use OCP\AppFramework\Http\DataResponse;
|
|
||||||
use OCP\AppFramework\Controller;
|
use OCP\AppFramework\Controller;
|
||||||
|
use OCP\AppFramework\Http\TemplateResponse;
|
||||||
|
use OCP\IRequest;
|
||||||
|
use OCP\Util;
|
||||||
|
|
||||||
class PageController extends Controller {
|
class PageController extends Controller {
|
||||||
private $userId;
|
public function __construct(IRequest $request) {
|
||||||
|
parent::__construct(Application::APP_ID, $request);
|
||||||
public function __construct($AppName, IRequest $request, $UserId){
|
|
||||||
parent::__construct($AppName, $request);
|
|
||||||
$this->userId = $UserId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CAUTION: the @Stuff turns off security checks; for this page no admin is
|
|
||||||
* required and no CSRF check. If you don't know what CSRF is, read
|
|
||||||
* it up in the docs or you might create a security hole. This is
|
|
||||||
* basically the only required method to add this exemption, don't
|
|
||||||
* add it to any other method if you don't exactly know what it does
|
|
||||||
*
|
|
||||||
* @NoAdminRequired
|
* @NoAdminRequired
|
||||||
* @NoCSRFRequired
|
* @NoCSRFRequired
|
||||||
|
*
|
||||||
|
* Render default template
|
||||||
*/
|
*/
|
||||||
public function index() {
|
public function index() {
|
||||||
return new TemplateResponse('upschooling', 'index'); // templates/index.php
|
Util::addScript(Application::APP_ID, 'notestutorial-main');
|
||||||
}
|
|
||||||
|
|
||||||
public function doEcho() {
|
return new TemplateResponse(Application::APP_ID, 'main');
|
||||||
return new DataResponse($_POST);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
21
lib/Db/Note.php
Normal file
21
lib/Db/Note.php
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace OCA\NotesTutorial\Db;
|
||||||
|
|
||||||
|
use JsonSerializable;
|
||||||
|
|
||||||
|
use OCP\AppFramework\Db\Entity;
|
||||||
|
|
||||||
|
class Note extends Entity implements JsonSerializable {
|
||||||
|
protected $title;
|
||||||
|
protected $content;
|
||||||
|
protected $userId;
|
||||||
|
|
||||||
|
public function jsonSerialize(): array {
|
||||||
|
return [
|
||||||
|
'id' => $this->id,
|
||||||
|
'title' => $this->title,
|
||||||
|
'content' => $this->content
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
45
lib/Db/NoteMapper.php
Normal file
45
lib/Db/NoteMapper.php
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace OCA\NotesTutorial\Db;
|
||||||
|
|
||||||
|
use OCP\AppFramework\Db\DoesNotExistException;
|
||||||
|
use OCP\AppFramework\Db\Entity;
|
||||||
|
use OCP\AppFramework\Db\QBMapper;
|
||||||
|
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||||
|
use OCP\IDBConnection;
|
||||||
|
|
||||||
|
class NoteMapper extends QBMapper {
|
||||||
|
public function __construct(IDBConnection $db) {
|
||||||
|
parent::__construct($db, 'notestutorial', Note::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $id
|
||||||
|
* @param string $userId
|
||||||
|
* @return Entity|Note
|
||||||
|
* @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('notestutorial')
|
||||||
|
->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('notestutorial')
|
||||||
|
->where($qb->expr()->eq('user_id', $qb->createNamedParameter($userId)));
|
||||||
|
return $this->findEntities($qb);
|
||||||
|
}
|
||||||
|
}
|
48
lib/Migration/Version000000Date20181013124731.php
Normal file
48
lib/Migration/Version000000Date20181013124731.php
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\NotesTutorial\Migration;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use OCP\DB\ISchemaWrapper;
|
||||||
|
use OCP\Migration\SimpleMigrationStep;
|
||||||
|
use OCP\Migration\IOutput;
|
||||||
|
|
||||||
|
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('notestutorial')) {
|
||||||
|
$table = $schema->createTable('notestutorial');
|
||||||
|
$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'], 'notestutorial_user_id_index');
|
||||||
|
}
|
||||||
|
return $schema;
|
||||||
|
}
|
||||||
|
}
|
6
lib/Service/NoteNotFound.php
Normal file
6
lib/Service/NoteNotFound.php
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace OCA\NotesTutorial\Service;
|
||||||
|
|
||||||
|
class NoteNotFound extends \Exception {
|
||||||
|
}
|
76
lib/Service/NoteService.php
Normal file
76
lib/Service/NoteService.php
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace OCA\NotesTutorial\Service;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
use OCP\AppFramework\Db\DoesNotExistException;
|
||||||
|
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
||||||
|
|
||||||
|
use OCA\NotesTutorial\Db\Note;
|
||||||
|
use OCA\NotesTutorial\Db\NoteMapper;
|
||||||
|
|
||||||
|
class NoteService {
|
||||||
|
|
||||||
|
/** @var NoteMapper */
|
||||||
|
private $mapper;
|
||||||
|
|
||||||
|
public function __construct(NoteMapper $mapper) {
|
||||||
|
$this->mapper = $mapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findAll(string $userId): array {
|
||||||
|
return $this->mapper->findAll($userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function handleException(Exception $e): void {
|
||||||
|
if ($e instanceof DoesNotExistException ||
|
||||||
|
$e instanceof MultipleObjectsReturnedException) {
|
||||||
|
throw new NoteNotFound($e->getMessage());
|
||||||
|
} else {
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function find($id, $userId) {
|
||||||
|
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($title, $content, $userId) {
|
||||||
|
$note = new Note();
|
||||||
|
$note->setTitle($title);
|
||||||
|
$note->setContent($content);
|
||||||
|
$note->setUserId($userId);
|
||||||
|
return $this->mapper->insert($note);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update($id, $title, $content, $userId) {
|
||||||
|
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($id, $userId) {
|
||||||
|
try {
|
||||||
|
$note = $this->mapper->find($id, $userId);
|
||||||
|
$this->mapper->delete($note);
|
||||||
|
return $note;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$this->handleException($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25700
package-lock.json
generated
Normal file
25700
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
50
package.json
Normal file
50
package.json
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
{
|
||||||
|
"name": "notestutorial",
|
||||||
|
"description": "A simple Nextcloud app tutorial for building a notes app",
|
||||||
|
"version": "19.0.0",
|
||||||
|
"author": "Julius Härtl <jus@bitgrid.net",
|
||||||
|
"contributors": [
|
||||||
|
"John Molakvoæ <skjnldsv@protonmail.com>"
|
||||||
|
],
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/nextcloud/app-tutorial/issues"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"url": "https://github.com/nextcloud/app-tutorial",
|
||||||
|
"type": "git"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/nextcloud/app-tutorial",
|
||||||
|
"license": "agpl",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"build": "NODE_ENV=production webpack --progress --config webpack.js",
|
||||||
|
"dev": "NODE_ENV=development webpack --progress --config webpack.js",
|
||||||
|
"watch": "NODE_ENV=development webpack --progress --watch --config webpack.js",
|
||||||
|
"serve": "NODE_ENV=development webpack serve --progress --config webpack.js",
|
||||||
|
"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.6.0",
|
||||||
|
"@nextcloud/dialogs": "^3.1.2",
|
||||||
|
"@nextcloud/router": "^1.2.0",
|
||||||
|
"@nextcloud/vue": "^3.10.1",
|
||||||
|
"vue": "^2.6.14"
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
"extends @nextcloud/browserslist-config"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0",
|
||||||
|
"npm": ">=7.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@nextcloud/babel-config": "^1.0.0",
|
||||||
|
"@nextcloud/browserslist-config": "^2.1.0",
|
||||||
|
"@nextcloud/eslint-config": "^6.1.0",
|
||||||
|
"@nextcloud/stylelint-config": "^1.0.0-beta.0",
|
||||||
|
"@nextcloud/webpack-vue-config": "^4.1.0"
|
||||||
|
}
|
||||||
|
}
|
234
src/App.vue
Normal file
234
src/App.vue
Normal file
|
@ -0,0 +1,234 @@
|
||||||
|
<template>
|
||||||
|
<div id="content" class="app-notestutorial">
|
||||||
|
<AppNavigation>
|
||||||
|
<AppNavigationNew v-if="!loading"
|
||||||
|
:text="t('notestutorial', 'New note')"
|
||||||
|
:disabled="false"
|
||||||
|
button-id="new-notestutorial-button"
|
||||||
|
button-class="icon-add"
|
||||||
|
@click="newNote" />
|
||||||
|
<ul>
|
||||||
|
<AppNavigationItem v-for="note in notes"
|
||||||
|
:key="note.id"
|
||||||
|
:title="note.title ? note.title : t('notestutorial', '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('notestutorial', 'Cancel note creation') }}
|
||||||
|
</ActionButton>
|
||||||
|
<ActionButton v-else
|
||||||
|
icon="icon-delete"
|
||||||
|
@click="deleteNote(note)">
|
||||||
|
{{ t('notestutorial', '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('notestutorial', 'Save')"
|
||||||
|
:disabled="updating || !savePossible"
|
||||||
|
@click="saveNote">
|
||||||
|
</div>
|
||||||
|
<div v-else id="emptycontent">
|
||||||
|
<div class="icon-file" />
|
||||||
|
<h2>{{ t('notestutorial', '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/notestutorial/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/notestutorial/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/notestutorial/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/notestutorial/notes/${note.id}`))
|
||||||
|
this.notes.splice(this.notes.indexOf(note), 1)
|
||||||
|
if (this.currentNoteId === note.id) {
|
||||||
|
this.currentNoteId = null
|
||||||
|
}
|
||||||
|
showSuccess(t('notestutorial', 'Note deleted'))
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
showError(t('notestutorial', '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>
|
35
src/main.js
Normal file
35
src/main.js
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (c) 2018 John Molakvoæ <skjnldsv@protonmail.com>
|
||||||
|
*
|
||||||
|
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
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),
|
||||||
|
})
|
3
stylelint.config.js
Normal file
3
stylelint.config.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
const stylelintConfig = require('@nextcloud/stylelint-config')
|
||||||
|
|
||||||
|
module.exports = stylelintConfig
|
|
@ -1,11 +0,0 @@
|
||||||
<h1>Hello world</h1>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<label for="text">Text to Send:</label><input type="text" id="text">
|
|
||||||
<button id="submit">Submit</button>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<label for="response">Response:</label>
|
|
||||||
<textarea id="response"></textarea>
|
|
|
@ -1,18 +0,0 @@
|
||||||
<?php
|
|
||||||
script('upschooling', 'script');
|
|
||||||
style('upschooling', 'style');
|
|
||||||
?>
|
|
||||||
|
|
||||||
<div id="app">
|
|
||||||
<div id="app-navigation">
|
|
||||||
<?php print_unescaped($this->inc('navigation/index')); ?>
|
|
||||||
<?php print_unescaped($this->inc('settings/index')); ?>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="app-content">
|
|
||||||
<div id="app-content-wrapper">
|
|
||||||
<?php print_unescaped($this->inc('content/index')); ?>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
1
templates/main.php
Normal file
1
templates/main.php
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<div id="content"></div>
|
|
@ -1,10 +0,0 @@
|
||||||
<ul>
|
|
||||||
<li><a href="#">First level entry</a></li>
|
|
||||||
<li>
|
|
||||||
<a href="#">First level container</a>
|
|
||||||
<ul>
|
|
||||||
<li><a href="#">Second level entry</a></li>
|
|
||||||
<li><a href="#">Second level entry</a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
|
@ -1,10 +0,0 @@
|
||||||
<div id="app-settings">
|
|
||||||
<div id="app-settings-header">
|
|
||||||
<button class="settings-button"
|
|
||||||
data-apps-slide-toggle="#app-settings-content"
|
|
||||||
></button>
|
|
||||||
</div>
|
|
||||||
<div id="app-settings-content">
|
|
||||||
<!-- Your settings in here -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,29 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace OCA\UPschooling\Tests\Integration\Controller;
|
|
||||||
|
|
||||||
use OCP\AppFramework\App;
|
|
||||||
use Test\TestCase;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This test shows how to make a small Integration Test. Query your class
|
|
||||||
* directly from the container, only pass in mocks if needed and run your tests
|
|
||||||
* against the database
|
|
||||||
*/
|
|
||||||
class AppTest extends TestCase {
|
|
||||||
|
|
||||||
private $container;
|
|
||||||
|
|
||||||
public function setUp() {
|
|
||||||
parent::setUp();
|
|
||||||
$app = new App('upschooling');
|
|
||||||
$this->container = $app->getContainer();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testAppInstalled() {
|
|
||||||
$appManager = $this->container->query('OCP\App\IAppManager');
|
|
||||||
$this->assertTrue($appManager->isInstalled('upschooling'));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
61
tests/Integration/NoteIntegrationTest.php
Normal file
61
tests/Integration/NoteIntegrationTest.php
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace OCA\NotesTutorial\Tests\Integration\Controller;
|
||||||
|
|
||||||
|
use OCP\AppFramework\App;
|
||||||
|
use OCP\IRequest;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
|
||||||
|
use OCA\NotesTutorial\Db\Note;
|
||||||
|
use OCA\NotesTutorial\Db\NoteMapper;
|
||||||
|
use OCA\NotesTutorial\Controller\NoteController;
|
||||||
|
|
||||||
|
class NoteIntegrationTest extends TestCase {
|
||||||
|
private $controller;
|
||||||
|
private $mapper;
|
||||||
|
private $userId = 'john';
|
||||||
|
|
||||||
|
public function setUp(): void {
|
||||||
|
$app = new App('notestutorial');
|
||||||
|
$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->query(NoteController::class);
|
||||||
|
$this->mapper = $container->query(NoteMapper::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUpdate() {
|
||||||
|
// 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());
|
||||||
|
}
|
||||||
|
}
|
12
tests/Unit/Controller/NoteApiControllerTest.php
Normal file
12
tests/Unit/Controller/NoteApiControllerTest.php
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace OCA\NotesTutorial\Tests\Unit\Controller;
|
||||||
|
|
||||||
|
use OCA\NotesTutorial\Controller\NoteApiController;
|
||||||
|
|
||||||
|
class NoteApiControllerTest extends NoteControllerTest {
|
||||||
|
public function setUp(): void {
|
||||||
|
parent::setUp();
|
||||||
|
$this->controller = new NoteApiController($this->request, $this->service, $this->userId);
|
||||||
|
}
|
||||||
|
}
|
54
tests/Unit/Controller/NoteControllerTest.php
Normal file
54
tests/Unit/Controller/NoteControllerTest.php
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace OCA\NotesTutorial\Tests\Unit\Controller;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
use OCP\AppFramework\Http;
|
||||||
|
use OCP\IRequest;
|
||||||
|
|
||||||
|
use OCA\NotesTutorial\Service\NoteNotFound;
|
||||||
|
use OCA\NotesTutorial\Service\NoteService;
|
||||||
|
use OCA\NotesTutorial\Controller\NoteController;
|
||||||
|
|
||||||
|
class NoteControllerTest extends TestCase {
|
||||||
|
protected $controller;
|
||||||
|
protected $service;
|
||||||
|
protected $userId = 'john';
|
||||||
|
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() {
|
||||||
|
$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() {
|
||||||
|
// 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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,31 +1,24 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace OCA\UPschooling\Tests\Unit\Controller;
|
namespace OCA\NotesTutorial\Controller;
|
||||||
|
|
||||||
use PHPUnit_Framework_TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
use OCP\AppFramework\Http\TemplateResponse;
|
use OCP\AppFramework\Http\TemplateResponse;
|
||||||
|
|
||||||
use OCA\UPschooling\Controller\PageController;
|
class PageControllerTest extends TestCase {
|
||||||
|
|
||||||
|
|
||||||
class PageControllerTest extends PHPUnit_Framework_TestCase {
|
|
||||||
private $controller;
|
private $controller;
|
||||||
private $userId = 'john';
|
|
||||||
|
|
||||||
public function setUp() {
|
public function setUp(): void {
|
||||||
$request = $this->getMockBuilder('OCP\IRequest')->getMock();
|
$request = $this->getMockBuilder('OCP\IRequest')->getMock();
|
||||||
|
$this->controller = new PageController($request);
|
||||||
$this->controller = new PageController(
|
|
||||||
'upschooling', $request, $this->userId
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function testIndex() {
|
public function testIndex() {
|
||||||
$result = $this->controller->index();
|
$result = $this->controller->index();
|
||||||
|
|
||||||
$this->assertEquals('index', $result->getTemplateName());
|
$this->assertEquals('main', $result->getTemplateName());
|
||||||
$this->assertTrue($result instanceof TemplateResponse);
|
$this->assertTrue($result instanceof TemplateResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
62
tests/Unit/Service/NoteServiceTest.php
Normal file
62
tests/Unit/Service/NoteServiceTest.php
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace OCA\NotesTutorial\Tests\Unit\Service;
|
||||||
|
|
||||||
|
use OCA\NotesTutorial\Service\NoteNotFound;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
use OCP\AppFramework\Db\DoesNotExistException;
|
||||||
|
|
||||||
|
use OCA\NotesTutorial\Db\Note;
|
||||||
|
use OCA\NotesTutorial\Service\NoteService;
|
||||||
|
use OCA\NotesTutorial\Db\NoteMapper;
|
||||||
|
|
||||||
|
class NoteServiceTest extends TestCase {
|
||||||
|
private $service;
|
||||||
|
private $mapper;
|
||||||
|
private $userId = 'john';
|
||||||
|
|
||||||
|
public function setUp(): void {
|
||||||
|
$this->mapper = $this->getMockBuilder(NoteMapper::class)
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
$this->service = new NoteService($this->mapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUpdate() {
|
||||||
|
// 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() {
|
||||||
|
$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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,19 +1,3 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
if (!defined('PHPUNIT_RUN')) {
|
require_once __DIR__ . '/../../../tests/bootstrap.php';
|
||||||
define('PHPUNIT_RUN', 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
require_once __DIR__.'/../../../lib/base.php';
|
|
||||||
|
|
||||||
// Fix for "Autoload path not allowed: .../tests/lib/testcase.php"
|
|
||||||
\OC::$loader->addValidRoot(OC::$SERVERROOT . '/tests');
|
|
||||||
|
|
||||||
// Fix for "Autoload path not allowed: .../upschooling/tests/testcase.php"
|
|
||||||
\OC_App::loadApp('upschooling');
|
|
||||||
|
|
||||||
if(!class_exists('PHPUnit_Framework_TestCase')) {
|
|
||||||
require_once('PHPUnit/Autoload.php');
|
|
||||||
}
|
|
||||||
|
|
||||||
OC_Hook::clear();
|
|
||||||
|
|
3
webpack.js
Normal file
3
webpack.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
const webpackConfig = require('@nextcloud/webpack-vue-config')
|
||||||
|
|
||||||
|
module.exports = webpackConfig
|
Loading…
Reference in a new issue