* uplift to latest expressive
This commit is contained in:
parent
b5c307166e
commit
8c5b58acd1
139
README.md
139
README.md
@ -1 +1,138 @@
|
|||||||
# Skies proxy api
|
# Expressive Skeleton and Installer
|
||||||
|
|
||||||
|
[](https://secure.travis-ci.org/zendframework/zend-expressive-skeleton)
|
||||||
|
[](https://coveralls.io/github/zendframework/zend-expressive-skeleton?branch=master)
|
||||||
|
|
||||||
|
*Begin developing PSR-7 middleware applications in seconds!*
|
||||||
|
|
||||||
|
[zend-expressive](https://github.com/zendframework/zend-expressive) builds on
|
||||||
|
[zend-stratigility](https://github.com/zendframework/zend-stratigility) to
|
||||||
|
provide a minimalist PSR-7 middleware framework for PHP with routing, DI
|
||||||
|
container, optional templating, and optional error handling capabilities.
|
||||||
|
|
||||||
|
This installer will setup a skeleton application based on zend-expressive by
|
||||||
|
choosing optional packages based on user input as demonstrated in the following
|
||||||
|
screenshot:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The user selected packages are saved into `composer.json` so that everyone else
|
||||||
|
working on the project have the same packages installed. Configuration files and
|
||||||
|
templates are prepared for first use. The installer command is removed from
|
||||||
|
`composer.json` after setup succeeded, and all installer related files are
|
||||||
|
removed.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
Start your new Expressive project with composer:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ composer create-project zendframework/zend-expressive-skeleton <project-path>
|
||||||
|
```
|
||||||
|
|
||||||
|
After choosing and installing the packages you want, go to the
|
||||||
|
`<project-path>` and start PHP's built-in web server to verify installation:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ composer run --timeout=0 serve
|
||||||
|
```
|
||||||
|
|
||||||
|
You can then browse to http://localhost:8080.
|
||||||
|
|
||||||
|
> ### Setting a timeout
|
||||||
|
>
|
||||||
|
> Composer commands time out after 300 seconds (5 minutes). On Linux-based
|
||||||
|
> systems, the `php -S` command that `composer serve` spawns continues running
|
||||||
|
> as a background process, but on other systems halts when the timeout occurs.
|
||||||
|
>
|
||||||
|
> As such, we recommend running the `serve` script using a timeout. This can
|
||||||
|
> be done by using `composer run` to execute the `serve` script, with a
|
||||||
|
> `--timeout` option. When set to `0`, as in the previous example, no timeout
|
||||||
|
> will be used, and it will run until you cancel the process (usually via
|
||||||
|
> `Ctrl-C`). Alternately, you can specify a finite timeout; as an example,
|
||||||
|
> the following will extend the timeout to a full day:
|
||||||
|
>
|
||||||
|
> ```bash
|
||||||
|
> $ composer run --timeout=86400 serve
|
||||||
|
> ```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
If the installer fails during the ``composer create-project`` phase, please go
|
||||||
|
through the following list before opening a new issue. Most issues we have seen
|
||||||
|
so far can be solved by `self-update` and `clear-cache`.
|
||||||
|
|
||||||
|
1. Be sure to work with the latest version of composer by running `composer self-update`.
|
||||||
|
2. Try clearing Composer's cache by running `composer clear-cache`.
|
||||||
|
|
||||||
|
If neither of the above help, you might face more serious issues:
|
||||||
|
|
||||||
|
- Info about the [zlib_decode error](https://github.com/composer/composer/issues/4121).
|
||||||
|
- Info and solutions for [composer degraded mode](https://getcomposer.org/doc/articles/troubleshooting.md#degraded-mode).
|
||||||
|
|
||||||
|
## Application Development Mode Tool
|
||||||
|
|
||||||
|
This skeleton comes with [zf-development-mode](https://github.com/zfcampus/zf-development-mode).
|
||||||
|
It provides a composer script to allow you to enable and disable development mode.
|
||||||
|
|
||||||
|
### To enable development mode
|
||||||
|
|
||||||
|
**Note:** Do NOT run development mode on your production server!
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ composer development-enable
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note:** Enabling development mode will also clear your configuration cache, to
|
||||||
|
allow safely updating dependencies and ensuring any new configuration is picked
|
||||||
|
up by your application.
|
||||||
|
|
||||||
|
### To disable development mode
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ composer development-disable
|
||||||
|
```
|
||||||
|
|
||||||
|
### Development mode status
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ composer development-status
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration caching
|
||||||
|
|
||||||
|
By default, the skeleton will create a configuration cache in
|
||||||
|
`data/config-cache.php`. When in development mode, the configuration cache is
|
||||||
|
disabled, and switching in and out of development mode will remove the
|
||||||
|
configuration cache.
|
||||||
|
|
||||||
|
You may need to clear the configuration cache in production when deploying if
|
||||||
|
you deploy to the same directory. You may do so using the following:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ composer clear-config-cache
|
||||||
|
```
|
||||||
|
|
||||||
|
You may also change the location of the configuration cache itself by editing
|
||||||
|
the `config/config.php` file and changing the `config_cache_path` entry of the
|
||||||
|
local `$cacheConfig` variable.
|
||||||
|
|
||||||
|
## Skeleton Development
|
||||||
|
|
||||||
|
This section applies only if you cloned this repo with `git clone`, not when you
|
||||||
|
installed expressive with `composer create-project ...`.
|
||||||
|
|
||||||
|
If you want to run tests against the installer, you need to clone this repo and
|
||||||
|
setup all dependencies with composer. Make sure you **prevent composer running
|
||||||
|
scripts** with `--no-scripts`, otherwise it will remove the installer and all
|
||||||
|
tests.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ composer update --no-scripts
|
||||||
|
$ composer test
|
||||||
|
```
|
||||||
|
|
||||||
|
Please note that the installer tests remove installed config files and templates
|
||||||
|
before and after running the tests.
|
||||||
|
|
||||||
|
Before contributing read [the contributing guide](CONTRIBUTING.md).
|
||||||
|
|||||||
46
bin/clear-config-cache.php
Normal file
46
bin/clear-config-cache.php
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Script for clearing the configuration cache.
|
||||||
|
*
|
||||||
|
* Can also be invoked as `composer clear-config-cache`.
|
||||||
|
*
|
||||||
|
* @see https://github.com/zendframework/zend-expressive-skeleton for the canonical source repository
|
||||||
|
* @copyright Copyright (c) 2017 Zend Technologies USA Inc. (http://www.zend.com)
|
||||||
|
* @license https://github.com/zendframework/zend-expressive-skeleton/blob/master/LICENSE.md New BSD License
|
||||||
|
*/
|
||||||
|
|
||||||
|
chdir(__DIR__ . '/../');
|
||||||
|
|
||||||
|
require 'vendor/autoload.php';
|
||||||
|
|
||||||
|
$config = include 'config/config.php';
|
||||||
|
|
||||||
|
if (! isset($config['config_cache_path'])) {
|
||||||
|
echo "No configuration cache path found" . PHP_EOL;
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! file_exists($config['config_cache_path'])) {
|
||||||
|
printf(
|
||||||
|
"Configured config cache file '%s' not found%s",
|
||||||
|
$config['config_cache_path'],
|
||||||
|
PHP_EOL
|
||||||
|
);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false === unlink($config['config_cache_path'])) {
|
||||||
|
printf(
|
||||||
|
"Error removing config cache file '%s'%s",
|
||||||
|
$config['config_cache_path'],
|
||||||
|
PHP_EOL
|
||||||
|
);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(
|
||||||
|
"Removed configured config cache file '%s'%s",
|
||||||
|
$config['config_cache_path'],
|
||||||
|
PHP_EOL
|
||||||
|
);
|
||||||
|
exit(0);
|
||||||
@ -4,21 +4,30 @@
|
|||||||
"type": "project",
|
"type": "project",
|
||||||
"homepage": "https://github.com/zendframework/zend-expressive-skeleton",
|
"homepage": "https://github.com/zendframework/zend-expressive-skeleton",
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
|
"config": {
|
||||||
|
"sort-packages": true
|
||||||
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^5.6 || ^7.0",
|
"php": "^7.1",
|
||||||
|
"doctrine/common": "2.9.x-dev",
|
||||||
|
"guzzlehttp/guzzle": "6.3.0",
|
||||||
|
"http-interop/http-middleware": "^0.4.1",
|
||||||
"roave/security-advisories": "dev-master",
|
"roave/security-advisories": "dev-master",
|
||||||
"zendframework/zend-expressive": "^1.0",
|
"zendframework/zend-component-installer": "^1.0",
|
||||||
"zendframework/zend-expressive-helpers": "^2.0",
|
"zendframework/zend-config-aggregator": "^1.0",
|
||||||
"zendframework/zend-stdlib": "^2.7 || ^3.0",
|
"zendframework/zend-dom": "2.6.0",
|
||||||
"zendframework/zend-expressive-fastroute": "^1.0",
|
"zendframework/zend-expressive": "^2.0.5",
|
||||||
"zendframework/zend-servicemanager": "^2.7.3 || ^3.0",
|
"zendframework/zend-expressive-fastroute": "^2.0",
|
||||||
"zendframework/zend-http": "^2.5",
|
"zendframework/zend-expressive-helpers": "^4.0",
|
||||||
"symfony/css-selector": "^3.2"
|
"zendframework/zend-json": "3.0.0",
|
||||||
|
"zendframework/zend-servicemanager": "^3.3",
|
||||||
|
"zendframework/zend-stdlib": "^3.1"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^4.8",
|
"phpunit/phpunit": "^6.0.8 || ^5.7.15",
|
||||||
"squizlabs/php_codesniffer": "^2.3",
|
"squizlabs/php_codesniffer": "^2.8.1",
|
||||||
"filp/whoops": "^1.1 || ^2.0"
|
"zfcampus/zf-development-mode": "^3.1",
|
||||||
|
"filp/whoops": "^2.1.7"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
@ -31,10 +40,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"post-create-project-cmd": [
|
||||||
|
"@development-enable"
|
||||||
|
],
|
||||||
|
"development-disable": "zf-development-mode disable",
|
||||||
|
"development-enable": "zf-development-mode enable",
|
||||||
|
"development-status": "zf-development-mode status",
|
||||||
"check": [
|
"check": [
|
||||||
"@cs-check",
|
"@cs-check",
|
||||||
"@test"
|
"@test"
|
||||||
],
|
],
|
||||||
|
"clear-config-cache": "php bin/clear-config-cache.php",
|
||||||
"cs-check": "phpcs",
|
"cs-check": "phpcs",
|
||||||
"cs-fix": "phpcbf",
|
"cs-fix": "phpcbf",
|
||||||
"serve": "php -S 0.0.0.0:8080 -t public public/index.php",
|
"serve": "php -S 0.0.0.0:8080 -t public public/index.php",
|
||||||
|
|||||||
2280
composer.lock
generated
2280
composer.lock
generated
File diff suppressed because it is too large
Load Diff
1
config/.gitignore
vendored
Normal file
1
config/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
development.config.php
|
||||||
@ -1,13 +1,21 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Zend\Expressive\Application;
|
use Zend\Expressive\Application;
|
||||||
use Zend\Expressive\Container\ApplicationFactory;
|
use Zend\Expressive\Container;
|
||||||
|
use Zend\Expressive\Delegate;
|
||||||
use Zend\Expressive\Helper;
|
use Zend\Expressive\Helper;
|
||||||
|
use Zend\Expressive\Middleware;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
// Provides application-wide services.
|
// Provides application-wide services.
|
||||||
// We recommend using fully-qualified class names whenever possible as
|
// We recommend using fully-qualified class names whenever possible as
|
||||||
// service names.
|
// service names.
|
||||||
'dependencies' => [
|
'dependencies' => [
|
||||||
|
// Use 'aliases' to alias a service name to another service. The
|
||||||
|
// key is the alias name, the value is the service to which it points.
|
||||||
|
'aliases' => [
|
||||||
|
'Zend\Expressive\Delegate\DefaultDelegate' => Delegate\NotFoundDelegate::class,
|
||||||
|
],
|
||||||
// Use 'invokables' for constructor-less services, or services that do
|
// Use 'invokables' for constructor-less services, or services that do
|
||||||
// not require arguments to the constructor. Map a service name to the
|
// not require arguments to the constructor. Map a service name to the
|
||||||
// class name.
|
// class name.
|
||||||
@ -16,10 +24,16 @@ return [
|
|||||||
Helper\ServerUrlHelper::class => Helper\ServerUrlHelper::class,
|
Helper\ServerUrlHelper::class => Helper\ServerUrlHelper::class,
|
||||||
],
|
],
|
||||||
// Use 'factories' for services provided by callbacks/factory classes.
|
// Use 'factories' for services provided by callbacks/factory classes.
|
||||||
'factories' => [
|
'factories' => [
|
||||||
Application::class => ApplicationFactory::class,
|
Application::class => Container\ApplicationFactory::class,
|
||||||
Helper\UrlHelper::class => Helper\UrlHelperFactory::class,
|
Delegate\NotFoundDelegate::class => Container\NotFoundDelegateFactory::class,
|
||||||
\App\Service\SkiesService::class => \App\Service\SkiesServiceFactory::class,
|
Helper\ServerUrlMiddleware::class => Helper\ServerUrlMiddlewareFactory::class,
|
||||||
|
Helper\UrlHelper::class => Helper\UrlHelperFactory::class,
|
||||||
|
Helper\UrlHelperMiddleware::class => Helper\UrlHelperMiddlewareFactory::class,
|
||||||
|
|
||||||
|
Zend\Stratigility\Middleware\ErrorHandler::class => Container\ErrorHandlerFactory::class,
|
||||||
|
Middleware\ErrorResponseGenerator::class => Container\ErrorResponseGeneratorFactory::class,
|
||||||
|
Middleware\NotFoundHandler::class => Container\NotFoundHandlerFactory::class,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|||||||
34
config/autoload/development.local.php.dist
Normal file
34
config/autoload/development.local.php.dist
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Development-only configuration.
|
||||||
|
*
|
||||||
|
* Put settings you want enabled when under development mode in this file, and
|
||||||
|
* check it into your repository.
|
||||||
|
*
|
||||||
|
* Developers on your team will then automatically enable them by calling on
|
||||||
|
* `composer development-enable`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Zend\Expressive\Container;
|
||||||
|
use Zend\Expressive\Middleware\ErrorResponseGenerator;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'dependencies' => [
|
||||||
|
'invokables' => [
|
||||||
|
],
|
||||||
|
'factories' => [
|
||||||
|
ErrorResponseGenerator::class => Container\WhoopsErrorResponseGeneratorFactory::class,
|
||||||
|
'Zend\Expressive\Whoops' => Container\WhoopsFactory::class,
|
||||||
|
'Zend\Expressive\WhoopsPageHandler' => Container\WhoopsPageHandlerFactory::class,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
'whoops' => [
|
||||||
|
'json_exceptions' => [
|
||||||
|
'display' => true,
|
||||||
|
'show_trace' => true,
|
||||||
|
'ajax_only' => true,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
@ -1,7 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
return [
|
|
||||||
'authKey' => '',
|
|
||||||
'debug' => true,
|
|
||||||
'config_cache_enabled' => false,
|
|
||||||
];
|
|
||||||
11
config/autoload/local.php.dist
Normal file
11
config/autoload/local.php.dist
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local configuration.
|
||||||
|
*
|
||||||
|
* Copy this file to `local.php` and change its settings as required.
|
||||||
|
* `local.php` is ignored by git and safe to use for local and sensitive data like usernames and passwords.
|
||||||
|
*/
|
||||||
|
|
||||||
|
return [
|
||||||
|
];
|
||||||
@ -1,69 +0,0 @@
|
|||||||
<?php
|
|
||||||
use Zend\Expressive\Container\ApplicationFactory;
|
|
||||||
use Zend\Expressive\Helper;
|
|
||||||
|
|
||||||
return [
|
|
||||||
'dependencies' => [
|
|
||||||
'factories' => [
|
|
||||||
Helper\ServerUrlMiddleware::class => Helper\ServerUrlMiddlewareFactory::class,
|
|
||||||
Helper\UrlHelperMiddleware::class => Helper\UrlHelperMiddlewareFactory::class,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
// This can be used to seed pre- and/or post-routing middleware
|
|
||||||
'middleware_pipeline' => [
|
|
||||||
// An array of middleware to register. Each item is of the following
|
|
||||||
// specification:
|
|
||||||
//
|
|
||||||
// [
|
|
||||||
// Required:
|
|
||||||
// 'middleware' => 'Name or array of names of middleware services and/or callables',
|
|
||||||
// Optional:
|
|
||||||
// 'path' => '/path/to/match', // string; literal path prefix to match
|
|
||||||
// // middleware will not execute
|
|
||||||
// // if path does not match!
|
|
||||||
// 'error' => true, // boolean; true for error middleware
|
|
||||||
// 'priority' => 1, // int; higher values == register early;
|
|
||||||
// // lower/negative == register last;
|
|
||||||
// // default is 1, if none is provided.
|
|
||||||
// ],
|
|
||||||
//
|
|
||||||
// While the ApplicationFactory ignores the keys associated with
|
|
||||||
// specifications, they can be used to allow merging related values
|
|
||||||
// defined in multiple configuration files/locations. This file defines
|
|
||||||
// some conventional keys for middleware to execute early, routing
|
|
||||||
// middleware, and error middleware.
|
|
||||||
'always' => [
|
|
||||||
'middleware' => [
|
|
||||||
// Add more middleware here that you want to execute on
|
|
||||||
// every request:
|
|
||||||
// - bootstrapping
|
|
||||||
// - pre-conditions
|
|
||||||
// - modifications to outgoing responses
|
|
||||||
Helper\ServerUrlMiddleware::class,
|
|
||||||
],
|
|
||||||
'priority' => 10000,
|
|
||||||
],
|
|
||||||
|
|
||||||
'routing' => [
|
|
||||||
'middleware' => [
|
|
||||||
ApplicationFactory::ROUTING_MIDDLEWARE,
|
|
||||||
Helper\UrlHelperMiddleware::class,
|
|
||||||
// Add more middleware here that needs to introspect the routing
|
|
||||||
// results; this might include:
|
|
||||||
// - route-based authentication
|
|
||||||
// - route-based validation
|
|
||||||
// - etc.
|
|
||||||
ApplicationFactory::DISPATCH_MIDDLEWARE,
|
|
||||||
],
|
|
||||||
'priority' => 1,
|
|
||||||
],
|
|
||||||
|
|
||||||
'error' => [
|
|
||||||
'middleware' => [
|
|
||||||
// Add error middleware here.
|
|
||||||
],
|
|
||||||
'error' => true,
|
|
||||||
'priority' => -10000,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
12
config/autoload/router.global.php
Normal file
12
config/autoload/router.global.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Zend\Expressive\Router\FastRouteRouter;
|
||||||
|
use Zend\Expressive\Router\RouterInterface;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'dependencies' => [
|
||||||
|
'invokables' => [
|
||||||
|
RouterInterface::class => FastRouteRouter::class,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
@ -1,41 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
return [
|
|
||||||
'dependencies' => [
|
|
||||||
'invokables' => [
|
|
||||||
Zend\Expressive\Router\RouterInterface::class => Zend\Expressive\Router\FastRouteRouter::class,
|
|
||||||
App\Action\PingAction::class => App\Action\PingAction::class,
|
|
||||||
],
|
|
||||||
'factories' => [
|
|
||||||
App\Action\HomePageAction::class => App\Action\HomePageFactory::class,
|
|
||||||
App\Action\ActivityAction::class => App\Action\ActivityFactory::class,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
|
|
||||||
'routes' => [
|
|
||||||
[
|
|
||||||
'name' => 'home',
|
|
||||||
'path' => '/',
|
|
||||||
'middleware' => App\Action\HomePageAction::class,
|
|
||||||
'allowed_methods' => ['GET'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'api.ping',
|
|
||||||
'path' => '/api/ping',
|
|
||||||
'middleware' => App\Action\PingAction::class,
|
|
||||||
'allowed_methods' => ['GET'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'api.activity',
|
|
||||||
'path' => '/api/activity',
|
|
||||||
'middleware' => App\Action\ActivityAction::class,
|
|
||||||
'allowed_methods' => ['GET', 'POST', 'DELETE', 'OPTIONS'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'api.activity.id',
|
|
||||||
'path' => '/api/activity/{id:\d+}',
|
|
||||||
'middleware' => App\Action\ActivityAction::class,
|
|
||||||
'allowed_methods' => ['GET', 'PUT', 'DELETE', 'OPTIONS'],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
@ -1,11 +1,24 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Zend\ConfigAggregator\ConfigAggregator;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
// Toggle the configuration cache. Set this to boolean false, or remove the
|
||||||
|
// directive, to disable configuration caching. Toggling development mode
|
||||||
|
// will also disable it by default; clear the configuration cache using
|
||||||
|
// `composer clear-config-cache`.
|
||||||
|
ConfigAggregator::ENABLE_CACHE => true,
|
||||||
|
|
||||||
|
// Enable debugging; typically used to provide debugging information within templates.
|
||||||
'debug' => false,
|
'debug' => false,
|
||||||
|
|
||||||
'config_cache_enabled' => false,
|
|
||||||
|
|
||||||
'zend-expressive' => [
|
'zend-expressive' => [
|
||||||
|
// Enable programmatic pipeline: Any `middleware_pipeline` or `routes`
|
||||||
|
// configuration will be ignored when creating the `Application` instance.
|
||||||
|
'programmatic_pipeline' => true,
|
||||||
|
|
||||||
|
// Provide templates for the error handling middleware to use when
|
||||||
|
// generating responses.
|
||||||
'error_handler' => [
|
'error_handler' => [
|
||||||
'template_404' => 'error::404',
|
'template_404' => 'error::404',
|
||||||
'template_error' => 'error::error',
|
'template_error' => 'error::error',
|
||||||
|
|||||||
@ -1,35 +1,32 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Zend\Stdlib\ArrayUtils;
|
use Zend\ConfigAggregator\ArrayProvider;
|
||||||
use Zend\Stdlib\Glob;
|
use Zend\ConfigAggregator\ConfigAggregator;
|
||||||
|
use Zend\ConfigAggregator\PhpFileProvider;
|
||||||
|
|
||||||
/**
|
// To enable or disable caching, set the `ConfigAggregator::ENABLE_CACHE` boolean in
|
||||||
* Configuration files are loaded in a specific order. First ``global.php``, then ``*.global.php``.
|
// `config/autoload/local.php`.
|
||||||
* then ``local.php`` and finally ``*.local.php``. This way local settings overwrite global settings.
|
$cacheConfig = [
|
||||||
*
|
'config_cache_path' => 'data/config-cache.php',
|
||||||
* The configuration can be cached. This can be done by setting ``config_cache_enabled`` to ``true``.
|
];
|
||||||
*
|
|
||||||
* Obviously, if you use closures in your config you can't cache it.
|
|
||||||
*/
|
|
||||||
|
|
||||||
$cachedConfigFile = 'data/cache/app_config.php';
|
$aggregator = new ConfigAggregator([
|
||||||
|
// Include cache configuration
|
||||||
|
new ArrayProvider($cacheConfig),
|
||||||
|
|
||||||
$config = [];
|
// Default App module config
|
||||||
if (is_file($cachedConfigFile)) {
|
App\ConfigProvider::class,
|
||||||
// Try to load the cached config
|
|
||||||
$config = include $cachedConfigFile;
|
|
||||||
} else {
|
|
||||||
// Load configuration from autoload path
|
|
||||||
foreach (Glob::glob('config/autoload/{{,*.}global,{,*.}local}.php', Glob::GLOB_BRACE) as $file) {
|
|
||||||
$config = ArrayUtils::merge($config, include $file);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cache config if enabled
|
// Load application config in a pre-defined order in such a way that local settings
|
||||||
if (isset($config['config_cache_enabled']) && $config['config_cache_enabled'] === true) {
|
// overwrite global settings. (Loaded as first to last):
|
||||||
file_put_contents($cachedConfigFile, '<?php return ' . var_export($config, true) . ';');
|
// - `global.php`
|
||||||
}
|
// - `*.global.php`
|
||||||
}
|
// - `local.php`
|
||||||
|
// - `*.local.php`
|
||||||
|
new PhpFileProvider('config/autoload/{{,*.}global,{,*.}local}.php'),
|
||||||
|
|
||||||
// Return an ArrayObject so we can inject the config as a service in Aura.Di
|
// Load development config if it exists
|
||||||
// and still use array checks like ``is_array``.
|
new PhpFileProvider('config/development.config.php'),
|
||||||
return new ArrayObject($config, ArrayObject::ARRAY_AS_PROPS);
|
], $cacheConfig['config_cache_path']);
|
||||||
|
|
||||||
|
return $aggregator->getMergedConfig();
|
||||||
|
|||||||
29
config/development.config.php.dist
Normal file
29
config/development.config.php.dist
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File required to allow enablement of development mode.
|
||||||
|
*
|
||||||
|
* For use with the zf-development-mode tool.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* $ composer development-disable
|
||||||
|
* $ composer development-enable
|
||||||
|
* $ composer development-status
|
||||||
|
*
|
||||||
|
* DO NOT MODIFY THIS FILE.
|
||||||
|
*
|
||||||
|
* Provide your own development-mode settings by editing the file
|
||||||
|
* `config/autoload/development.local.php.dist`.
|
||||||
|
*
|
||||||
|
* Because this file is aggregated last, it simply ensures:
|
||||||
|
*
|
||||||
|
* - The `debug` flag is _enabled_.
|
||||||
|
* - Configuration caching is _disabled_.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Zend\ConfigAggregator\ConfigAggregator;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'debug' => true,
|
||||||
|
ConfigAggregator::ENABLE_CACHE => false,
|
||||||
|
];
|
||||||
55
config/pipeline.php
Normal file
55
config/pipeline.php
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Zend\Expressive\Helper\ServerUrlMiddleware;
|
||||||
|
use Zend\Expressive\Helper\UrlHelperMiddleware;
|
||||||
|
use Zend\Expressive\Middleware\ImplicitHeadMiddleware;
|
||||||
|
use Zend\Expressive\Middleware\ImplicitOptionsMiddleware;
|
||||||
|
use Zend\Expressive\Middleware\NotFoundHandler;
|
||||||
|
use Zend\Stratigility\Middleware\ErrorHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup middleware pipeline:
|
||||||
|
*/
|
||||||
|
|
||||||
|
// The error handler should be the first (most outer) middleware to catch
|
||||||
|
// all Exceptions.
|
||||||
|
$app->pipe(ErrorHandler::class);
|
||||||
|
$app->pipe(ServerUrlMiddleware::class);
|
||||||
|
|
||||||
|
// Pipe more middleware here that you want to execute on every request:
|
||||||
|
// - bootstrapping
|
||||||
|
// - pre-conditions
|
||||||
|
// - modifications to outgoing responses
|
||||||
|
//
|
||||||
|
// Piped Middleware may be either callables or service names. Middleware may
|
||||||
|
// also be passed as an array; each item in the array must resolve to
|
||||||
|
// middleware eventually (i.e., callable or service name).
|
||||||
|
//
|
||||||
|
// Middleware can be attached to specific paths, allowing you to mix and match
|
||||||
|
// applications under a common domain. The handlers in each middleware
|
||||||
|
// attached this way will see a URI with the MATCHED PATH SEGMENT REMOVED!!!
|
||||||
|
//
|
||||||
|
// - $app->pipe('/api', $apiMiddleware);
|
||||||
|
// - $app->pipe('/docs', $apiDocMiddleware);
|
||||||
|
// - $app->pipe('/files', $filesMiddleware);
|
||||||
|
|
||||||
|
// Register the routing middleware in the middleware pipeline
|
||||||
|
$app->pipeRoutingMiddleware();
|
||||||
|
$app->pipe(ImplicitHeadMiddleware::class);
|
||||||
|
$app->pipe(ImplicitOptionsMiddleware::class);
|
||||||
|
$app->pipe(UrlHelperMiddleware::class);
|
||||||
|
|
||||||
|
// Add more middleware here that needs to introspect the routing results; this
|
||||||
|
// might include:
|
||||||
|
//
|
||||||
|
// - route-based authentication
|
||||||
|
// - route-based validation
|
||||||
|
// - etc.
|
||||||
|
|
||||||
|
// Register the dispatch middleware in the middleware pipeline
|
||||||
|
$app->pipeDispatchMiddleware();
|
||||||
|
|
||||||
|
// At this point, if no Response is return by any middleware, the
|
||||||
|
// NotFoundHandler kicks in; alternately, you can provide other fallback
|
||||||
|
// middleware to execute.
|
||||||
|
$app->pipe(NotFoundHandler::class);
|
||||||
34
config/routes.php
Normal file
34
config/routes.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Setup routes with a single request method:
|
||||||
|
*
|
||||||
|
* $app->get('/', App\Action\HomePageAction::class, 'home');
|
||||||
|
* $app->post('/album', App\Action\AlbumCreateAction::class, 'album.create');
|
||||||
|
* $app->put('/album/:id', App\Action\AlbumUpdateAction::class, 'album.put');
|
||||||
|
* $app->patch('/album/:id', App\Action\AlbumUpdateAction::class, 'album.patch');
|
||||||
|
* $app->delete('/album/:id', App\Action\AlbumDeleteAction::class, 'album.delete');
|
||||||
|
*
|
||||||
|
* Or with multiple request methods:
|
||||||
|
*
|
||||||
|
* $app->route('/contact', App\Action\ContactAction::class, ['GET', 'POST', ...], 'contact');
|
||||||
|
*
|
||||||
|
* Or handling all request methods:
|
||||||
|
*
|
||||||
|
* $app->route('/contact', App\Action\ContactAction::class)->setName('contact');
|
||||||
|
*
|
||||||
|
* or:
|
||||||
|
*
|
||||||
|
* $app->route(
|
||||||
|
* '/contact',
|
||||||
|
* App\Action\ContactAction::class,
|
||||||
|
* Zend\Expressive\Router\Route::HTTP_METHOD_ANY,
|
||||||
|
* 'contact'
|
||||||
|
* );
|
||||||
|
*/
|
||||||
|
|
||||||
|
$app->get('/', App\Action\HomePageAction::class, 'home');
|
||||||
|
$app->get('/api/ping', App\Action\PingAction::class, 'api.ping');
|
||||||
|
|
||||||
|
$app->get('/api/activity[/{id:\d+}]', App\Action\ActivityAction::class, 'api.activity.get');
|
||||||
|
$app->get('/api/activity/signup/{id:\d+}', App\Action\ActivitySignupAction::class, 'api.activity.signup');
|
||||||
|
$app->get('/api/activity/signoff/{id:\d+}', App\Action\ActivitySignoffAction::class, 'api.activity.signoff');
|
||||||
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<ruleset name="Zend Framework coding standard">
|
<ruleset name="Expressive Skeleton coding standard">
|
||||||
<description>Zend Framework coding standard</description>
|
<description>Expressive Skeleton coding standard</description>
|
||||||
|
|
||||||
<!-- display progress -->
|
<!-- display progress -->
|
||||||
<arg value="p"/>
|
<arg value="p"/>
|
||||||
@ -10,9 +10,20 @@ if (php_sapi_name() === 'cli-server'
|
|||||||
chdir(dirname(__DIR__));
|
chdir(dirname(__DIR__));
|
||||||
require 'vendor/autoload.php';
|
require 'vendor/autoload.php';
|
||||||
|
|
||||||
/** @var \Interop\Container\ContainerInterface $container */
|
/**
|
||||||
$container = require 'config/container.php';
|
* Self-called anonymous function that creates its own scope and keep the global namespace clean.
|
||||||
|
*/
|
||||||
|
call_user_func(function () {
|
||||||
|
/** @var \Interop\Container\ContainerInterface $container */
|
||||||
|
$container = require 'config/container.php';
|
||||||
|
|
||||||
/** @var \Zend\Expressive\Application $app */
|
/** @var \Zend\Expressive\Application $app */
|
||||||
$app = $container->get(\Zend\Expressive\Application::class);
|
$app = $container->get(\Zend\Expressive\Application::class);
|
||||||
$app->run();
|
|
||||||
|
// Import programmatic/declarative middleware pipeline and routing
|
||||||
|
// configuration statements
|
||||||
|
require 'config/pipeline.php';
|
||||||
|
require 'config/routes.php';
|
||||||
|
|
||||||
|
$app->run();
|
||||||
|
});
|
||||||
|
|||||||
@ -2,16 +2,17 @@
|
|||||||
|
|
||||||
namespace App\Action;
|
namespace App\Action;
|
||||||
|
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||||
|
use Interop\Http\ServerMiddleware\MiddlewareInterface as ServerMiddlewareInterface;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
use Zend\Diactoros\Response\JsonResponse;
|
use Zend\Diactoros\Response\JsonResponse;
|
||||||
use Zend\Stratigility\MiddlewareInterface;
|
use Zend\Json\Json;
|
||||||
|
|
||||||
abstract class AbstractAction implements MiddlewareInterface
|
abstract class AbstractAction implements ServerMiddlewareInterface
|
||||||
{
|
{
|
||||||
const IDENTIFIER_NAME = 'id';
|
const IDENTIFIER_NAME = 'id';
|
||||||
|
|
||||||
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
|
public function process(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||||
{
|
{
|
||||||
$requestMethod = strtoupper($request->getMethod());
|
$requestMethod = strtoupper($request->getMethod());
|
||||||
$id = $request->getAttribute(static::IDENTIFIER_NAME);
|
$id = $request->getAttribute(static::IDENTIFIER_NAME);
|
||||||
@ -19,68 +20,68 @@ abstract class AbstractAction implements MiddlewareInterface
|
|||||||
switch ($requestMethod) {
|
switch ($requestMethod) {
|
||||||
case 'GET':
|
case 'GET':
|
||||||
return isset($id)
|
return isset($id)
|
||||||
? $this->get($request, $response, $next)
|
? $this->get($request, $delegate)
|
||||||
: $this->getList($request, $response, $next);
|
: $this->getList($request, $delegate);
|
||||||
case 'POST':
|
case 'POST':
|
||||||
return $this->create($request, $response, $next);
|
return $this->create($request, $delegate);
|
||||||
case 'PUT':
|
case 'PUT':
|
||||||
return $this->update($request, $response, $next);
|
return $this->update($request, $delegate);
|
||||||
case 'DELETE':
|
case 'DELETE':
|
||||||
return isset($id)
|
return isset($id)
|
||||||
? $this->delete($request, $response, $next)
|
? $this->delete($request, $delegate)
|
||||||
: $this->deleteList($request, $response, $next);
|
: $this->deleteList($request, $delegate);
|
||||||
case 'HEAD':
|
case 'HEAD':
|
||||||
return $this->head($request, $response, $next);
|
return $this->head($request, $delegate);
|
||||||
case 'OPTIONS':
|
case 'OPTIONS':
|
||||||
return $this->options($request, $response, $next);
|
return $this->options($request, $delegate);
|
||||||
case 'PATCH':
|
case 'PATCH':
|
||||||
return $this->patch($request, $response, $next);
|
return $this->patch($request, $delegate);
|
||||||
default:
|
default:
|
||||||
return $next($request, $response);
|
return $delegate->process($request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
|
public function get(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||||
{
|
{
|
||||||
return $this->createResponse(['content' => 'Method not allowed'], 405);
|
return $this->createResponse(['content' => 'Method not allowed'], 405);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getList(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
|
public function getList(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||||
{
|
{
|
||||||
return $this->createResponse(['content' => 'Method not allowed'], 405);
|
return $this->createResponse(['content' => 'Method not allowed'], 405);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function create(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
|
public function create(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||||
{
|
{
|
||||||
return $this->createResponse(['content' => 'Method not allowed'], 405);
|
return $this->createResponse(['content' => 'Method not allowed'], 405);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
|
public function update(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||||
{
|
{
|
||||||
return $this->createResponse(['content' => 'Method not allowed'], 405);
|
return $this->createResponse(['content' => 'Method not allowed'], 405);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
|
public function delete(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||||
{
|
{
|
||||||
return $this->createResponse(['content' => 'Method not allowed'], 405);
|
return $this->createResponse(['content' => 'Method not allowed'], 405);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function deleteList(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
|
public function deleteList(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||||
{
|
{
|
||||||
return $this->createResponse(['content' => 'Method not allowed'], 405);
|
return $this->createResponse(['content' => 'Method not allowed'], 405);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function head(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
|
public function head(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||||
{
|
{
|
||||||
return $this->createResponse(['content' => 'Method not allowed'], 405);
|
return $this->createResponse(['content' => 'Method not allowed'], 405);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function options(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
|
public function options(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||||
{
|
{
|
||||||
return $this->createResponse(['content' => 'Method not allowed'], 405);
|
return $this->createResponse(['content' => 'Method not allowed'], 405);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function patch(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
|
public function patch(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||||
{
|
{
|
||||||
return $this->createResponse(['content' => 'Method not allowed'], 405);
|
return $this->createResponse(['content' => 'Method not allowed'], 405);
|
||||||
}
|
}
|
||||||
@ -89,4 +90,69 @@ abstract class AbstractAction implements MiddlewareInterface
|
|||||||
{
|
{
|
||||||
return new JsonResponse($data, $status);
|
return new JsonResponse($data, $status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
* @return array|object
|
||||||
|
*/
|
||||||
|
public function getRequestData(ServerRequestInterface $request)
|
||||||
|
{
|
||||||
|
$body = $request->getParsedBody();
|
||||||
|
|
||||||
|
if (!empty($body)) {
|
||||||
|
return $body;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->parseRequestData(
|
||||||
|
$request->getBody()->getContents(),
|
||||||
|
$request->getHeaderLine('content-type')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param string $input
|
||||||
|
* @param string $contentType
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
private function parseRequestData($input, $contentType)
|
||||||
|
{
|
||||||
|
$contentTypeParts = preg_split('/\s*[;,]\s*/', $contentType);
|
||||||
|
$parser = $this->returnParserContentType($contentTypeParts[0]);
|
||||||
|
|
||||||
|
return $parser($input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param string $contentType
|
||||||
|
* @return callable
|
||||||
|
*/
|
||||||
|
private function returnParserContentType(string $contentType)
|
||||||
|
{
|
||||||
|
if ($contentType === 'application/x-www-form-urlencoded') {
|
||||||
|
return function ($input) {
|
||||||
|
parse_str($input, $data);
|
||||||
|
return $data;
|
||||||
|
};
|
||||||
|
} elseif ($contentType === 'application/json') {
|
||||||
|
return function ($input) {
|
||||||
|
$jsonDecoder = new Json();
|
||||||
|
try {
|
||||||
|
return $jsonDecoder->decode($input, Json::TYPE_ARRAY);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} elseif ($contentType === 'multipart/form-data') {
|
||||||
|
return function ($input) {
|
||||||
|
return $input;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return function ($input) {
|
||||||
|
return $input;
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,33 +2,34 @@
|
|||||||
|
|
||||||
namespace App\Action;
|
namespace App\Action;
|
||||||
|
|
||||||
use App\Service\SkiesService;
|
use App\Service\SkiesClientService;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
use Zend\Diactoros\Response\JsonResponse;
|
use Zend\Diactoros\Response\JsonResponse;
|
||||||
|
use Zend\Diactoros\Response\TextResponse;
|
||||||
|
|
||||||
class ActivityAction extends AbstractAction
|
class ActivityAction extends AbstractAction
|
||||||
{
|
{
|
||||||
private $skiesService;
|
/**
|
||||||
|
* @var SkiesClientService
|
||||||
|
*/
|
||||||
|
private $skiesClient;
|
||||||
|
|
||||||
public function __construct(SkiesService $skiesService)
|
public function __construct(SkiesClientService $skiesClient)
|
||||||
{
|
{
|
||||||
$this->skiesService = $skiesService;
|
$this->skiesClient = $skiesClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getList(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
|
public function getList(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||||
{
|
{
|
||||||
return new JsonResponse($this->skiesService->getActivityList());
|
$authHeader = $request->getHeaderLine("x-passthru-auth");
|
||||||
|
return new JsonResponse($this->skiesClient->setAuthHeader($authHeader)->getActivities());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
|
public function get(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||||
{
|
{
|
||||||
$id = $request->getAttribute(self::IDENTIFIER_NAME);
|
$id = $request->getAttribute(self::IDENTIFIER_NAME);
|
||||||
return new JsonResponse($this->skiesService->getActivity($id));
|
$authHeader = $request->getHeaderLine("x-passthru-auth");
|
||||||
}
|
return new JsonResponse($this->skiesClient->setAuthHeader($authHeader)->getActivity($id));
|
||||||
|
|
||||||
public function options(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
|
|
||||||
{
|
|
||||||
return new JsonResponse(true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,14 +2,14 @@
|
|||||||
|
|
||||||
namespace App\Action;
|
namespace App\Action;
|
||||||
|
|
||||||
use App\Service\SkiesService;
|
use App\Service\SkiesClientService;
|
||||||
use Interop\Container\ContainerInterface;
|
use Interop\Container\ContainerInterface;
|
||||||
|
|
||||||
class ActivityFactory
|
class ActivityFactory
|
||||||
{
|
{
|
||||||
public function __invoke(ContainerInterface $container)
|
public function __invoke(ContainerInterface $container)
|
||||||
{
|
{
|
||||||
$skiesService = $container->get(SkiesService::class);
|
$skiesClient = $container->get(SkiesClientService::class);
|
||||||
return new ActivityAction($skiesService);
|
return new ActivityAction($skiesClient);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
29
src/App/Action/ActivitySignoffAction.php
Normal file
29
src/App/Action/ActivitySignoffAction.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Action;
|
||||||
|
|
||||||
|
use App\Service\SkiesClientService;
|
||||||
|
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||||
|
use Interop\Http\ServerMiddleware\MiddlewareInterface as ServerMiddlewareInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Zend\Diactoros\Response\JsonResponse;
|
||||||
|
|
||||||
|
class ActivitySignoffAction implements ServerMiddlewareInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var SkiesClientService
|
||||||
|
*/
|
||||||
|
private $skiesClient;
|
||||||
|
|
||||||
|
public function __construct(SkiesClientService $skiesClient)
|
||||||
|
{
|
||||||
|
$this->skiesClient = $skiesClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||||
|
{
|
||||||
|
$authHeader = $request->getHeaderLine("x-passthru-auth");
|
||||||
|
$id = $request->getAttribute("id");
|
||||||
|
return new JsonResponse($this->skiesClient->setAuthHeader($authHeader)->signOffActivity($id));
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/App/Action/ActivitySignoffFactory.php
Normal file
15
src/App/Action/ActivitySignoffFactory.php
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Action;
|
||||||
|
|
||||||
|
use App\Service\SkiesClientService;
|
||||||
|
use Interop\Container\ContainerInterface;
|
||||||
|
|
||||||
|
class ActivitySignoffFactory
|
||||||
|
{
|
||||||
|
public function __invoke(ContainerInterface $container)
|
||||||
|
{
|
||||||
|
$skiesClient = $container->get(SkiesClientService::class);
|
||||||
|
return new ActivitySignoffAction($skiesClient);
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/App/Action/ActivitySignupAction.php
Normal file
29
src/App/Action/ActivitySignupAction.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Action;
|
||||||
|
|
||||||
|
use App\Service\SkiesClientService;
|
||||||
|
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||||
|
use Interop\Http\ServerMiddleware\MiddlewareInterface as ServerMiddlewareInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Zend\Diactoros\Response\JsonResponse;
|
||||||
|
|
||||||
|
class ActivitySignupAction implements ServerMiddlewareInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var SkiesClientService
|
||||||
|
*/
|
||||||
|
private $skiesClient;
|
||||||
|
|
||||||
|
public function __construct(SkiesClientService $skiesClient)
|
||||||
|
{
|
||||||
|
$this->skiesClient = $skiesClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||||
|
{
|
||||||
|
$authHeader = $request->getHeaderLine("x-passthru-auth");
|
||||||
|
$id = $request->getAttribute("id");
|
||||||
|
return new JsonResponse($this->skiesClient->setAuthHeader($authHeader)->signUpActivity($id));
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/App/Action/ActivitySignupFactory.php
Normal file
15
src/App/Action/ActivitySignupFactory.php
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Action;
|
||||||
|
|
||||||
|
use App\Service\SkiesClientService;
|
||||||
|
use Interop\Container\ContainerInterface;
|
||||||
|
|
||||||
|
class ActivitySignupFactory
|
||||||
|
{
|
||||||
|
public function __invoke(ContainerInterface $container)
|
||||||
|
{
|
||||||
|
$skiesClient = $container->get(SkiesClientService::class);
|
||||||
|
return new ActivitySignupAction($skiesClient);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
namespace App\Action;
|
namespace App\Action;
|
||||||
|
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||||
|
use Interop\Http\ServerMiddleware\MiddlewareInterface as ServerMiddlewareInterface;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
use Zend\Diactoros\Response\HtmlResponse;
|
use Zend\Diactoros\Response\HtmlResponse;
|
||||||
use Zend\Diactoros\Response\JsonResponse;
|
use Zend\Diactoros\Response\JsonResponse;
|
||||||
@ -12,7 +13,7 @@ use Zend\Expressive\Plates\PlatesRenderer;
|
|||||||
use Zend\Expressive\Twig\TwigRenderer;
|
use Zend\Expressive\Twig\TwigRenderer;
|
||||||
use Zend\Expressive\ZendView\ZendViewRenderer;
|
use Zend\Expressive\ZendView\ZendViewRenderer;
|
||||||
|
|
||||||
class HomePageAction
|
class HomePageAction implements ServerMiddlewareInterface
|
||||||
{
|
{
|
||||||
private $router;
|
private $router;
|
||||||
|
|
||||||
@ -24,8 +25,15 @@ class HomePageAction
|
|||||||
$this->template = $template;
|
$this->template = $template;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
|
public function process(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||||
{
|
{
|
||||||
|
if (! $this->template) {
|
||||||
|
return new JsonResponse([
|
||||||
|
'welcome' => 'Congratulations! You have installed the zend-expressive skeleton application.',
|
||||||
|
'docsUrl' => 'https://docs.zendframework.com/zend-expressive/',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
$data = [];
|
$data = [];
|
||||||
|
|
||||||
if ($this->router instanceof Router\AuraRouter) {
|
if ($this->router instanceof Router\AuraRouter) {
|
||||||
@ -36,7 +44,7 @@ class HomePageAction
|
|||||||
$data['routerDocs'] = 'https://github.com/nikic/FastRoute';
|
$data['routerDocs'] = 'https://github.com/nikic/FastRoute';
|
||||||
} elseif ($this->router instanceof Router\ZendRouter) {
|
} elseif ($this->router instanceof Router\ZendRouter) {
|
||||||
$data['routerName'] = 'Zend Router';
|
$data['routerName'] = 'Zend Router';
|
||||||
$data['routerDocs'] = 'http://framework.zend.com/manual/current/en/modules/zend.mvc.routing.html';
|
$data['routerDocs'] = 'https://docs.zendframework.com/zend-router/';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->template instanceof PlatesRenderer) {
|
if ($this->template instanceof PlatesRenderer) {
|
||||||
@ -47,14 +55,7 @@ class HomePageAction
|
|||||||
$data['templateDocs'] = 'http://twig.sensiolabs.org/documentation';
|
$data['templateDocs'] = 'http://twig.sensiolabs.org/documentation';
|
||||||
} elseif ($this->template instanceof ZendViewRenderer) {
|
} elseif ($this->template instanceof ZendViewRenderer) {
|
||||||
$data['templateName'] = 'Zend View';
|
$data['templateName'] = 'Zend View';
|
||||||
$data['templateDocs'] = 'http://framework.zend.com/manual/current/en/modules/zend.view.quick-start.html';
|
$data['templateDocs'] = 'https://docs.zendframework.com/zend-view/';
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->template) {
|
|
||||||
return new JsonResponse([
|
|
||||||
'welcome' => 'Congratulations! You have installed the zend-expressive skeleton application.',
|
|
||||||
'docsUrl' => 'zend-expressive.readthedocs.org',
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new HtmlResponse($this->template->render('app::home-page', $data));
|
return new HtmlResponse($this->template->render('app::home-page', $data));
|
||||||
|
|||||||
@ -11,7 +11,7 @@ class HomePageFactory
|
|||||||
public function __invoke(ContainerInterface $container)
|
public function __invoke(ContainerInterface $container)
|
||||||
{
|
{
|
||||||
$router = $container->get(RouterInterface::class);
|
$router = $container->get(RouterInterface::class);
|
||||||
$template = ($container->has(TemplateRendererInterface::class))
|
$template = $container->has(TemplateRendererInterface::class)
|
||||||
? $container->get(TemplateRendererInterface::class)
|
? $container->get(TemplateRendererInterface::class)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
|||||||
18
src/App/Action/NewsAction.php
Normal file
18
src/App/Action/NewsAction.php
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Action;
|
||||||
|
|
||||||
|
use App\Service\SkiesClientService;
|
||||||
|
|
||||||
|
class NewsAction
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var SkiesClientService
|
||||||
|
*/
|
||||||
|
private $skiesClient;
|
||||||
|
|
||||||
|
public function __construct(SkiesClientService $skiesClient)
|
||||||
|
{
|
||||||
|
$this->skiesClient = $skiesClient;
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/App/Action/NewsFactory.php
Normal file
15
src/App/Action/NewsFactory.php
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Action;
|
||||||
|
|
||||||
|
use App\Service\SkiesClientService;
|
||||||
|
use Interop\Container\ContainerInterface;
|
||||||
|
|
||||||
|
class NewsFactory
|
||||||
|
{
|
||||||
|
public function __invoke(ContainerInterface $container)
|
||||||
|
{
|
||||||
|
$skiesClient = $container->get(SkiesClientService::class);
|
||||||
|
return new NewsAction($skiesClient);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,16 +2,15 @@
|
|||||||
|
|
||||||
namespace App\Action;
|
namespace App\Action;
|
||||||
|
|
||||||
|
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||||
|
use Interop\Http\ServerMiddleware\MiddlewareInterface as ServerMiddlewareInterface;
|
||||||
use Zend\Diactoros\Response\JsonResponse;
|
use Zend\Diactoros\Response\JsonResponse;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
class PingAction
|
class PingAction implements ServerMiddlewareInterface
|
||||||
{
|
{
|
||||||
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
|
public function process(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||||
{
|
{
|
||||||
return new JsonResponse([
|
return new JsonResponse(['ack' => time()]);
|
||||||
'ack' => time(),
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
66
src/App/ConfigProvider.php
Normal file
66
src/App/ConfigProvider.php
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The configuration provider for the App module
|
||||||
|
*
|
||||||
|
* @see https://docs.zendframework.com/zend-component-installer/
|
||||||
|
*/
|
||||||
|
class ConfigProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Returns the configuration array
|
||||||
|
*
|
||||||
|
* To add a bit of a structure, each section is defined in a separate
|
||||||
|
* method which returns an array with its configuration.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function __invoke()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'dependencies' => $this->getDependencies(),
|
||||||
|
'templates' => $this->getTemplates(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the container dependencies
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getDependencies()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'invokables' => [
|
||||||
|
Action\PingAction::class => Action\PingAction::class,
|
||||||
|
],
|
||||||
|
'factories' => [
|
||||||
|
Action\HomePageAction::class => Action\HomePageFactory::class,
|
||||||
|
Action\ActivityAction::class => Action\ActivityFactory::class,
|
||||||
|
Action\ActivitySignupAction::class => Action\ActivitySignupFactory::class,
|
||||||
|
Action\ActivitySignoffAction::class => Action\ActivitySignoffFactory::class,
|
||||||
|
Action\NewsAction::class => Action\NewsFactory::class,
|
||||||
|
|
||||||
|
Service\SkiesClientService::class => Service\SkiesClientServiceFactory::class,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the templates configuration
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getTemplates()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'paths' => [
|
||||||
|
'app' => ['templates/app'],
|
||||||
|
'error' => ['templates/error'],
|
||||||
|
'layout' => ['templates/layout'],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
305
src/App/Entity/Activity.php
Normal file
305
src/App/Entity/Activity.php
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Entity;
|
||||||
|
|
||||||
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
|
|
||||||
|
class Activity implements \JsonSerializable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \DateTime
|
||||||
|
*/
|
||||||
|
private $date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \DateTime
|
||||||
|
*/
|
||||||
|
private $finalEntry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $accountable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ArrayCollection|Comment[]
|
||||||
|
*/
|
||||||
|
private $comments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
private $signedUsers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $isSignedup = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can sign up or sign off activity
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $canChangeSignup = false;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->comments = new ArrayCollection();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getId(): int
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $id
|
||||||
|
* @return Activity
|
||||||
|
*/
|
||||||
|
public function setId(int $id)
|
||||||
|
{
|
||||||
|
$this->id = $id;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName(): ?string
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name
|
||||||
|
* @return Activity
|
||||||
|
*/
|
||||||
|
public function setName(?string $name): Activity
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \DateTime
|
||||||
|
*/
|
||||||
|
public function getDate(): ?\DateTime
|
||||||
|
{
|
||||||
|
return $this->date;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \DateTime $date
|
||||||
|
* @return Activity
|
||||||
|
*/
|
||||||
|
public function setDate(?\DateTime $date): Activity
|
||||||
|
{
|
||||||
|
$this->date = $date;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getDescription(): ?string
|
||||||
|
{
|
||||||
|
return $this->description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $description
|
||||||
|
* @return Activity
|
||||||
|
*/
|
||||||
|
public function setDescription(?string $description): Activity
|
||||||
|
{
|
||||||
|
$this->description = $description;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \DateTime
|
||||||
|
*/
|
||||||
|
public function getFinalEntry(): ?\DateTime
|
||||||
|
{
|
||||||
|
return $this->finalEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \DateTime $finalEntry
|
||||||
|
* @return Activity
|
||||||
|
*/
|
||||||
|
public function setFinalEntry(\DateTime $finalEntry): Activity
|
||||||
|
{
|
||||||
|
$this->finalEntry = $finalEntry;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getAccountable(): ?string
|
||||||
|
{
|
||||||
|
return $this->accountable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $accountable
|
||||||
|
* @return Activity
|
||||||
|
*/
|
||||||
|
public function setAccountable(string $accountable): Activity
|
||||||
|
{
|
||||||
|
$this->accountable = $accountable;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Comment[]|ArrayCollection
|
||||||
|
*/
|
||||||
|
public function getComments()
|
||||||
|
{
|
||||||
|
return $this->comments;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Comment[]|ArrayCollection $comments
|
||||||
|
* @return Activity
|
||||||
|
*/
|
||||||
|
public function setComments($comments): Activity
|
||||||
|
{
|
||||||
|
$this->comments = $comments;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Comment $comment
|
||||||
|
* @return Activity
|
||||||
|
*/
|
||||||
|
public function addComment(Comment $comment): Activity
|
||||||
|
{
|
||||||
|
if(!$this->comments->contains($comment)) {
|
||||||
|
$this->comments->add($comment);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Comment $comment
|
||||||
|
* @return Activity
|
||||||
|
*/
|
||||||
|
public function removeComment(Comment $comment): Activity
|
||||||
|
{
|
||||||
|
if($this->comments->contains($comment)) {
|
||||||
|
$this->comments->removeElement($comment);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public function getSignedUsers(): ?array
|
||||||
|
{
|
||||||
|
return $this->signedUsers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string[] $signedUsers
|
||||||
|
* @return Activity
|
||||||
|
*/
|
||||||
|
public function setSignedUsers(array $signedUsers): Activity
|
||||||
|
{
|
||||||
|
$this->signedUsers = $signedUsers;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $signed
|
||||||
|
* @return Activity
|
||||||
|
*/
|
||||||
|
public function addSignedUser(string $signed): Activity
|
||||||
|
{
|
||||||
|
$this->signedUsers[] = $signed;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isSignedup(): bool
|
||||||
|
{
|
||||||
|
return $this->isSignedup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool $isSignedup
|
||||||
|
* @return Activity
|
||||||
|
*/
|
||||||
|
public function setIsSignedup(bool $isSignedup): Activity
|
||||||
|
{
|
||||||
|
$this->isSignedup = $isSignedup;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isCanChangeSignup(): bool
|
||||||
|
{
|
||||||
|
return $this->canChangeSignup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool $canChangeSignup
|
||||||
|
* @return Activity
|
||||||
|
*/
|
||||||
|
public function setCanChangeSignup(bool $canChangeSignup): Activity
|
||||||
|
{
|
||||||
|
$this->canChangeSignup = $canChangeSignup;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify data which should be serialized to JSON
|
||||||
|
* @link http://php.net/manual/en/jsonserializable.jsonserialize.php
|
||||||
|
* @return mixed data which can be serialized by <b>json_encode</b>,
|
||||||
|
* which is a value of any type other than a resource.
|
||||||
|
* @since 5.4.0
|
||||||
|
*/
|
||||||
|
public function jsonSerialize()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'id' => $this->getId(),
|
||||||
|
'name' => $this->getName(),
|
||||||
|
'date' => $this->getDate()
|
||||||
|
? $this->getDate()->format("Y-m-d H:i:s")
|
||||||
|
: null,
|
||||||
|
'description' => $this->getDescription(),
|
||||||
|
'finalEntry' => $this->getFinalEntry()
|
||||||
|
? $this->getFinalEntry()->format("Y-m-d H:i:s")
|
||||||
|
: null,
|
||||||
|
'accountable' => $this->getAccountable(),
|
||||||
|
'comments' => $this->getComments()->getValues(),
|
||||||
|
'signedUsers' => $this->getSignedUsers(),
|
||||||
|
'canChangeSignup' => $this->isCanChangeSignup(),
|
||||||
|
'isSignedUp' => $this->isSignedup()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
93
src/App/Entity/Comment.php
Normal file
93
src/App/Entity/Comment.php
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Entity;
|
||||||
|
|
||||||
|
class Comment implements \JsonSerializable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $text;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \DateTime
|
||||||
|
*/
|
||||||
|
private $createdAt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var User
|
||||||
|
*/
|
||||||
|
private $user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getText(): string
|
||||||
|
{
|
||||||
|
return $this->text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $text
|
||||||
|
* @return Comment
|
||||||
|
*/
|
||||||
|
public function setText(string $text): Comment
|
||||||
|
{
|
||||||
|
$this->text = $text;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \DateTime
|
||||||
|
*/
|
||||||
|
public function getCreatedAt(): \DateTime
|
||||||
|
{
|
||||||
|
return $this->createdAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \DateTime $createdAt
|
||||||
|
* @return Comment
|
||||||
|
*/
|
||||||
|
public function setCreatedAt(\DateTime $createdAt): Comment
|
||||||
|
{
|
||||||
|
$this->createdAt = $createdAt;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return User
|
||||||
|
*/
|
||||||
|
public function getUser(): User
|
||||||
|
{
|
||||||
|
return $this->user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param User $user
|
||||||
|
* @return Comment
|
||||||
|
*/
|
||||||
|
public function setUser(User $user): Comment
|
||||||
|
{
|
||||||
|
$this->user = $user;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify data which should be serialized to JSON
|
||||||
|
* @link http://php.net/manual/en/jsonserializable.jsonserialize.php
|
||||||
|
* @return mixed data which can be serialized by <b>json_encode</b>,
|
||||||
|
* which is a value of any type other than a resource.
|
||||||
|
* @since 5.4.0
|
||||||
|
*/
|
||||||
|
public function jsonSerialize()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'text' => $this->getText(),
|
||||||
|
'user' => $this->getUser(),
|
||||||
|
'createdAt' => $this->getCreatedAt()
|
||||||
|
? $this->getCreatedAt()->format("Y-m-d H:i:s")
|
||||||
|
: null,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/App/Entity/News.php
Normal file
8
src/App/Entity/News.php
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Entity;
|
||||||
|
|
||||||
|
class News
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
67
src/App/Entity/User.php
Normal file
67
src/App/Entity/User.php
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Entity;
|
||||||
|
|
||||||
|
class User implements \JsonSerializable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $username;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $displayName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getUsername(): string
|
||||||
|
{
|
||||||
|
return $this->username;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $username
|
||||||
|
* @return User
|
||||||
|
*/
|
||||||
|
public function setUsername(string $username): User
|
||||||
|
{
|
||||||
|
$this->username = $username;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getDisplayName(): string
|
||||||
|
{
|
||||||
|
return $this->displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $displayName
|
||||||
|
* @return User
|
||||||
|
*/
|
||||||
|
public function setDisplayName(string $displayName): User
|
||||||
|
{
|
||||||
|
$this->displayName = $displayName;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify data which should be serialized to JSON
|
||||||
|
* @link http://php.net/manual/en/jsonserializable.jsonserialize.php
|
||||||
|
* @return mixed data which can be serialized by <b>json_encode</b>,
|
||||||
|
* which is a value of any type other than a resource.
|
||||||
|
* @since 5.4.0
|
||||||
|
*/
|
||||||
|
public function jsonSerialize()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'username' => $this->getUsername(),
|
||||||
|
'displayName' => $this->getDisplayName(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
397
src/App/Service/SkiesClientService.php
Normal file
397
src/App/Service/SkiesClientService.php
Normal file
@ -0,0 +1,397 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Service;
|
||||||
|
|
||||||
|
use App\Entity\Activity;
|
||||||
|
use App\Entity\Comment;
|
||||||
|
use App\Entity\User;
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Zend\Dom\Document;
|
||||||
|
use Zend\Expressive\Exception\MissingDependencyException;
|
||||||
|
|
||||||
|
class SkiesClientService
|
||||||
|
{
|
||||||
|
const SKIES_MAIN_URL = "https://skies.sigmatechnology.se/main.asp";
|
||||||
|
const SKIES_PROFILE_URL = "https://skies.sigmatechnology.se/main.asp?rID=1&alt=2&username=%s";
|
||||||
|
const SKIES_ACTIVITIES_URL = "https://skies.sigmatechnology.se/main.asp?rID=2";
|
||||||
|
const SKIES_ACTIVITY_URL = "https://skies.sigmatechnology.se/main.asp?rID=2&alt=1&aktID=%s";
|
||||||
|
const SKIES_ACTIVITY_SIGNUP_URL = "https://skies.sigmatechnology.se/main.asp?rID=2&alt=1&aktID=%s&doJoin=1";
|
||||||
|
const SKIES_ACTIVITY_SIGNOFF_URL = "https://skies.sigmatechnology.se/main.asp?rID=2&alt=1&aktID=%s&doCancel=1&user=%s";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Client
|
||||||
|
*/
|
||||||
|
private $client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $authHeader = null;
|
||||||
|
|
||||||
|
public function __construct(Client $client)
|
||||||
|
{
|
||||||
|
$this->client = $client;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $authHeader
|
||||||
|
* @return SkiesClientService
|
||||||
|
*/
|
||||||
|
public function setAuthHeader(string $authHeader): SkiesClientService
|
||||||
|
{
|
||||||
|
$this->authHeader = $authHeader;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getNews()
|
||||||
|
{
|
||||||
|
$response = $this->doSkiesRequest('GET', self::SKIES_MAIN_URL);
|
||||||
|
$htmlBody = $response->getBody();
|
||||||
|
return $this->parseMainPage($htmlBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Activity[]
|
||||||
|
*/
|
||||||
|
public function getActivities()
|
||||||
|
{
|
||||||
|
$response = $this->doSkiesRequest('GET', self::SKIES_ACTIVITIES_URL);
|
||||||
|
$htmlBody = $response->getBody();
|
||||||
|
return $this->parseActivitiesPage($htmlBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $htmlBody
|
||||||
|
* @return Activity[]
|
||||||
|
*/
|
||||||
|
private function parseActivitiesPage(string $htmlBody)
|
||||||
|
{
|
||||||
|
$domDocument = new Document($htmlBody);
|
||||||
|
$eventNodes = Document\Query::execute(
|
||||||
|
"div.container div.row div.box > table tr",
|
||||||
|
$domDocument,
|
||||||
|
Document\Query::TYPE_CSS
|
||||||
|
);
|
||||||
|
|
||||||
|
$activities = [];
|
||||||
|
|
||||||
|
for ($i = 1; $i < $eventNodes->count(); $i++) {
|
||||||
|
$eventRow = $eventNodes[$i];
|
||||||
|
$rowCells = Document\Query::execute(
|
||||||
|
"./td",
|
||||||
|
$domDocument,
|
||||||
|
Document\Query::TYPE_XPATH,
|
||||||
|
$eventRow
|
||||||
|
);
|
||||||
|
|
||||||
|
$href = $rowCells[0]->childNodes->item(0)->getAttribute("href");
|
||||||
|
$queryString = parse_url($href, PHP_URL_QUERY);
|
||||||
|
parse_str($queryString, $queryParams);
|
||||||
|
$activities[] = $this->getActivity($queryParams["aktID"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $activities;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $id
|
||||||
|
* @return Activity
|
||||||
|
*/
|
||||||
|
public function getActivity(int $id): Activity
|
||||||
|
{
|
||||||
|
$response = $this->doSkiesRequest('GET', sprintf(self::SKIES_ACTIVITY_URL, $id));
|
||||||
|
$htmlBody = $response->getBody();
|
||||||
|
return $this->parseActivityPage($htmlBody, $id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $id
|
||||||
|
* @return Activity
|
||||||
|
*/
|
||||||
|
public function signUpActivity(int $id): Activity
|
||||||
|
{
|
||||||
|
$this->doSkiesRequest('POST', sprintf(self::SKIES_ACTIVITY_SIGNUP_URL, $id), [
|
||||||
|
'form_params' => [
|
||||||
|
'user' => $this->getUsername(),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
return $this->getActivity($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $id
|
||||||
|
* @return Activity
|
||||||
|
*/
|
||||||
|
public function signOffActivity(int $id): Activity
|
||||||
|
{
|
||||||
|
$username = $this->getUsername();
|
||||||
|
$this->doSkiesRequest('GET', sprintf(self::SKIES_ACTIVITY_SIGNOFF_URL, $id, $username));
|
||||||
|
return $this->getActivity($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $htmlBody
|
||||||
|
* @param int $id
|
||||||
|
* @return Activity
|
||||||
|
*/
|
||||||
|
private function parseActivityPage(string $htmlBody, int $id): Activity
|
||||||
|
{
|
||||||
|
$domDocument = new Document($htmlBody);
|
||||||
|
/** Activity body */
|
||||||
|
$activityNode = Document\Query::execute(
|
||||||
|
"div.col-md-10 div.box",
|
||||||
|
$domDocument,
|
||||||
|
Document\Query::TYPE_CSS
|
||||||
|
);
|
||||||
|
|
||||||
|
/** Activity name */
|
||||||
|
$h5Nodes = Document\Query::execute(
|
||||||
|
"./h5",
|
||||||
|
$domDocument,
|
||||||
|
Document\Query::TYPE_XPATH,
|
||||||
|
$activityNode[0]
|
||||||
|
);
|
||||||
|
|
||||||
|
$divNodes = Document\Query::execute(
|
||||||
|
".//div",
|
||||||
|
$domDocument,
|
||||||
|
Document\Query::TYPE_XPATH,
|
||||||
|
$activityNode[0]
|
||||||
|
);
|
||||||
|
|
||||||
|
/** Comment block */
|
||||||
|
$commentNodes = Document\Query::execute(
|
||||||
|
'.//div[@class="onecomment"]',
|
||||||
|
$domDocument,
|
||||||
|
Document\Query::TYPE_XPATH,
|
||||||
|
$activityNode[0]
|
||||||
|
);
|
||||||
|
|
||||||
|
/** Signed users block */
|
||||||
|
$signedUserParagraph = Document\Query::execute(
|
||||||
|
'//div[@class="portlet"]//div[@class="paragraph"]/text()',
|
||||||
|
$domDocument,
|
||||||
|
Document\Query::TYPE_XPATH
|
||||||
|
);
|
||||||
|
|
||||||
|
preg_match("#Date:.*?([0-9]{4}-[0-9]{2}-[0-9]{2}).*?Time: ([0-9]{1,2}:[0-9]{2}).*?Final entry day:.*?([0-9]{4}-[0-9]{2}-[0-9]{2}).*?Accountable: (.*?) / ([0-9 +]*)#msi", $divNodes[1]->textContent, $matches);
|
||||||
|
|
||||||
|
$signedUsers = $this->parseActivitySignedUsers($signedUserParagraph);
|
||||||
|
$isSignedUp = in_array($this->getDisplayName($this->getUsername()), $signedUsers);
|
||||||
|
$canChangeSignup = $isSignedUp
|
||||||
|
? $this->canSignOff($domDocument, $activityNode[0])
|
||||||
|
: $this->canSignup($domDocument, $activityNode[0]);
|
||||||
|
|
||||||
|
$activity = new Activity();
|
||||||
|
$activity->setId($id)
|
||||||
|
->setName($this->clearTextNode($h5Nodes[0]->textContent))
|
||||||
|
->setDescription($this->parseActivityDescription($activityNode[0]))
|
||||||
|
->setDate(new \DateTime(sprintf("%s %s", $matches[1], $matches[2])))
|
||||||
|
->setFinalEntry(new \DateTime($matches[3]))
|
||||||
|
->setAccountable(str_replace(
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
$matches[5]
|
||||||
|
? sprintf("%s (%s)", $matches[4], $matches[5])
|
||||||
|
: $matches[4])
|
||||||
|
)
|
||||||
|
->setSignedUsers($signedUsers)
|
||||||
|
->setIsSignedup($isSignedUp)
|
||||||
|
->setCanChangeSignup($canChangeSignup);
|
||||||
|
$this->parseActivityComments($commentNodes, $activity);
|
||||||
|
|
||||||
|
return $activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \DOMNode $element
|
||||||
|
* @return null|string
|
||||||
|
*/
|
||||||
|
private function parseActivityDescription(\DOMNode $element): ?string
|
||||||
|
{
|
||||||
|
$description = "";
|
||||||
|
$isContent = false;
|
||||||
|
/** @var \DOMElement $childNode */
|
||||||
|
foreach ($element->childNodes as $childNode) {
|
||||||
|
if ($childNode->nodeName == "hr") {
|
||||||
|
$isContent = true;
|
||||||
|
}
|
||||||
|
if ($childNode->nodeName == "form") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ($childNode->nodeName == "span") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ($childNode->nodeName == "script") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ($isContent) {
|
||||||
|
$description .= $childNode->nodeName == "br"
|
||||||
|
? "\n"
|
||||||
|
: rtrim($childNode->textContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $this->clearTextNode($description);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Document\NodeList $commentElements
|
||||||
|
* @param Activity $activity
|
||||||
|
*/
|
||||||
|
private function parseActivityComments(Document\NodeList $commentElements, Activity $activity)
|
||||||
|
{
|
||||||
|
/** @var \DOMElement $commentElement */
|
||||||
|
foreach ($commentElements as $commentElement) {
|
||||||
|
$divElements = $commentElement->getElementsByTagName("div");
|
||||||
|
|
||||||
|
$queryString = parse_url(
|
||||||
|
$commentElement->getElementsByTagName("a")->item(0)->getAttribute("href"),
|
||||||
|
PHP_URL_QUERY
|
||||||
|
);
|
||||||
|
parse_str($queryString, $queryParams);
|
||||||
|
preg_match(
|
||||||
|
"#(.*)\s/\s([0-9]{4}-[0-9]{2}-[0-9]{2})\s([0-9]{1,2}:[0-9]{1,2})#msi",
|
||||||
|
str_replace(" ", " ", $divElements->item(2)->textContent),
|
||||||
|
$matches
|
||||||
|
);
|
||||||
|
|
||||||
|
$user = new User();
|
||||||
|
$user->setDisplayName($matches[1])
|
||||||
|
->setUsername($queryParams['username']);
|
||||||
|
|
||||||
|
$comment = new Comment();
|
||||||
|
$comment
|
||||||
|
->setText($divElements->item(1)->textContent)
|
||||||
|
->setUser($user)
|
||||||
|
->setCreatedAt(new \DateTime(sprintf(
|
||||||
|
"%s %s",
|
||||||
|
$matches[2],
|
||||||
|
$matches[3]
|
||||||
|
)));
|
||||||
|
$activity->addComment($comment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Document\NodeList $usersBlockText
|
||||||
|
* @return array
|
||||||
|
* @todo remove unsign button if it is present
|
||||||
|
*/
|
||||||
|
private function parseActivitySignedUsers(Document\NodeList $usersBlockText): array
|
||||||
|
{
|
||||||
|
$signed = [];
|
||||||
|
for ($i = 1; $i < $usersBlockText->count(); $i++) {
|
||||||
|
preg_match(
|
||||||
|
"#[0-9]+\.[^\p{L}]([\p{L}\s]+)#msiu",
|
||||||
|
$usersBlockText[$i]->textContent,
|
||||||
|
$usernameMatch
|
||||||
|
);
|
||||||
|
$signed[] = $usernameMatch[1];
|
||||||
|
}
|
||||||
|
$collator = new \Collator("hu_HU");
|
||||||
|
$collator->sort($signed);
|
||||||
|
return $signed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can sign off if there is no red span inside the node
|
||||||
|
* @param Document $domDocument
|
||||||
|
* @param \DOMNode $domNode
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function canSignOff(Document $domDocument, \DOMNode $domNode): bool
|
||||||
|
{
|
||||||
|
$redSpanNodes = Document\Query::execute(
|
||||||
|
'./span[@class="red"]',
|
||||||
|
$domDocument,
|
||||||
|
Document\Query::TYPE_XPATH,
|
||||||
|
$domNode
|
||||||
|
);
|
||||||
|
return $redSpanNodes->count() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can sign up if there is a form direct descendant
|
||||||
|
* @param Document $domDocument
|
||||||
|
* @param \DOMNode $domNode
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function canSignUp(Document $domDocument, \DOMNode $domNode): bool
|
||||||
|
{
|
||||||
|
$formNodes = Document\Query::execute(
|
||||||
|
"./form",
|
||||||
|
$domDocument,
|
||||||
|
Document\Query::TYPE_XPATH,
|
||||||
|
$domNode
|
||||||
|
);
|
||||||
|
return $formNodes->count() == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function parseMainPage(string $htmlBody)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getDisplayName(string $username): string
|
||||||
|
{
|
||||||
|
$response = $this->doSkiesRequest("GET", sprintf(self::SKIES_PROFILE_URL, $username));
|
||||||
|
$profilePage = $response->getBody();
|
||||||
|
|
||||||
|
$domDocument = new Document($profilePage);
|
||||||
|
$h1Nodes = Document\Query::execute(
|
||||||
|
'//div[@class="box"]/h1',
|
||||||
|
$domDocument,
|
||||||
|
Document\Query::TYPE_XPATH
|
||||||
|
);
|
||||||
|
return $this->clearTextNode($h1Nodes[0]->textContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear junk from text nodes
|
||||||
|
*
|
||||||
|
* @param string $text
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function clearTextNode(string $text): string
|
||||||
|
{
|
||||||
|
$text = str_replace(" ", " ", $text);
|
||||||
|
$text = str_replace("", "-", $text);
|
||||||
|
$text = preg_replace("#[ \t]+#msiu", " ", $text);
|
||||||
|
return trim($text, " \t\n\r\0\x0B" . chr(0xC2) . chr(0xA0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do an http request adding the Authorization header
|
||||||
|
*
|
||||||
|
* @param string $method
|
||||||
|
* @param string $url
|
||||||
|
* @param array $options
|
||||||
|
* @return ResponseInterface
|
||||||
|
*/
|
||||||
|
private function doSkiesRequest(string $method, string $url, $options = []): ResponseInterface
|
||||||
|
{
|
||||||
|
if ($this->authHeader == null) {
|
||||||
|
throw new MissingDependencyException("X-Passthru-Auth header is missing");
|
||||||
|
}
|
||||||
|
return $this->client
|
||||||
|
->request($method, $url, [
|
||||||
|
'headers' => [
|
||||||
|
'Authorization' => "Basic {$this->authHeader}",
|
||||||
|
]
|
||||||
|
] + $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function getUsername(): string
|
||||||
|
{
|
||||||
|
if (null == $this->authHeader) {
|
||||||
|
throw new MissingDependencyException("X-Passthru-Auth header is missing");
|
||||||
|
}
|
||||||
|
$decodedHeader = base64_decode($this->authHeader);
|
||||||
|
list($username) = explode(":", $decodedHeader);
|
||||||
|
return $username;
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/App/Service/SkiesClientServiceFactory.php
Normal file
17
src/App/Service/SkiesClientServiceFactory.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Service;
|
||||||
|
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use Interop\Container\ContainerInterface;
|
||||||
|
|
||||||
|
class SkiesClientServiceFactory
|
||||||
|
{
|
||||||
|
public function __invoke(ContainerInterface $container)
|
||||||
|
{
|
||||||
|
$httpClient = new Client([
|
||||||
|
'cookies' => true,
|
||||||
|
]);
|
||||||
|
return new SkiesClientService($httpClient);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,187 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Service;
|
|
||||||
|
|
||||||
use Interop\Container\ContainerInterface;
|
|
||||||
use Symfony\Component\CssSelector\CssSelectorConverter;
|
|
||||||
use Zend\Http\Client;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class SkiesService
|
|
||||||
*
|
|
||||||
* @package App\Service
|
|
||||||
* @todo handle errors in http response
|
|
||||||
*/
|
|
||||||
class SkiesService
|
|
||||||
{
|
|
||||||
const BASE_URI = 'http://skies.sigmatechnology.se/';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var Client
|
|
||||||
*/
|
|
||||||
private $httpClient;
|
|
||||||
/**
|
|
||||||
* @var ContainerInterface
|
|
||||||
*/
|
|
||||||
private $container;
|
|
||||||
|
|
||||||
public function __construct(ContainerInterface $container)
|
|
||||||
{
|
|
||||||
$this->container = $container;
|
|
||||||
$this->httpClient = new Client();
|
|
||||||
$headers = $this->httpClient->getRequest()->getHeaders();
|
|
||||||
$headers->addHeaderLine('Authorization', 'Basic ' . $this->getAuthToken());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a list of all activities
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getActivityList()
|
|
||||||
{
|
|
||||||
$this->httpClient->setUri(self::BASE_URI . "main.asp?rID=2");
|
|
||||||
$skiesResponse = $this->httpClient->send();
|
|
||||||
$skiesHtmlBody = $skiesResponse->getBody();
|
|
||||||
|
|
||||||
$cssToXpathConverter = new CssSelectorConverter();
|
|
||||||
$xpathQuery = $cssToXpathConverter->toXPath('html > body > div > div.row > div.col-md-10 > div.row > div.col-md-9 > div.box > table.table-striped > tr');
|
|
||||||
|
|
||||||
$doc = new \DOMDocument();
|
|
||||||
$doc->loadHTML($skiesHtmlBody);
|
|
||||||
|
|
||||||
$xpath = new \DOMXPath($doc);
|
|
||||||
$elements = $xpath->query($xpathQuery);
|
|
||||||
|
|
||||||
$parsed = [];
|
|
||||||
/** @var \DOMNode $domElement */
|
|
||||||
foreach ($elements as $domElement) {
|
|
||||||
if($domElement->childNodes->item(0)->nodeName != 'td') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$url = $domElement->childNodes->item(0)->childNodes->item(0)->attributes->getNamedItem('href')->textContent;
|
|
||||||
preg_match("/aktid=([0-9]+)/msi", $url, $match);
|
|
||||||
$parsed[] = [
|
|
||||||
'id' => (int)$match[1],
|
|
||||||
'label' => $domElement->childNodes->item(0)->childNodes->item(0)->textContent,
|
|
||||||
'date' => $domElement->childNodes->item(2)->textContent,
|
|
||||||
'time' => trim($domElement->childNodes->item(3)->textContent, " \t\n\r\0\x0B\xc2\xa0"),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $parsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a single activity
|
|
||||||
*
|
|
||||||
* @param int $id
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getActivity(int $id)
|
|
||||||
{
|
|
||||||
$this->httpClient->setUri(self::BASE_URI . "main.asp?rID=2&alt=1&aktID=" . $id);
|
|
||||||
$skiesResponse = $this->httpClient->send();
|
|
||||||
$skiesHtmlBody = $skiesResponse->getBody();
|
|
||||||
|
|
||||||
$cssToXpathConverter = new CssSelectorConverter();
|
|
||||||
$xpathQuery = $cssToXpathConverter->toXPath('div.container > div.row > div.col-md-10 > div.row > div.col-md-9 > div.box');
|
|
||||||
$xpathCommentsQuery = $cssToXpathConverter->toXPath('div.container div.paragraph > div.onecomment');
|
|
||||||
|
|
||||||
$doc = new \DOMDocument();
|
|
||||||
$doc->loadHTML($skiesHtmlBody);
|
|
||||||
|
|
||||||
$xpath = new \DOMXPath($doc);
|
|
||||||
$elements = $xpath->query($xpathQuery);
|
|
||||||
|
|
||||||
$h5Elements = $elements->item(0)->getElementsByTagName('h5');
|
|
||||||
$title = $h5Elements->item(0)->textContent;
|
|
||||||
|
|
||||||
$detailDivElements = $elements->item(0)->getElementsByTagName('div');
|
|
||||||
$commentElements = $xpath->query($xpathCommentsQuery);
|
|
||||||
|
|
||||||
$eventDetails = $this->parseActivityDetails($detailDivElements->item(1)->textContent);
|
|
||||||
$eventDescription = $this->parseActivityDescription($elements->item(0));
|
|
||||||
$eventComments = $this->parseActivityComments($commentElements);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'title' => $title,
|
|
||||||
'details' => $eventDetails,
|
|
||||||
'description' => $eventDescription,
|
|
||||||
'comments' => $eventComments,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return null|string
|
|
||||||
* @todo real auth token from whatever...
|
|
||||||
*/
|
|
||||||
private function getAuthToken(): ?string
|
|
||||||
{
|
|
||||||
$config = $this->container->get('config');
|
|
||||||
return $config['authKey'];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse the activity information details returning the date, time finaly entry and the person accountable
|
|
||||||
*
|
|
||||||
* @param string $divText
|
|
||||||
* @return array|null
|
|
||||||
*/
|
|
||||||
private function parseActivityDetails(string $divText): ?array
|
|
||||||
{
|
|
||||||
preg_match("#Date:.*?([0-9]{4}-[0-9]{2}-[0-9]{2}).*?Time: ([0-9]{1,2}:[0-9]{2}).*?Final entry day:.*?([0-9]{4}-[0-9]{2}-[0-9]{2}).*?Accountable: (.*?) / ([0-9 +]*)#msi", $divText, $matches);
|
|
||||||
return [
|
|
||||||
'date' => $matches[1],
|
|
||||||
'time' => $matches[2],
|
|
||||||
'finalEntry' => $matches[3],
|
|
||||||
'accountable' => str_replace(" ", " ", $matches[5] ? sprintf("%s (%s)", $matches[4], $matches[5]) : $matches[4]),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses comment block. Comment section is pretty much unformatted text
|
|
||||||
* between a <hr> tag and a <form> tag in the passed $element
|
|
||||||
*
|
|
||||||
* @param \DOMElement $element
|
|
||||||
* @return null|string
|
|
||||||
*/
|
|
||||||
private function parseActivityDescription(\DOMElement $element): ?string
|
|
||||||
{
|
|
||||||
$description = "";
|
|
||||||
$isContent = false;
|
|
||||||
/** @var \DOMElement $childNode */
|
|
||||||
foreach($element->childNodes as $childNode) {
|
|
||||||
if ($childNode->nodeName == "hr") { $isContent = true; }
|
|
||||||
if ($childNode->nodeName == "form") { break; }
|
|
||||||
if ($isContent) {
|
|
||||||
$description .= $childNode->nodeName == "br"
|
|
||||||
? "\n"
|
|
||||||
: rtrim($childNode->textContent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return trim($description);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse $commentElements
|
|
||||||
*
|
|
||||||
* @param \DOMNodeList $commentElements
|
|
||||||
* @return array|null
|
|
||||||
*/
|
|
||||||
private function parseActivityComments(\DOMNodeList $commentElements): ?array
|
|
||||||
{
|
|
||||||
$comments = [];
|
|
||||||
/** @var \DOMElement $commentElement */
|
|
||||||
foreach($commentElements as $commentElement) {
|
|
||||||
preg_match("#(.*)\s/\s([0-9]{4}-[0-9]{2}-[0-9]{2})\s([0-9]{1,2}:[0-9]{1,2})#msi", str_replace(" "," ", $commentElement->getElementsByTagName("div")->item(2)->textContent), $matches);
|
|
||||||
$comments[] = [
|
|
||||||
'comment' => $commentElement->getElementsByTagName("div")->item(1)->textContent,
|
|
||||||
'user' => $matches[1],
|
|
||||||
'date' => $matches[2],
|
|
||||||
'time' => $matches[3],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
return $comments;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Service;
|
|
||||||
|
|
||||||
use Interop\Container\ContainerInterface;
|
|
||||||
|
|
||||||
class SkiesServiceFactory
|
|
||||||
{
|
|
||||||
public function __invoke(ContainerInterface $container)
|
|
||||||
{
|
|
||||||
return new SkiesService($container);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -3,11 +3,16 @@
|
|||||||
namespace AppTest\Action;
|
namespace AppTest\Action;
|
||||||
|
|
||||||
use App\Action\HomePageAction;
|
use App\Action\HomePageAction;
|
||||||
use Zend\Diactoros\Response;
|
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||||
use Zend\Diactoros\ServerRequest;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Prophecy\Argument;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Zend\Diactoros\Response\HtmlResponse;
|
||||||
|
use Zend\Diactoros\Response\JsonResponse;
|
||||||
use Zend\Expressive\Router\RouterInterface;
|
use Zend\Expressive\Router\RouterInterface;
|
||||||
|
use Zend\Expressive\Template\TemplateRendererInterface;
|
||||||
|
|
||||||
class HomePageActionTest extends \PHPUnit_Framework_TestCase
|
class HomePageActionTest extends TestCase
|
||||||
{
|
{
|
||||||
/** @var RouterInterface */
|
/** @var RouterInterface */
|
||||||
protected $router;
|
protected $router;
|
||||||
@ -17,12 +22,31 @@ class HomePageActionTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->router = $this->prophesize(RouterInterface::class);
|
$this->router = $this->prophesize(RouterInterface::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testResponse()
|
public function testReturnsJsonResponseWhenNoTemplateRendererProvided()
|
||||||
{
|
{
|
||||||
$homePage = new HomePageAction($this->router->reveal(), null);
|
$homePage = new HomePageAction($this->router->reveal(), null);
|
||||||
$response = $homePage(new ServerRequest(['/']), new Response(), function () {
|
$response = $homePage->process(
|
||||||
});
|
$this->prophesize(ServerRequestInterface::class)->reveal(),
|
||||||
|
$this->prophesize(DelegateInterface::class)->reveal()
|
||||||
|
);
|
||||||
|
|
||||||
$this->assertTrue($response instanceof Response);
|
$this->assertInstanceOf(JsonResponse::class, $response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReturnsHtmlResponseWhenTemplateRendererProvided()
|
||||||
|
{
|
||||||
|
$renderer = $this->prophesize(TemplateRendererInterface::class);
|
||||||
|
$renderer
|
||||||
|
->render('app::home-page', Argument::type('array'))
|
||||||
|
->willReturn('');
|
||||||
|
|
||||||
|
$homePage = new HomePageAction($this->router->reveal(), $renderer->reveal());
|
||||||
|
|
||||||
|
$response = $homePage->process(
|
||||||
|
$this->prophesize(ServerRequestInterface::class)->reveal(),
|
||||||
|
$this->prophesize(DelegateInterface::class)->reveal()
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertInstanceOf(HtmlResponse::class, $response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,10 +5,11 @@ namespace AppTest\Action;
|
|||||||
use App\Action\HomePageAction;
|
use App\Action\HomePageAction;
|
||||||
use App\Action\HomePageFactory;
|
use App\Action\HomePageFactory;
|
||||||
use Interop\Container\ContainerInterface;
|
use Interop\Container\ContainerInterface;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
use Zend\Expressive\Router\RouterInterface;
|
use Zend\Expressive\Router\RouterInterface;
|
||||||
use Zend\Expressive\Template\TemplateRendererInterface;
|
use Zend\Expressive\Template\TemplateRendererInterface;
|
||||||
|
|
||||||
class HomePageFactoryTest extends \PHPUnit_Framework_TestCase
|
class HomePageFactoryTest extends TestCase
|
||||||
{
|
{
|
||||||
/** @var ContainerInterface */
|
/** @var ContainerInterface */
|
||||||
protected $container;
|
protected $container;
|
||||||
@ -26,11 +27,11 @@ class HomePageFactoryTest extends \PHPUnit_Framework_TestCase
|
|||||||
$factory = new HomePageFactory();
|
$factory = new HomePageFactory();
|
||||||
$this->container->has(TemplateRendererInterface::class)->willReturn(false);
|
$this->container->has(TemplateRendererInterface::class)->willReturn(false);
|
||||||
|
|
||||||
$this->assertTrue($factory instanceof HomePageFactory);
|
$this->assertInstanceOf(HomePageFactory::class, $factory);
|
||||||
|
|
||||||
$homePage = $factory($this->container->reveal());
|
$homePage = $factory($this->container->reveal());
|
||||||
|
|
||||||
$this->assertTrue($homePage instanceof HomePageAction);
|
$this->assertInstanceOf(HomePageAction::class, $homePage);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFactoryWithTemplate()
|
public function testFactoryWithTemplate()
|
||||||
@ -41,10 +42,10 @@ class HomePageFactoryTest extends \PHPUnit_Framework_TestCase
|
|||||||
->get(TemplateRendererInterface::class)
|
->get(TemplateRendererInterface::class)
|
||||||
->willReturn($this->prophesize(TemplateRendererInterface::class));
|
->willReturn($this->prophesize(TemplateRendererInterface::class));
|
||||||
|
|
||||||
$this->assertTrue($factory instanceof HomePageFactory);
|
$this->assertInstanceOf(HomePageFactory::class, $factory);
|
||||||
|
|
||||||
$homePage = $factory($this->container->reveal());
|
$homePage = $factory($this->container->reveal());
|
||||||
|
|
||||||
$this->assertTrue($homePage instanceof HomePageAction);
|
$this->assertInstanceOf(HomePageAction::class, $homePage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,20 +3,24 @@
|
|||||||
namespace AppTest\Action;
|
namespace AppTest\Action;
|
||||||
|
|
||||||
use App\Action\PingAction;
|
use App\Action\PingAction;
|
||||||
use Zend\Diactoros\Response;
|
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||||
use Zend\Diactoros\ServerRequest;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Zend\Diactoros\Response\JsonResponse;
|
||||||
|
|
||||||
class PingActionTest extends \PHPUnit_Framework_TestCase
|
class PingActionTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testResponse()
|
public function testResponse()
|
||||||
{
|
{
|
||||||
$pingAction = new PingAction();
|
$pingAction = new PingAction();
|
||||||
$response = $pingAction(new ServerRequest(['/']), new Response(), function () {
|
$response = $pingAction->process(
|
||||||
});
|
$this->prophesize(ServerRequestInterface::class)->reveal(),
|
||||||
|
$this->prophesize(DelegateInterface::class)->reveal()
|
||||||
|
);
|
||||||
|
|
||||||
$json = json_decode((string) $response->getBody());
|
$json = json_decode((string) $response->getBody());
|
||||||
|
|
||||||
$this->assertTrue($response instanceof Response);
|
$this->assertInstanceOf(JsonResponse::class, $response);
|
||||||
$this->assertTrue($response instanceof Response\JsonResponse);
|
|
||||||
$this->assertTrue(isset($json->ack));
|
$this->assertTrue(isset($json->ack));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user