Compare commits
7 commits
16438014fa
...
a400c7210c
Author | SHA1 | Date | |
---|---|---|---|
Ben | a400c7210c | ||
Ben | 6183f6f5db | ||
Ben | 3e17e44ab7 | ||
Ben | f4bf5cb042 | ||
Ben | 87b671a21a | ||
Finn | 546e2ffe58 | ||
Ben | 7e62489337 |
5
.eslintrc.js
Normal file
5
.eslintrc.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
module.exports = {
|
||||
extends: [
|
||||
'@nextcloud',
|
||||
]
|
||||
}
|
3
.gitattributes
vendored
Normal file
3
.gitattributes
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
/js/* binary
|
||||
*.png -text
|
||||
*.epgz -text
|
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: upschooling
|
||||
|
||||
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
|
||||
/build
|
||||
/.idea/
|
||||
*.iml
|
||||
/build/
|
||||
node_modules/
|
||||
/.php_cs.cache
|
||||
/js/
|
||||
|
||||
### Composer ###
|
||||
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
|
172
Makefile
172
Makefile
|
@ -1,73 +1,17 @@
|
|||
# This file is licensed under the Affero General Public License version 3 or
|
||||
# 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))
|
||||
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)
|
||||
|
||||
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
|
||||
# a copy is fetched from the web
|
||||
.PHONY: composer
|
||||
composer:
|
||||
ifeq (, $(composer))
|
||||
@echo "No composer command available, downloading a copy from the web"
|
||||
|
@ -75,81 +19,53 @@ ifeq (, $(composer))
|
|||
curl -sS https://getcomposer.org/installer | php
|
||||
mv composer.phar $(build_tools_directory)
|
||||
php $(build_tools_directory)/composer.phar install --prefer-dist
|
||||
php $(build_tools_directory)/composer.phar update --prefer-dist
|
||||
else
|
||||
composer install --prefer-dist
|
||||
composer update --prefer-dist
|
||||
endif
|
||||
|
||||
# Installs npm dependencies
|
||||
.PHONY: npm
|
||||
npm:
|
||||
ifeq (,$(wildcard $(CURDIR)/package.json))
|
||||
cd js && $(npm) run build
|
||||
else
|
||||
npm-init:
|
||||
npm ci
|
||||
|
||||
npm-update:
|
||||
npm update
|
||||
|
||||
# Building
|
||||
build-js:
|
||||
npm run dev
|
||||
|
||||
build-js-production:
|
||||
npm run build
|
||||
endif
|
||||
|
||||
# Removes the appstore build
|
||||
.PHONY: clean
|
||||
watch-js:
|
||||
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:
|
||||
rm -rf ./build
|
||||
rm -rf js/*
|
||||
|
||||
# Same as clean but also removes dependencies installed by composer, bower and
|
||||
# npm
|
||||
.PHONY: distclean
|
||||
distclean: clean
|
||||
rm -rf vendor
|
||||
clean-dev:
|
||||
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 ../$(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
|
||||
# Tests
|
||||
test:
|
||||
./vendor/phpunit/phpunit/phpunit -c phpunit.xml
|
||||
./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/**
|
||||
|
||||
## 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
|
||||
|
||||
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"
|
||||
}
|
||||
```
|
||||
## Installing dependencies
|
||||
|
||||
|
||||
## 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.
|
|
@ -2,7 +2,7 @@
|
|||
<info xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
|
||||
<id>upschooling</id>
|
||||
<name>U Pschooling</name>
|
||||
<name>UPschooling</name>
|
||||
<summary>UPschooling Support Platform</summary>
|
||||
<description><![CDATA[Ticketsystem für UPschooling]]></description>
|
||||
<version>0.0.1</version>
|
||||
|
@ -16,7 +16,7 @@
|
|||
</dependencies>
|
||||
<navigations>
|
||||
<navigation>
|
||||
<name>U Pschooling</name>
|
||||
<name>Support</name>
|
||||
<route>upschooling.page.index</route>
|
||||
</navigation>
|
||||
</navigations>
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
<?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 [
|
||||
'resources' => [
|
||||
'note' => ['url' => '/notes'],
|
||||
'note_api' => ['url' => '/api/0.1/notes']
|
||||
],
|
||||
'routes' => [
|
||||
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
|
||||
['name' => 'page#do_echo', 'url' => '/echo', 'verb' => 'POST'],
|
||||
['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-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"
|
||||
}
|
||||
}
|
2405
composer.lock
generated
2405
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -1,3 +1,34 @@
|
|||
#hello {
|
||||
color: red;
|
||||
#app-content-wrapper {
|
||||
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;
|
||||
}
|
13
lib/AppInfo/Application.php
Normal file
13
lib/AppInfo/Application.php
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\UPschooling\AppInfo;
|
||||
|
||||
use OCP\AppFramework\App;
|
||||
|
||||
class Application extends App {
|
||||
public const APP_ID = 'upschooling';
|
||||
|
||||
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\UPschooling\Controller;
|
||||
|
||||
use Closure;
|
||||
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
|
||||
use OCA\UPschooling\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\UPschooling\Controller;
|
||||
|
||||
use OCA\UPschooling\AppInfo\Application;
|
||||
use OCA\UPschooling\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\UPschooling\Controller;
|
||||
|
||||
use OCA\UPschooling\AppInfo\Application;
|
||||
use OCA\UPschooling\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,31 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\UPschooling\Controller;
|
||||
|
||||
use OCP\IRequest;
|
||||
use OCP\AppFramework\Http\TemplateResponse;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCA\UPschooling\AppInfo\Application;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\AppFramework\Http\TemplateResponse;
|
||||
use OCP\IRequest;
|
||||
use OCP\Util;
|
||||
|
||||
class PageController extends Controller {
|
||||
private $userId;
|
||||
|
||||
public function __construct($AppName, IRequest $request, $UserId){
|
||||
parent::__construct($AppName, $request);
|
||||
$this->userId = $UserId;
|
||||
public function __construct(IRequest $request) {
|
||||
parent::__construct(Application::APP_ID, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @NoCSRFRequired
|
||||
*
|
||||
* Render default template
|
||||
*/
|
||||
public function index() {
|
||||
return new TemplateResponse('upschooling', 'index'); // templates/index.php
|
||||
}
|
||||
Util::addScript(Application::APP_ID, 'upschooling-main');
|
||||
|
||||
return new TemplateResponse(Application::APP_ID, 'main');
|
||||
}
|
||||
}
|
||||
|
|
21
lib/Db/Note.php
Normal file
21
lib/Db/Note.php
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\UPschooling\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\UPschooling\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, 'upschooling', 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('upschooling')
|
||||
->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('upschooling')
|
||||
->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\UPschooling\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('upschooling')) {
|
||||
$table = $schema->createTable('upschooling');
|
||||
$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'], 'upschooling_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\UPschooling\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\UPschooling\Service;
|
||||
|
||||
use Exception;
|
||||
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
||||
|
||||
use OCA\UPschooling\Db\Note;
|
||||
use OCA\UPschooling\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);
|
||||
}
|
||||
}
|
||||
}
|
BIN
mockup/nutzeransicht-einzelticket.epgz
Normal file
BIN
mockup/nutzeransicht-einzelticket.epgz
Normal file
Binary file not shown.
BIN
mockup/nutzeransicht-einzelticket.png
Normal file
BIN
mockup/nutzeransicht-einzelticket.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 208 KiB |
BIN
mockup/nutzeransicht-meine-tickets.epgz
Normal file
BIN
mockup/nutzeransicht-meine-tickets.epgz
Normal file
Binary file not shown.
BIN
mockup/nutzeransicht-meine-tickets.png
Normal file
BIN
mockup/nutzeransicht-meine-tickets.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 133 KiB |
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": "upschooling",
|
||||
"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"
|
||||
}
|
||||
}
|
|
@ -8,4 +8,3 @@ DIR="${0%/*}"
|
|||
podman unshare -- chown -R 33 "$DIR"
|
||||
podman unshare -- chgrp -R 0 "$DIR"
|
||||
podman unshare -- chmod -R ug+rw "$DIR"
|
||||
|
||||
|
|
|
@ -8,5 +8,7 @@ DIR="${0%/*}"
|
|||
podman run -d --name=nextcloud --replace=true -p 8080:80 -v "$DIR:/var/www/html/custom_apps/upschooling" docker.io/nextcloud
|
||||
podman exec nextcloud chown -R 33 /var/www/html/custom_apps
|
||||
"$DIR/podman-reown.sh"
|
||||
podman exec --user 33 nextcloud bash -c 'cd /var/www/html/custom_apps/upschooling && make'
|
||||
podman exec --user 33 nextcloud bash -c 'cd /var/www/html/custom_apps/upschooling && make composer'
|
||||
podman exec --user 33 nextcloud php occ maintenance:install --database sqlite --admin-user admin --admin-pass admin
|
||||
podman exec --user 33 nextcloud php occ config:system:set --value=true --type=boolean debug
|
||||
podman exec --user 33 nextcloud php occ app:enable --force upschooling
|
||||
|
|
234
src/App.vue
Normal file
234
src/App.vue
Normal file
|
@ -0,0 +1,234 @@
|
|||
<template>
|
||||
<div id="content" class="app-upschooling">
|
||||
<AppNavigation>
|
||||
<AppNavigationNew v-if="!loading"
|
||||
:text="t('upschooling', 'New note')"
|
||||
:disabled="false"
|
||||
button-id="new-upschooling-button"
|
||||
button-class="icon-add"
|
||||
@click="newNote" />
|
||||
<ul>
|
||||
<AppNavigationItem v-for="note in notes"
|
||||
:key="note.id"
|
||||
:title="note.title ? note.title : t('upschooling', '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('upschooling', 'Cancel note creation') }}
|
||||
</ActionButton>
|
||||
<ActionButton v-else
|
||||
icon="icon-delete"
|
||||
@click="deleteNote(note)">
|
||||
{{ t('upschooling', '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('upschooling', 'Save')"
|
||||
:disabled="updating || !savePossible"
|
||||
@click="saveNote">
|
||||
</div>
|
||||
<div v-else id="emptycontent">
|
||||
<div class="icon-file" />
|
||||
<h2>{{ t('upschooling', '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/upschooling/notes'))
|
||||
this.notes = response.data
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
showError(t('upschooling', '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/upschooling/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('upschooling', '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/upschooling/notes/${note.id}`), note)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
showError(t('upschooling', '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/upschooling/notes/${note.id}`))
|
||||
this.notes.splice(this.notes.indexOf(note), 1)
|
||||
if (this.currentNoteId === note.id) {
|
||||
this.currentNoteId = null
|
||||
}
|
||||
showSuccess(t('upschooling', 'Note deleted'))
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
showError(t('upschooling', '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 +0,0 @@
|
|||
<h1>Hello world</h1>
|
|
@ -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\UPschooling\Tests\Integration\Controller;
|
||||
|
||||
use OCP\AppFramework\App;
|
||||
use OCP\IRequest;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
|
||||
use OCA\UPschooling\Db\Note;
|
||||
use OCA\UPschooling\Db\NoteMapper;
|
||||
use OCA\UPschooling\Controller\NoteController;
|
||||
|
||||
class NoteIntegrationTest extends TestCase {
|
||||
private $controller;
|
||||
private $mapper;
|
||||
private $userId = 'john';
|
||||
|
||||
public function setUp(): void {
|
||||
$app = new App('upschooling');
|
||||
$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\UPschooling\Tests\Unit\Controller;
|
||||
|
||||
use OCA\UPschooling\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\UPschooling\Tests\Unit\Controller;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\IRequest;
|
||||
|
||||
use OCA\UPschooling\Service\NoteNotFound;
|
||||
use OCA\UPschooling\Service\NoteService;
|
||||
use OCA\UPschooling\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
|
||||
|
||||
namespace OCA\UPschooling\Tests\Unit\Controller;
|
||||
namespace OCA\UPschooling\Controller;
|
||||
|
||||
use PHPUnit_Framework_TestCase;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
use OCP\AppFramework\Http\TemplateResponse;
|
||||
|
||||
use OCA\UPschooling\Controller\PageController;
|
||||
|
||||
|
||||
class PageControllerTest extends PHPUnit_Framework_TestCase {
|
||||
class PageControllerTest extends TestCase {
|
||||
private $controller;
|
||||
private $userId = 'john';
|
||||
|
||||
public function setUp() {
|
||||
public function setUp(): void {
|
||||
$request = $this->getMockBuilder('OCP\IRequest')->getMock();
|
||||
|
||||
$this->controller = new PageController(
|
||||
'upschooling', $request, $this->userId
|
||||
);
|
||||
$this->controller = new PageController($request);
|
||||
}
|
||||
|
||||
|
||||
public function testIndex() {
|
||||
$result = $this->controller->index();
|
||||
|
||||
$this->assertEquals('index', $result->getTemplateName());
|
||||
$this->assertEquals('main', $result->getTemplateName());
|
||||
$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\UPschooling\Tests\Unit\Service;
|
||||
|
||||
use OCA\UPschooling\Service\NoteNotFound;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
|
||||
use OCA\UPschooling\Db\Note;
|
||||
use OCA\UPschooling\Service\NoteService;
|
||||
use OCA\UPschooling\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
|
||||
|
||||
if (!defined('PHPUNIT_RUN')) {
|
||||
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();
|
||||
require_once __DIR__ . '/../../../tests/bootstrap.php';
|
||||
|
|
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