App Tutorial

A Simple Nextcloud App Tutorial

Quem sou eu?

Realizador de sonhos desde 2003
Amante de opensource
Palestrante
PHP Zend Certified Engineer ( ZEND024235 )
PHPRio ( https://telegram.me/phprio )

CTO LibreCode
Redes sociais: ( VitorMattos ou VitorMattosRJ )

Essentials

> appinfo/info.xml

# PRESENTING CODE
<?xml version="1.0"?>
<info xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
	  xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
	<id>wifi_password</id>
	<name>Wifi - Password</name>
	<summary>Create, manage and share wi-fi passwords</summary>
	<description>Create, manage and share wi-fi passwords using qrcode</description>
	<version>1.0.0-alfa</version>
	<licence>agpl</licence>
	<author>Vinicius Reis</author>
	<author>Vitor Mattos</author>
	<namespace>WifiPassword</namespace>
	<category>files</category>
	<category>tools</category>
	<bugs>https://github.com/vinicius73/wifi-password/issues</bugs>
	<dependencies>
		<nextcloud min-version="22" max-version="25" />
	</dependencies>
</info>

Essentials

> lib/AppInfo/Application.php

# PRESENTING CODE
<?php

declare(strict_types=1);

namespace OCA\WifiPassword\AppInfo;

use OCP\AppFramework\App;

class Application extends App {
	public const APP_ID = 'wifi_password';

	public function __construct() {
		parent::__construct(self::APP_ID);
	}
}

Essentials

composer init

# PRESENTING CODE
{
	"name": "vinicius73/wifi-password",
	"type": "project",
	"license": "AGPL",
	"authors": [
		{
			"name": "Vinicius Reis",
			"email": "luiz.vinicius73@gmail.com"
		},
		{
			"name": "Vitor Mattos",
			"email": "vitor@php.rio"
		}
	],
	"autoload": {
		"psr-4": {
			"Root\\Teste\\": "src/"
		}
	}
}

Essentials

composer init

# PRESENTING CODE
{
	"name": "vinicius73/wifi-password",
	"type": "project",
	"license": "AGPL",
	"authors": [
		{
			"name": "Vinicius Reis",
			"email": "luiz.vinicius73@gmail.com"
		},
		{
			"name": "Vitor Mattos",
			"email": "vitor@php.rio"
		}
	],
	"autoload": {
		"psr-4": {
			"OCA\\WifiPassword\\": "src/"
		}
	}
}

Info: fix the namespace

Enabling the app

# PRESENTING CODE
occ app:enable wifi_password

Quality assurance
phpcs

# PRESENTING CODE
composer require --dev nextcloud/coding-standard
"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"
}

Quality assurance
phpcs

# PRESENTING CODE
<?php

declare(strict_types=1);

require_once './vendor/autoload.php';

use Nextcloud\CodingStandard\Config;

$config = new Config();
$config
	->getFinder()
	->ignoreVCSIgnored(true)
	->notPath('build')
	->notPath('l10n')
	->notPath('src')
	->notPath('vendor')
	->in(__DIR__);
return $config;

.php-cs-fixer.dist.php

PS: add .php-cs-fixer.cache to .gitignore

Quality assurance
psalm

# PRESENTING CODE
composer require --dev vimeo/psalm
composer require --dev christophwurst/nextcloud:dev-master
"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",
	"psalm": "psalm"
}

Quality assurance

# PRESENTING CODE
<?xml version="1.0"?>
<psalm
	errorLevel="5"
	resolveFromConfigFile="true"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="https://getpsalm.org/schema/config"
	xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
>
	<projectFiles>
		<directory name="lib" />
		<ignoreFiles>
			<directory name="vendor" />
		</ignoreFiles>
	</projectFiles>
	<issueHandlers>
		<UndefinedClass>
			<errorLevel type="suppress">
				<referencedClass name="OC\*" />
				<referencedClass name="OC" />
			</errorLevel>
		</UndefinedClass>
	</issueHandlers>
</psalm>

psalm.xml

Quality assurance

# PRESENTING CODE
composer require --dev libresign/nextcloud-behat
vendor/bin/behat init

Behat

default:
  autoload:
    '': '%paths.base%/tests/features/bootstrap'
  suites:
    default:
      paths:
        - '%paths.base%/tests/features'
  extensions:
    PhpBuiltin\Server:
      verbose: false
      rootDir: /var/www/html
      host: localhost

behat.yml

Quality assurance

# PRESENTING CODE

Show me the code

Continuous Integration

# PRESENTING CODE

Show me the code

API endpoints

# PRESENTING CODE
return [
    'routes' => [
        ['name' => 'author#index', 'url' => '/authors', 'verb' => 'GET'],
        ['name' => 'author#show', 'url' => '/authors/{id}', 'verb' => 'GET'],
        ['name' => 'author#create', 'url' => '/authors', 'verb' => 'POST'],
        ['name' => 'author#update', 'url' => '/authors/{id}', 'verb' => 'PUT'],
        ['name' => 'author#destroy', 'url' => '/authors/{id}', 'verb' => 'DELETE'],
        // your other routes here
    ],
];

this...

API endpoints

# PRESENTING CODE
return [
    'resources' => [
        'author' => ['url' => '/authors'],
    ],
    'routes' => [
        // your other routes here
    ],
];

can be abbreviated by using the resources key:

First page
> appinfo/routes.php

# PRESENTING CODE
<?php

return [
	'routes' => [
		['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
	],
];

First page
> src/Controller/PageController.php

# PRESENTING CODE
<?php

declare(strict_types=1);

namespace OCA\WifiPassword\Controller;

use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\TemplateResponse;
use OCA\WifiPassword\AppInfo\Application;

class PageController extends Controller {
	/**
	 * @NoAdminRequired
	 * @NoCSRFRequired
	 */
	public function index(): TemplateResponse
	{
		return new TemplateResponse(Application::APP_ID, "page-main");
	}
}

First page
> templates/page-main.php

# PRESENTING CODE
Hello World

First page
Add to menu

# PRESENTING CODE
	...
	<dependencies>
		<nextcloud min-version="22" max-version="25" />
	</dependencies>
	<navigations>
		<navigation>
			<name>Wifi Password</name>
			<route>wifi_password.page.index</route>
		</navigation>
	</navigations>
</info>

First page
Add to menu

# PRESENTING CODE
img/app.svg
img/app-dark.svg

JavaScript
> js/page-main.js

# PRESENTING CODE

Nextcloud app tutorial

By Vitor Mattos

Nextcloud app tutorial

A simple Nextcloud app tutorial

  • 136