mirror of
https://github.com/shlinkio/shlink.git
synced 2026-03-06 23:33:13 +08:00
Compare commits
91 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1009f9e7c6 | ||
|
|
9c8eef12ba | ||
|
|
9260b3ac6b | ||
|
|
ff98e1fb3d | ||
|
|
f3389d3738 | ||
|
|
a138f4153d | ||
|
|
602e11d5e7 | ||
|
|
3cd14153ca | ||
|
|
095d8e73b8 | ||
|
|
b37f303e76 | ||
|
|
c8368c9098 | ||
|
|
8d0bac9478 | ||
|
|
286c24f8c0 | ||
|
|
963d26f59b | ||
|
|
e07c464de8 | ||
|
|
575509c45b | ||
|
|
563e654b99 | ||
|
|
3e268e2012 | ||
|
|
b2d9f2fc01 | ||
|
|
6717102dd2 | ||
|
|
1ba7fc81ac | ||
|
|
caf4fa7fdd | ||
|
|
5c7962966d | ||
|
|
95ec7e0afa | ||
|
|
c37660f763 | ||
|
|
486ea10c3c | ||
|
|
e0f18f8d1f | ||
|
|
a66f116d66 | ||
|
|
dd099dc39c | ||
|
|
c05aeabdee | ||
|
|
23922f6c7b | ||
|
|
69a99949e1 | ||
|
|
d56cde72a3 | ||
|
|
99ffff11c7 | ||
|
|
bb050cc1b6 | ||
|
|
3547889ad5 | ||
|
|
479e694478 | ||
|
|
2368b634e3 | ||
|
|
dcc09975a9 | ||
|
|
102f5c4e12 | ||
|
|
cc688fa3ce | ||
|
|
e7f7cbcaac | ||
|
|
f9c56d7cb1 | ||
|
|
1fe2e6f6bd | ||
|
|
c3cc88f03e | ||
|
|
584e1f5643 | ||
|
|
04c479148a | ||
|
|
c45cb7bacb | ||
|
|
17be221920 | ||
|
|
10da57572f | ||
|
|
52478ca60a | ||
|
|
62b49dcb19 | ||
|
|
a365faef9c | ||
|
|
5d2698e8a1 | ||
|
|
ec4a413a5b | ||
|
|
596d1ee797 | ||
|
|
d117f82bcb | ||
|
|
18abe9d0f9 | ||
|
|
b53e51de33 | ||
|
|
9478c71af1 | ||
|
|
a2c4eebec8 | ||
|
|
2e5a7d76df | ||
|
|
288249d0b8 | ||
|
|
cd47aae902 | ||
|
|
9bd18ee041 | ||
|
|
22c76df8e6 | ||
|
|
6c87436a96 | ||
|
|
734dac9456 | ||
|
|
85ca366893 | ||
|
|
46db736af8 | ||
|
|
7530048fbd | ||
|
|
c3c03a3a3b | ||
|
|
d1018b6da7 | ||
|
|
fe7928ae0e | ||
|
|
f6c39285c9 | ||
|
|
0e2a289f9f | ||
|
|
f7424da16b | ||
|
|
2e10ee66b7 | ||
|
|
7e442671c3 | ||
|
|
ca646ec2b7 | ||
|
|
4df1af5fd8 | ||
|
|
b4548f3401 | ||
|
|
1819481710 | ||
|
|
e59ae654c0 | ||
|
|
a8bf699f2d | ||
|
|
de9d9d8667 | ||
|
|
869865f22a | ||
|
|
29fd313337 | ||
|
|
7781f07352 | ||
|
|
072371d459 | ||
|
|
51bf948458 |
@@ -1,7 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace PHPSTORM_META;
|
namespace PHPSTORM_META;
|
||||||
|
|
||||||
use Interop\Container\ContainerInterface;
|
use Psr\Container\ContainerInterface;
|
||||||
|
use Zend\ServiceManager\ServiceManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PhpStorm Container Interop code completion
|
* PhpStorm Container Interop code completion
|
||||||
@@ -16,4 +17,7 @@ $STATIC_METHOD_TYPES = [
|
|||||||
ContainerInterface::get('') => [
|
ContainerInterface::get('') => [
|
||||||
'' == '@',
|
'' == '@',
|
||||||
],
|
],
|
||||||
|
ServiceManager::build('') => [
|
||||||
|
'' == '@',
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|||||||
137
CHANGELOG.md
137
CHANGELOG.md
@@ -1,20 +1,59 @@
|
|||||||
## CHANGELOG
|
## CHANGELOG
|
||||||
|
|
||||||
|
### 1.5.0
|
||||||
|
|
||||||
|
**Enhancements:**
|
||||||
|
|
||||||
|
* [95: Add tags CRUD to CLI](https://github.com/shlinkio/shlink/issues/95)
|
||||||
|
* [59: Add tags CRUD to REST](https://github.com/shlinkio/shlink/issues/59)
|
||||||
|
* [66: Allow to import certain information from older app directory when updating](https://github.com/shlinkio/shlink/issues/66)
|
||||||
|
|
||||||
|
**Tasks**
|
||||||
|
|
||||||
|
* [96: Add namespace to functions](https://github.com/shlinkio/shlink/issues/96)
|
||||||
|
* [76: Add response examples to swagger docs](https://github.com/shlinkio/shlink/issues/76)
|
||||||
|
* [93: Improve cross domain management by using the ImplicitOptionsMiddleware](https://github.com/shlinkio/shlink/issues/93)
|
||||||
|
|
||||||
|
**Bugs**
|
||||||
|
|
||||||
|
* [92: Fix formatted dates, using an ISO compliant format](https://github.com/shlinkio/shlink/issues/92)
|
||||||
|
|
||||||
|
### 1.4.0
|
||||||
|
|
||||||
|
**Enhancements:**
|
||||||
|
|
||||||
|
* [89: Update to expressive 2](https://github.com/shlinkio/shlink/issues/89)
|
||||||
|
|
||||||
|
### 1.3.1
|
||||||
|
|
||||||
|
**Tasks**
|
||||||
|
|
||||||
|
* [82: Enable FastRoute routes cache](https://github.com/shlinkio/shlink/issues/82)
|
||||||
|
* [85: Update year in license file](https://github.com/shlinkio/shlink/issues/85)
|
||||||
|
* [81: Add docker containers config](https://github.com/shlinkio/shlink/issues/81)
|
||||||
|
|
||||||
|
**Bugs**
|
||||||
|
|
||||||
|
* [83: Short codes list: search in tags when filtering by query string](https://github.com/shlinkio/shlink/issues/83)
|
||||||
|
* [79: Increase the number of followed redirects](https://github.com/shlinkio/shlink/issues/79)
|
||||||
|
* [75: Apply PathVersionMiddleware only to rest routes defining it by configuration instead of code](https://github.com/shlinkio/shlink/issues/75)
|
||||||
|
* [77: Allow defining database server hostname and port](https://github.com/shlinkio/shlink/issues/77)
|
||||||
|
|
||||||
### 1.3.0
|
### 1.3.0
|
||||||
|
|
||||||
**Enhancements:**
|
**Enhancements:**
|
||||||
|
|
||||||
* [67: Allow to order the short codes list](https://github.com/acelaya/url-shortener/issues/67)
|
* [67: Allow to order the short codes list](https://github.com/shlinkio/shlink/issues/67)
|
||||||
* [60: Accept JSON requests in REST and use a body parser middleware to set the parsedBody](https://github.com/acelaya/url-shortener/issues/60)
|
* [60: Accept JSON requests in REST and use a body parser middleware to set the parsedBody](https://github.com/shlinkio/shlink/issues/60)
|
||||||
* [72: When listing API keys from CLI, display in yellow color enabled keys that have expired](https://github.com/acelaya/url-shortener/issues/72)
|
* [72: When listing API keys from CLI, display in yellow color enabled keys that have expired](https://github.com/shlinkio/shlink/issues/72)
|
||||||
* [58: Allow to filter short URLs by tag](https://github.com/acelaya/url-shortener/issues/58)
|
* [58: Allow to filter short URLs by tag](https://github.com/shlinkio/shlink/issues/58)
|
||||||
* [69: Allow to filter short codes by text query](https://github.com/acelaya/url-shortener/issues/69)
|
* [69: Allow to filter short codes by text query](https://github.com/shlinkio/shlink/issues/69)
|
||||||
|
|
||||||
**Tasks**
|
**Tasks**
|
||||||
|
|
||||||
* [73: Tag endpoints in swagger file](https://github.com/acelaya/url-shortener/issues/73)
|
* [73: Tag endpoints in swagger file](https://github.com/shlinkio/shlink/issues/73)
|
||||||
* [71: Separate swagger docs into multiple files](https://github.com/acelaya/url-shortener/issues/71)
|
* [71: Separate swagger docs into multiple files](https://github.com/shlinkio/shlink/issues/71)
|
||||||
* [63: Add path versioning to REST API routes](https://github.com/acelaya/url-shortener/issues/63)
|
* [63: Add path versioning to REST API routes](https://github.com/shlinkio/shlink/issues/63)
|
||||||
|
|
||||||
### 1.2.2
|
### 1.2.2
|
||||||
|
|
||||||
@@ -26,91 +65,91 @@
|
|||||||
|
|
||||||
**Bugs**
|
**Bugs**
|
||||||
|
|
||||||
* [62: Fix cross-domain requests in REST API](https://github.com/acelaya/url-shortener/issues/62)
|
* [62: Fix cross-domain requests in REST API](https://github.com/shlinkio/shlink/issues/62)
|
||||||
|
|
||||||
### 1.2.0
|
### 1.2.0
|
||||||
|
|
||||||
**Features**
|
**Features**
|
||||||
|
|
||||||
* [45: Allow to define tags on short codes, to improve filtering and classification](https://github.com/acelaya/url-shortener/issues/45)
|
* [45: Allow to define tags on short codes, to improve filtering and classification](https://github.com/shlinkio/shlink/issues/45)
|
||||||
* [7: Add website previews while listing available URLs](https://github.com/acelaya/url-shortener/issues/7)
|
* [7: Add website previews while listing available URLs](https://github.com/shlinkio/shlink/issues/7)
|
||||||
|
|
||||||
**Enhancements:**
|
**Enhancements:**
|
||||||
|
|
||||||
* [57: Add database migrations system to improve updating between versions](https://github.com/acelaya/url-shortener/issues/57)
|
* [57: Add database migrations system to improve updating between versions](https://github.com/shlinkio/shlink/issues/57)
|
||||||
* [31: Add support for other database management systems by improving the EntityManager factory](https://github.com/acelaya/url-shortener/issues/31)
|
* [31: Add support for other database management systems by improving the EntityManager factory](https://github.com/shlinkio/shlink/issues/31)
|
||||||
* [51: Generate build process to paquetize the app and ease distribution](https://github.com/acelaya/url-shortener/issues/51)
|
* [51: Generate build process to paquetize the app and ease distribution](https://github.com/shlinkio/shlink/issues/51)
|
||||||
* [38: Define installation script. It will request dynamic data on the fly so that there is no need to define env vars](https://github.com/acelaya/url-shortener/issues/38)
|
* [38: Define installation script. It will request dynamic data on the fly so that there is no need to define env vars](https://github.com/shlinkio/shlink/issues/38)
|
||||||
|
|
||||||
**Tasks**
|
**Tasks**
|
||||||
|
|
||||||
* [55: Create update script which does not try to create a new database](https://github.com/acelaya/url-shortener/issues/55)
|
* [55: Create update script which does not try to create a new database](https://github.com/shlinkio/shlink/issues/55)
|
||||||
* [54: Add cache namespace to prevent name collisions with other apps in the same environment](https://github.com/acelaya/url-shortener/issues/54)
|
* [54: Add cache namespace to prevent name collisions with other apps in the same environment](https://github.com/shlinkio/shlink/issues/54)
|
||||||
* [29: Use the acelaya/ze-content-based-error-handler package instead of custom error handler implementation](https://github.com/acelaya/url-shortener/issues/29)
|
* [29: Use the acelaya/ze-content-based-error-handler package instead of custom error handler implementation](https://github.com/shlinkio/shlink/issues/29)
|
||||||
|
|
||||||
**Bugs**
|
**Bugs**
|
||||||
|
|
||||||
* [53: Fix entities database interoperability](https://github.com/acelaya/url-shortener/issues/53)
|
* [53: Fix entities database interoperability](https://github.com/shlinkio/shlink/issues/53)
|
||||||
* [52: Add missing htaccess file for apache environments](https://github.com/acelaya/url-shortener/issues/52)
|
* [52: Add missing htaccess file for apache environments](https://github.com/shlinkio/shlink/issues/52)
|
||||||
|
|
||||||
### 1.1.0
|
### 1.1.0
|
||||||
|
|
||||||
**Features**
|
**Features**
|
||||||
|
|
||||||
* [46: Define a route that returns a QR code representing the shortened URL](https://github.com/acelaya/url-shortener/issues/46)
|
* [46: Define a route that returns a QR code representing the shortened URL](https://github.com/shlinkio/shlink/issues/46)
|
||||||
|
|
||||||
**Enhancements:**
|
**Enhancements:**
|
||||||
|
|
||||||
* [32: Add support for other cache adapters by improving the Cache factory](https://github.com/acelaya/url-shortener/issues/32)
|
* [32: Add support for other cache adapters by improving the Cache factory](https://github.com/shlinkio/shlink/issues/32)
|
||||||
* [14: https://github.com/shlinkio/shlink/issues/14](https://github.com/acelaya/url-shortener/issues/14)
|
* [14: https://github.com/shlinkio/shlink/issues/14](https://github.com/shlinkio/shlink/issues/14)
|
||||||
* [41: Cache the "short code" => "URL" map to prevent extra DB hits](https://github.com/acelaya/url-shortener/issues/41)
|
* [41: Cache the "short code" => "URL" map to prevent extra DB hits](https://github.com/shlinkio/shlink/issues/41)
|
||||||
* [13: Improve REST authentication](https://github.com/acelaya/url-shortener/issues/13)
|
* [13: Improve REST authentication](https://github.com/shlinkio/shlink/issues/13)
|
||||||
|
|
||||||
**Tasks**
|
**Tasks**
|
||||||
|
|
||||||
* [39: Change copyright from "Alejandro Celaya" to "Shlink" in error pages](https://github.com/acelaya/url-shortener/issues/39)
|
* [39: Change copyright from "Alejandro Celaya" to "Shlink" in error pages](https://github.com/shlinkio/shlink/issues/39)
|
||||||
* [42: Make REST endpoints that need to find something return a 404 when "something" is not found](https://github.com/acelaya/url-shortener/issues/42)
|
* [42: Make REST endpoints that need to find something return a 404 when "something" is not found](https://github.com/shlinkio/shlink/issues/42)
|
||||||
* [35: Make CLI commands to use the same PHP namespace as the one used for the command name](https://github.com/acelaya/url-shortener/issues/35)
|
* [35: Make CLI commands to use the same PHP namespace as the one used for the command name](https://github.com/shlinkio/shlink/issues/35)
|
||||||
|
|
||||||
**Bugs**
|
**Bugs**
|
||||||
|
|
||||||
* [40: Take into account the X-Forwarded-For header in order to get the visitor information, in case the server is behind a load balancer or proxy](https://github.com/acelaya/url-shortener/issues/40)
|
* [40: Take into account the X-Forwarded-For header in order to get the visitor information, in case the server is behind a load balancer or proxy](https://github.com/shlinkio/shlink/issues/40)
|
||||||
|
|
||||||
### 1.0.0
|
### 1.0.0
|
||||||
|
|
||||||
**Enhancements:**
|
**Enhancements:**
|
||||||
|
|
||||||
* [33: Create a command to generate a short code charset by randomizing the default one](https://github.com/acelaya/url-shortener/issues/33)
|
* [33: Create a command to generate a short code charset by randomizing the default one](https://github.com/shlinkio/shlink/issues/33)
|
||||||
* [15: Return JSON/HTML responses for errors (4xx and 5xx) based on accept header (content negotiation)](https://github.com/acelaya/url-shortener/issues/15)
|
* [15: Return JSON/HTML responses for errors (4xx and 5xx) based on accept header (content negotiation)](https://github.com/shlinkio/shlink/issues/15)
|
||||||
* [23: Translate application literals](https://github.com/acelaya/url-shortener/issues/23)
|
* [23: Translate application literals](https://github.com/shlinkio/shlink/issues/23)
|
||||||
* [21: Allow to filter visits by date range](https://github.com/acelaya/url-shortener/issues/21)
|
* [21: Allow to filter visits by date range](https://github.com/shlinkio/shlink/issues/21)
|
||||||
* [22: Save visits locations data on a visit_locations table](https://github.com/acelaya/url-shortener/issues/22)
|
* [22: Save visits locations data on a visit_locations table](https://github.com/shlinkio/shlink/issues/22)
|
||||||
* [20: Inject cross domain headers in response only if the Origin header is present in the request](https://github.com/acelaya/url-shortener/issues/20)
|
* [20: Inject cross domain headers in response only if the Origin header is present in the request](https://github.com/shlinkio/shlink/issues/20)
|
||||||
* [11: Separate code into multiple modules](https://github.com/acelaya/url-shortener/issues/11)
|
* [11: Separate code into multiple modules](https://github.com/shlinkio/shlink/issues/11)
|
||||||
* [18: Group routable middleware in an Action namespace](https://github.com/acelaya/url-shortener/issues/18)
|
* [18: Group routable middleware in an Action namespace](https://github.com/shlinkio/shlink/issues/18)
|
||||||
|
|
||||||
**Tasks**
|
**Tasks**
|
||||||
|
|
||||||
* [36: Remove hhvm from the CI matrix since it doesn't support array constants and will fail](https://github.com/acelaya/url-shortener/issues/36)
|
* [36: Remove hhvm from the CI matrix since it doesn't support array constants and will fail](https://github.com/shlinkio/shlink/issues/36)
|
||||||
* [4: Installation steps](https://github.com/acelaya/url-shortener/issues/4)
|
* [4: Installation steps](https://github.com/shlinkio/shlink/issues/4)
|
||||||
* [6: Remove dependency on expressive helpers package](https://github.com/acelaya/url-shortener/issues/6)
|
* [6: Remove dependency on expressive helpers package](https://github.com/shlinkio/shlink/issues/6)
|
||||||
* [30: Replace the "services" first level config entry by "dependencies", in order to fulfill default Expressive name](https://github.com/acelaya/url-shortener/issues/30)
|
* [30: Replace the "services" first level config entry by "dependencies", in order to fulfill default Expressive name](https://github.com/shlinkio/shlink/issues/30)
|
||||||
* [12: Improve code coverage](https://github.com/acelaya/url-shortener/issues/12)
|
* [12: Improve code coverage](https://github.com/shlinkio/shlink/issues/12)
|
||||||
* [25: Replace "Middleware" suffix on routable middlewares by "Action"](https://github.com/acelaya/url-shortener/issues/25)
|
* [25: Replace "Middleware" suffix on routable middlewares by "Action"](https://github.com/shlinkio/shlink/issues/25)
|
||||||
* [19: Update the vendor and app namespace from Acelaya\UrlShortener to Shlinkio\Shlink](https://github.com/acelaya/url-shortener/issues/19)
|
* [19: Update the vendor and app namespace from Acelaya\UrlShortener to Shlinkio\Shlink](https://github.com/shlinkio/shlink/issues/19)
|
||||||
|
|
||||||
**Bugs**
|
**Bugs**
|
||||||
|
|
||||||
* [24: Prevent duplicated shortcodes errors because of the case insensitive behavior on MySQL](https://github.com/acelaya/url-shortener/issues/24)
|
* [24: Prevent duplicated shortcodes errors because of the case insensitive behavior on MySQL](https://github.com/shlinkio/shlink/issues/24)
|
||||||
|
|
||||||
### 0.2.0
|
### 0.2.0
|
||||||
|
|
||||||
**Enhancements:**
|
**Enhancements:**
|
||||||
|
|
||||||
* [9: Use symfony/console to dispatch console requests, instead of trying to integrate the process with expressive](https://github.com/acelaya/url-shortener/issues/9)
|
* [9: Use symfony/console to dispatch console requests, instead of trying to integrate the process with expressive](https://github.com/shlinkio/shlink/issues/9)
|
||||||
* [8: Create a REST API](https://github.com/acelaya/url-shortener/issues/8)
|
* [8: Create a REST API](https://github.com/shlinkio/shlink/issues/8)
|
||||||
* [10: Add more CLI functionality](https://github.com/acelaya/url-shortener/issues/10)
|
* [10: Add more CLI functionality](https://github.com/shlinkio/shlink/issues/10)
|
||||||
|
|
||||||
**Tasks**
|
**Tasks**
|
||||||
|
|
||||||
* [5: Create CHANGELOG file](https://github.com/acelaya/url-shortener/issues/5)
|
* [5: Create CHANGELOG file](https://github.com/shlinkio/shlink/issues/5)
|
||||||
|
|||||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2016 Alejandro Celaya
|
Copyright (c) 2017 Alejandro Celaya
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
5
bin/cli
5
bin/cli
@@ -5,7 +5,4 @@ use Symfony\Component\Console\Application as CliApp;
|
|||||||
|
|
||||||
/** @var ContainerInterface $container */
|
/** @var ContainerInterface $container */
|
||||||
$container = include __DIR__ . '/../config/container.php';
|
$container = include __DIR__ . '/../config/container.php';
|
||||||
|
$container->get(CliApp::class)->run();
|
||||||
/** @var CliApp $app */
|
|
||||||
$app = $container->get(CliApp::class);
|
|
||||||
$app->run();
|
|
||||||
|
|||||||
17
bin/install
17
bin/install
@@ -1,14 +1,19 @@
|
|||||||
#!/usr/bin/env php
|
#!/usr/bin/env php
|
||||||
<?php
|
<?php
|
||||||
use Shlinkio\Shlink\CLI\Command\Install\InstallCommand;
|
use Shlinkio\Shlink\CLI\Factory\InstallApplicationFactory;
|
||||||
use Symfony\Component\Console\Application;
|
use Symfony\Component\Console\Application;
|
||||||
use Zend\Config\Writer\PhpArray;
|
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||||
|
use Symfony\Component\Filesystem\Filesystem;
|
||||||
|
use Zend\ServiceManager\Factory\InvokableFactory;
|
||||||
|
use Zend\ServiceManager\ServiceManager;
|
||||||
|
|
||||||
chdir(dirname(__DIR__));
|
chdir(dirname(__DIR__));
|
||||||
|
|
||||||
require __DIR__ . '/../vendor/autoload.php';
|
require __DIR__ . '/../vendor/autoload.php';
|
||||||
|
|
||||||
$app = new Application();
|
$container = new ServiceManager(['factories' => [
|
||||||
$app->add(new InstallCommand(new PhpArray()));
|
Application::class => InstallApplicationFactory::class,
|
||||||
$app->setDefaultCommand('shlink:install');
|
Filesystem::class => InvokableFactory::class,
|
||||||
$app->run();
|
QuestionHelper::class => InvokableFactory::class,
|
||||||
|
]]);
|
||||||
|
$container->build(Application::class)->run();
|
||||||
|
|||||||
17
bin/update
17
bin/update
@@ -1,14 +1,19 @@
|
|||||||
#!/usr/bin/env php
|
#!/usr/bin/env php
|
||||||
<?php
|
<?php
|
||||||
use Shlinkio\Shlink\CLI\Command\Install\UpdateCommand;
|
use Shlinkio\Shlink\CLI\Factory\InstallApplicationFactory;
|
||||||
use Symfony\Component\Console\Application;
|
use Symfony\Component\Console\Application;
|
||||||
use Zend\Config\Writer\PhpArray;
|
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||||
|
use Symfony\Component\Filesystem\Filesystem;
|
||||||
|
use Zend\ServiceManager\Factory\InvokableFactory;
|
||||||
|
use Zend\ServiceManager\ServiceManager;
|
||||||
|
|
||||||
chdir(dirname(__DIR__));
|
chdir(dirname(__DIR__));
|
||||||
|
|
||||||
require __DIR__ . '/../vendor/autoload.php';
|
require __DIR__ . '/../vendor/autoload.php';
|
||||||
|
|
||||||
$app = new Application();
|
$container = new ServiceManager(['factories' => [
|
||||||
$app->add(new UpdateCommand(new PhpArray()));
|
Application::class => InstallApplicationFactory::class,
|
||||||
$app->setDefaultCommand('shlink:install');
|
Filesystem::class => InvokableFactory::class,
|
||||||
$app->run();
|
QuestionHelper::class => InvokableFactory::class,
|
||||||
|
]]);
|
||||||
|
$container->build(Application::class, ['isUpdate' => true])->run();
|
||||||
|
|||||||
10
build.sh
10
build.sh
@@ -8,13 +8,14 @@ if [ "$#" -ne 1 ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
version=$1
|
version=$1
|
||||||
builtcontent=$(readlink -f '../shlink_build_tmp')
|
builtcontent=$(readlink -f "../shlink_${version}_dist")
|
||||||
projectdir=$(pwd)
|
projectdir=$(pwd)
|
||||||
|
|
||||||
# Copy project content to temp dir
|
# Copy project content to temp dir
|
||||||
echo 'Copying project files...'
|
echo 'Copying project files...'
|
||||||
rm -rf "${builtcontent}"
|
rm -rf "${builtcontent}"
|
||||||
mkdir "${builtcontent}"
|
mkdir "${builtcontent}"
|
||||||
|
sudo chmod -R 777 "${projectdir}"/data/infra/{database,nginx}
|
||||||
cp -R "${projectdir}"/* "${builtcontent}"
|
cp -R "${projectdir}"/* "${builtcontent}"
|
||||||
cd "${builtcontent}"
|
cd "${builtcontent}"
|
||||||
|
|
||||||
@@ -22,7 +23,7 @@ cd "${builtcontent}"
|
|||||||
rm -r vendor
|
rm -r vendor
|
||||||
rm composer.lock
|
rm composer.lock
|
||||||
composer self-update
|
composer self-update
|
||||||
composer install --no-dev --optimize-autoloader
|
composer install --no-dev --optimize-autoloader --no-progress --no-interaction
|
||||||
|
|
||||||
# Delete development files
|
# Delete development files
|
||||||
echo 'Deleting dev files...'
|
echo 'Deleting dev files...'
|
||||||
@@ -30,15 +31,18 @@ rm build.sh
|
|||||||
rm CHANGELOG.md
|
rm CHANGELOG.md
|
||||||
rm composer.*
|
rm composer.*
|
||||||
rm LICENSE
|
rm LICENSE
|
||||||
|
rm indocker
|
||||||
|
rm docker-compose.yml
|
||||||
rm php*
|
rm php*
|
||||||
rm README.md
|
rm README.md
|
||||||
rm -r build
|
rm -r build
|
||||||
rm -f data/database.sqlite
|
rm -f data/database.sqlite
|
||||||
|
rm -rf data/infra
|
||||||
rm -rf data/{cache,log,proxies}/{*,.gitignore}
|
rm -rf data/{cache,log,proxies}/{*,.gitignore}
|
||||||
rm -rf config/params/{*,.gitignore}
|
rm -rf config/params/{*,.gitignore}
|
||||||
rm -rf config/autoload/{{,*.}local.php{,.dist},.gitignore}
|
rm -rf config/autoload/{{,*.}local.php{,.dist},.gitignore}
|
||||||
|
|
||||||
# Compressing file
|
# Compressing file
|
||||||
rm -f "${projectdir}"/build/shlink_${version}_dist.zip
|
rm -f "${projectdir}"/build/shlink_${version}_dist.zip
|
||||||
zip -ry "${projectdir}"/build/shlink_${version}_dist.zip .
|
zip -ry "${projectdir}"/build/shlink_${version}_dist.zip "../shlink_${version}_dist"
|
||||||
rm -rf "${builtcontent}"
|
rm -rf "${builtcontent}"
|
||||||
|
|||||||
@@ -1,29 +1,29 @@
|
|||||||
{
|
{
|
||||||
"name": "shlinkio/shlink",
|
"name": "shlinkio/shlink",
|
||||||
"type": "project",
|
"type": "project",
|
||||||
"homepage": "http://shlink.io",
|
"homepage": "https://shlink.io",
|
||||||
"description": "A self-hosted and PHP-based URL shortener application with CLI and REST interfaces",
|
"description": "A self-hosted and PHP-based URL shortener application with CLI and REST interfaces",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
"name": "Alejandro Celaya Alastrué",
|
"name": "Alejandro Celaya Alastrué",
|
||||||
"homepage": "http://www.alejandrocelaya.com",
|
"homepage": "https://www.alejandrocelaya.com",
|
||||||
"email": "alejandro@alejandrocelaya.com"
|
"email": "alejandro@alejandrocelaya.com"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^5.6 || ^7.0",
|
"php": "^5.6 || ^7.0",
|
||||||
"zendframework/zend-expressive": "^1.0",
|
"zendframework/zend-expressive": "^2.0",
|
||||||
"zendframework/zend-expressive-fastroute": "^1.1",
|
"zendframework/zend-expressive-fastroute": "^2.0",
|
||||||
"zendframework/zend-expressive-twigrenderer": "^1.0",
|
"zendframework/zend-expressive-twigrenderer": "^1.4",
|
||||||
"zendframework/zend-stdlib": "^2.7",
|
"zendframework/zend-stdlib": "^3.0",
|
||||||
"zendframework/zend-servicemanager": "^3.0",
|
"zendframework/zend-servicemanager": "^3.0",
|
||||||
"zendframework/zend-paginator": "^2.6",
|
"zendframework/zend-paginator": "^2.6",
|
||||||
"zendframework/zend-config": "^2.6",
|
"zendframework/zend-config": "^3.0",
|
||||||
"zendframework/zend-i18n": "^2.7",
|
"zendframework/zend-i18n": "^2.7",
|
||||||
"mtymek/expressive-config-manager": "^0.4",
|
"zendframework/zend-config-aggregator": "^0.1",
|
||||||
"acelaya/zsm-annotated-services": "^0.2.0",
|
"acelaya/zsm-annotated-services": "^1.0",
|
||||||
"acelaya/ze-content-based-error-handler": "^1.0",
|
"acelaya/ze-content-based-error-handler": "^2.0",
|
||||||
"doctrine/orm": "^2.5",
|
"doctrine/orm": "^2.5",
|
||||||
"guzzlehttp/guzzle": "^6.2",
|
"guzzlehttp/guzzle": "^6.2",
|
||||||
"symfony/console": "^3.0",
|
"symfony/console": "^3.0",
|
||||||
@@ -37,13 +37,13 @@
|
|||||||
"doctrine/migrations": "^1.4"
|
"doctrine/migrations": "^1.4"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^5.0",
|
"phpunit/phpunit": "^5.7 || ^6.0",
|
||||||
"squizlabs/php_codesniffer": "^2.3",
|
"squizlabs/php_codesniffer": "^2.3",
|
||||||
"roave/security-advisories": "dev-master",
|
"roave/security-advisories": "dev-master",
|
||||||
"filp/whoops": "^2.0",
|
"filp/whoops": "^2.0",
|
||||||
"symfony/var-dumper": "^3.0",
|
"symfony/var-dumper": "^3.0",
|
||||||
"vlucas/phpdotenv": "^2.2",
|
"vlucas/phpdotenv": "^2.2",
|
||||||
"phly/changelog-generator": "^2.1"
|
"zendframework/zend-expressive-tooling": "^0.4"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
|
use Shlinkio\Shlink\Common;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
'app_options' => [
|
'app_options' => [
|
||||||
'name' => 'Shlink',
|
'name' => 'Shlink',
|
||||||
'version' => '1.2.0',
|
'version' => '1.2.0',
|
||||||
'secret_key' => env('SECRET_KEY'),
|
'secret_key' => Common\env('SECRET_KEY'),
|
||||||
],
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,21 +1,23 @@
|
|||||||
<?php
|
<?php
|
||||||
|
use Shlinkio\Shlink\Common\Factory\EmptyResponseImplicitOptionsMiddlewareFactory;
|
||||||
use Zend\Expressive;
|
use Zend\Expressive;
|
||||||
use Zend\Expressive\Container;
|
use Zend\Expressive\Container;
|
||||||
|
use Zend\Expressive\Middleware;
|
||||||
use Zend\Expressive\Router;
|
use Zend\Expressive\Router;
|
||||||
use Zend\Expressive\Template;
|
use Zend\Expressive\Template;
|
||||||
use Zend\Expressive\Twig;
|
use Zend\Expressive\Twig;
|
||||||
use Zend\ServiceManager\Factory\InvokableFactory;
|
use Zend\Stratigility\Middleware\ErrorHandler;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
'dependencies' => [
|
'dependencies' => [
|
||||||
'factories' => [
|
'factories' => [
|
||||||
Expressive\Application::class => Container\ApplicationFactory::class,
|
Expressive\Application::class => Container\ApplicationFactory::class,
|
||||||
Router\FastRouteRouter::class => InvokableFactory::class,
|
|
||||||
Template\TemplateRendererInterface::class => Twig\TwigRendererFactory::class,
|
Template\TemplateRendererInterface::class => Twig\TwigRendererFactory::class,
|
||||||
],
|
\Twig_Environment::class => Twig\TwigEnvironmentFactory::class,
|
||||||
'aliases' => [
|
Router\RouterInterface::class => Router\FastRouteRouterFactory::class,
|
||||||
Router\RouterInterface::class => Router\FastRouteRouter::class,
|
ErrorHandler::class => Container\ErrorHandlerFactory::class,
|
||||||
|
Middleware\ImplicitOptionsMiddleware::class => EmptyResponseImplicitOptionsMiddlewareFactory::class,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
use Shlinkio\Shlink\Common;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
'entity_manager' => [
|
'entity_manager' => [
|
||||||
@@ -6,14 +8,10 @@ return [
|
|||||||
'proxies_dir' => 'data/proxies',
|
'proxies_dir' => 'data/proxies',
|
||||||
],
|
],
|
||||||
'connection' => [
|
'connection' => [
|
||||||
'driver' => 'pdo_mysql',
|
'user' => Common\env('DB_USER'),
|
||||||
'user' => env('DB_USER'),
|
'password' => Common\env('DB_PASSWORD'),
|
||||||
'password' => env('DB_PASSWORD'),
|
'dbname' => Common\env('DB_NAME', 'shlink'),
|
||||||
'dbname' => env('DB_NAME', 'shlink'),
|
|
||||||
'charset' => 'utf8',
|
'charset' => 'utf8',
|
||||||
'driverOptions' => [
|
|
||||||
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
14
config/autoload/entity-manager.local.php.dist
Normal file
14
config/autoload/entity-manager.local.php.dist
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
|
||||||
|
'entity_manager' => [
|
||||||
|
'connection' => [
|
||||||
|
'driver' => 'pdo_mysql',
|
||||||
|
'host' => 'shlink_db',
|
||||||
|
'driverOptions' => [
|
||||||
|
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
];
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
use Acelaya\ExpressiveErrorHandler\ErrorHandler\ContentBasedErrorHandler;
|
use Zend\Expressive\Container\WhoopsErrorResponseGeneratorFactory;
|
||||||
use Zend\Expressive\Container\WhoopsErrorHandlerFactory;
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'dependencies' => [
|
'dependencies' => [
|
||||||
@@ -21,7 +20,7 @@ return [
|
|||||||
'error_handler' => [
|
'error_handler' => [
|
||||||
'plugins' => [
|
'plugins' => [
|
||||||
'factories' => [
|
'factories' => [
|
||||||
ContentBasedErrorHandler::DEFAULT_CONTENT => WhoopsErrorHandlerFactory::class,
|
'text/html' => WhoopsErrorResponseGeneratorFactory::class,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,19 +1,51 @@
|
|||||||
<?php
|
<?php
|
||||||
use Zend\Expressive\Container\ApplicationFactory;
|
use Shlinkio\Shlink\Common\Middleware\LocaleMiddleware;
|
||||||
|
use Shlinkio\Shlink\Rest\Middleware\BodyParserMiddleware;
|
||||||
|
use Shlinkio\Shlink\Rest\Middleware\CheckAuthenticationMiddleware;
|
||||||
|
use Shlinkio\Shlink\Rest\Middleware\CrossDomainMiddleware;
|
||||||
|
use Shlinkio\Shlink\Rest\Middleware\PathVersionMiddleware;
|
||||||
|
use Zend\Expressive;
|
||||||
|
use Zend\Stratigility\Middleware\ErrorHandler;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
'middleware_pipeline' => [
|
'middleware_pipeline' => [
|
||||||
|
'pre-routing' => [
|
||||||
|
'middleware' => [
|
||||||
|
ErrorHandler::class,
|
||||||
|
LocaleMiddleware::class,
|
||||||
|
],
|
||||||
|
'priority' => 11,
|
||||||
|
],
|
||||||
|
'pre-routing-rest' => [
|
||||||
|
'path' => '/rest',
|
||||||
|
'middleware' => [
|
||||||
|
PathVersionMiddleware::class,
|
||||||
|
],
|
||||||
|
'priority' => 11,
|
||||||
|
],
|
||||||
|
|
||||||
'routing' => [
|
'routing' => [
|
||||||
'middleware' => [
|
'middleware' => [
|
||||||
ApplicationFactory::ROUTING_MIDDLEWARE,
|
Expressive\Application::ROUTING_MIDDLEWARE,
|
||||||
],
|
],
|
||||||
'priority' => 10,
|
'priority' => 10,
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'rest' => [
|
||||||
|
'path' => '/rest',
|
||||||
|
'middleware' => [
|
||||||
|
CrossDomainMiddleware::class,
|
||||||
|
Expressive\Middleware\ImplicitOptionsMiddleware::class,
|
||||||
|
BodyParserMiddleware::class,
|
||||||
|
CheckAuthenticationMiddleware::class,
|
||||||
|
],
|
||||||
|
'priority' => 5,
|
||||||
|
],
|
||||||
|
|
||||||
'post-routing' => [
|
'post-routing' => [
|
||||||
'middleware' => [
|
'middleware' => [
|
||||||
ApplicationFactory::DISPATCH_MIDDLEWARE,
|
Expressive\Application::DISPATCH_MIDDLEWARE,
|
||||||
],
|
],
|
||||||
'priority' => 1,
|
'priority' => 1,
|
||||||
],
|
],
|
||||||
|
|||||||
13
config/autoload/router.global.php
Normal file
13
config/autoload/router.global.php
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
use Zend\Expressive\Router\FastRouteRouter;
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
'router' => [
|
||||||
|
'fastroute' => [
|
||||||
|
FastRouteRouter::CONFIG_CACHE_ENABLED => true,
|
||||||
|
FastRouteRouter::CONFIG_CACHE_FILE => 'data/cache/fastroute_cached_routes.php',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
];
|
||||||
12
config/autoload/router.local.php.dist
Normal file
12
config/autoload/router.local.php.dist
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
use Zend\Expressive\Router\FastRouteRouter;
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
'router' => [
|
||||||
|
'fastroute' => [
|
||||||
|
FastRouteRouter::CONFIG_CACHE_ENABLED => false,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
];
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
|
use Shlinkio\Shlink\Common;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
'translator' => [
|
'translator' => [
|
||||||
'locale' => env('DEFAULT_LOCALE', 'en'),
|
'locale' => Common\env('DEFAULT_LOCALE', 'en'),
|
||||||
],
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
<?php
|
<?php
|
||||||
|
use Shlinkio\Shlink\Common;
|
||||||
use Shlinkio\Shlink\Core\Service\UrlShortener;
|
use Shlinkio\Shlink\Core\Service\UrlShortener;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
'url_shortener' => [
|
'url_shortener' => [
|
||||||
'domain' => [
|
'domain' => [
|
||||||
'schema' => env('SHORTENED_URL_SCHEMA', 'http'),
|
'schema' => Common\env('SHORTENED_URL_SCHEMA', 'http'),
|
||||||
'hostname' => env('SHORTENED_URL_HOSTNAME'),
|
'hostname' => Common\env('SHORTENED_URL_HOSTNAME'),
|
||||||
],
|
],
|
||||||
'shortcode_chars' => env('SHORTCODE_CHARS', UrlShortener::DEFAULT_CHARS),
|
'shortcode_chars' => Common\env('SHORTCODE_CHARS', UrlShortener::DEFAULT_CHARS),
|
||||||
],
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use Shlinkio\Shlink\CLI;
|
|||||||
use Shlinkio\Shlink\Common;
|
use Shlinkio\Shlink\Common;
|
||||||
use Shlinkio\Shlink\Core;
|
use Shlinkio\Shlink\Core;
|
||||||
use Shlinkio\Shlink\Rest;
|
use Shlinkio\Shlink\Rest;
|
||||||
use Zend\Expressive\ConfigManager;
|
use Zend\ConfigAggregator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration files are loaded in a specific order. First ``global.php``, then ``*.global.php``.
|
* Configuration files are loaded in a specific order. First ``global.php``, then ``*.global.php``.
|
||||||
@@ -15,11 +15,11 @@ use Zend\Expressive\ConfigManager;
|
|||||||
* Obviously, if you use closures in your config you can't cache it.
|
* Obviously, if you use closures in your config you can't cache it.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
return (new ConfigManager\ConfigManager([
|
return (new ConfigAggregator\ConfigAggregator([
|
||||||
ExpressiveErrorHandler\ConfigProvider::class,
|
ExpressiveErrorHandler\ConfigProvider::class,
|
||||||
Common\ConfigProvider::class,
|
Common\ConfigProvider::class,
|
||||||
Core\ConfigProvider::class,
|
Core\ConfigProvider::class,
|
||||||
CLI\ConfigProvider::class,
|
CLI\ConfigProvider::class,
|
||||||
Rest\ConfigProvider::class,
|
Rest\ConfigProvider::class,
|
||||||
new ConfigManager\ZendConfigProvider('config/{autoload/{{,*.}global,{,*.}local},params/generated_config}.php'),
|
new ConfigAggregator\ZendConfigProvider('config/{autoload/{{,*.}global,{,*.}local},params/generated_config}.php'),
|
||||||
], 'data/cache/app_config.php'))->getMergedConfig();
|
], 'data/cache/app_config.php'))->getMergedConfig();
|
||||||
|
|||||||
2
data/infra/database/.gitignore
vendored
Normal file
2
data/infra/database/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
6
data/infra/db.Dockerfile
Normal file
6
data/infra/db.Dockerfile
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
FROM mysql:5.7
|
||||||
|
MAINTAINER Alejandro Celaya <alejandro@alejandrocelaya.com>
|
||||||
|
|
||||||
|
# Enable remote access (default is localhost only, we change this
|
||||||
|
# otherwise our database would not be reachable from outside the container)
|
||||||
|
RUN sed -i -e"s/^bind-address\s*=\s*127.0.0.1/bind-address = 0.0.0.0/" /etc/mysql/my.cnf
|
||||||
5
data/infra/nginx.Dockerfile
Normal file
5
data/infra/nginx.Dockerfile
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
FROM nginx:1.11.6-alpine
|
||||||
|
MAINTAINER Alejandro Celaya <alejandro@alejandrocelaya.com>
|
||||||
|
|
||||||
|
# Delete default nginx vhost
|
||||||
|
RUN rm /etc/nginx/conf.d/default.conf
|
||||||
2
data/infra/nginx/.gitignore
vendored
Normal file
2
data/infra/nginx/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
87
data/infra/php.Dockerfile
Normal file
87
data/infra/php.Dockerfile
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
FROM php:7.1-fpm-alpine
|
||||||
|
MAINTAINER Alejandro Celaya <alejandro@alejandrocelaya.com>
|
||||||
|
|
||||||
|
RUN apk update
|
||||||
|
|
||||||
|
# Install common php extensions
|
||||||
|
RUN docker-php-ext-install pdo_mysql
|
||||||
|
RUN docker-php-ext-install iconv
|
||||||
|
RUN docker-php-ext-install mbstring
|
||||||
|
RUN docker-php-ext-install calendar
|
||||||
|
|
||||||
|
RUN apk add --no-cache --virtual sqlite-libs
|
||||||
|
RUN apk add --no-cache --virtual sqlite-dev
|
||||||
|
RUN docker-php-ext-install pdo_sqlite
|
||||||
|
|
||||||
|
RUN apk add --no-cache --virtual icu-dev
|
||||||
|
RUN docker-php-ext-install intl
|
||||||
|
|
||||||
|
RUN apk add --no-cache --virtual zlib-dev
|
||||||
|
RUN docker-php-ext-install zip
|
||||||
|
|
||||||
|
RUN apk add --no-cache --virtual libmcrypt-dev
|
||||||
|
RUN docker-php-ext-install mcrypt
|
||||||
|
|
||||||
|
RUN apk add --no-cache --virtual libpng-dev
|
||||||
|
RUN docker-php-ext-install gd
|
||||||
|
|
||||||
|
# Install redis extension
|
||||||
|
ADD https://github.com/phpredis/phpredis/archive/php7.tar.gz /tmp/phpredis.tar.gz
|
||||||
|
RUN mkdir -p /usr/src/php/ext/redis\
|
||||||
|
&& tar xf /tmp/phpredis.tar.gz -C /usr/src/php/ext/redis --strip-components=1
|
||||||
|
# configure and install
|
||||||
|
RUN docker-php-ext-configure redis\
|
||||||
|
&& docker-php-ext-install redis
|
||||||
|
# cleanup
|
||||||
|
RUN rm /tmp/phpredis.tar.gz
|
||||||
|
|
||||||
|
# Install memcached extension
|
||||||
|
RUN apk add --no-cache --virtual cyrus-sasl-dev
|
||||||
|
RUN apk add --no-cache --virtual libmemcached-dev
|
||||||
|
ADD https://github.com/php-memcached-dev/php-memcached/archive/php7.tar.gz /tmp/memcached.tar.gz
|
||||||
|
RUN mkdir -p /usr/src/php/ext/memcached\
|
||||||
|
&& tar xf /tmp/memcached.tar.gz -C /usr/src/php/ext/memcached --strip-components=1
|
||||||
|
# configure and install
|
||||||
|
RUN docker-php-ext-configure memcached\
|
||||||
|
&& docker-php-ext-install memcached
|
||||||
|
# cleanup
|
||||||
|
RUN rm /tmp/memcached.tar.gz
|
||||||
|
|
||||||
|
# Install APCu extension
|
||||||
|
ADD https://pecl.php.net/get/apcu-5.1.3.tgz /tmp/apcu.tar.gz
|
||||||
|
RUN mkdir -p /usr/src/php/ext/apcu\
|
||||||
|
&& tar xf /tmp/apcu.tar.gz -C /usr/src/php/ext/apcu --strip-components=1
|
||||||
|
# configure and install
|
||||||
|
RUN docker-php-ext-configure apcu\
|
||||||
|
&& docker-php-ext-install apcu
|
||||||
|
# cleanup
|
||||||
|
RUN rm /tmp/apcu.tar.gz
|
||||||
|
|
||||||
|
# Install APCu-BC extension
|
||||||
|
ADD https://pecl.php.net/get/apcu_bc-1.0.3.tgz /tmp/apcu_bc.tar.gz
|
||||||
|
RUN mkdir -p /usr/src/php/ext/apcu-bc\
|
||||||
|
&& tar xf /tmp/apcu_bc.tar.gz -C /usr/src/php/ext/apcu-bc --strip-components=1
|
||||||
|
# configure and install
|
||||||
|
RUN docker-php-ext-configure apcu-bc\
|
||||||
|
&& docker-php-ext-install apcu-bc
|
||||||
|
# cleanup
|
||||||
|
RUN rm /tmp/apcu_bc.tar.gz
|
||||||
|
|
||||||
|
# Load APCU.ini before APC.ini
|
||||||
|
RUN rm /usr/local/etc/php/conf.d/docker-php-ext-apcu.ini
|
||||||
|
RUN echo extension=apcu.so > /usr/local/etc/php/conf.d/20-php-ext-apcu.ini
|
||||||
|
|
||||||
|
# Install xdebug
|
||||||
|
ADD https://pecl.php.net/get/xdebug-2.5.0 /tmp/xdebug.tar.gz
|
||||||
|
RUN mkdir -p /usr/src/php/ext/xdebug\
|
||||||
|
&& tar xf /tmp/xdebug.tar.gz -C /usr/src/php/ext/xdebug --strip-components=1
|
||||||
|
# configure and install
|
||||||
|
RUN docker-php-ext-configure xdebug\
|
||||||
|
&& docker-php-ext-install xdebug
|
||||||
|
# cleanup
|
||||||
|
RUN rm /tmp/xdebug.tar.gz
|
||||||
|
|
||||||
|
# Install composer
|
||||||
|
RUN php -r "readfile('https://getcomposer.org/installer');" | php
|
||||||
|
RUN chmod +x composer.phar
|
||||||
|
RUN mv composer.phar /usr/local/bin/composer
|
||||||
1
data/infra/php.ini
Normal file
1
data/infra/php.ini
Normal file
@@ -0,0 +1 @@
|
|||||||
|
date.timezone = Europe/Madrid
|
||||||
21
data/infra/vhost.conf
Normal file
21
data/infra/vhost.conf
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
server {
|
||||||
|
listen 80 default_server;
|
||||||
|
server_name shlink.local;
|
||||||
|
root /home/shlink/www/public;
|
||||||
|
index index.php;
|
||||||
|
|
||||||
|
charset utf-8;
|
||||||
|
error_log /home/shlink/www/data/infra/nginx/shlink.error.log;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.php$is_args$args;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ \.php$ {
|
||||||
|
root /home/shlink/www/public;
|
||||||
|
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||||
|
fastcgi_pass shlink_php:9000;
|
||||||
|
fastcgi_index index.php;
|
||||||
|
include fastcgi.conf;
|
||||||
|
}
|
||||||
|
}
|
||||||
0
data/proxies/.gitignore
vendored
Normal file → Executable file
0
data/proxies/.gitignore
vendored
Normal file → Executable file
41
docker-compose.yml
Normal file
41
docker-compose.yml
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
version: '2'
|
||||||
|
|
||||||
|
services:
|
||||||
|
shlink_nginx:
|
||||||
|
container_name: shlink_nginx
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: ./data/infra/nginx.Dockerfile
|
||||||
|
ports:
|
||||||
|
- "8000:80"
|
||||||
|
volumes:
|
||||||
|
- ./:/home/shlink/www
|
||||||
|
- ./docs:/home/shlink/www/public/docs
|
||||||
|
- ./data/infra/vhost.conf:/etc/nginx/conf.d/shlink-vhost.conf
|
||||||
|
links:
|
||||||
|
- shlink_php
|
||||||
|
|
||||||
|
shlink_php:
|
||||||
|
container_name: shlink_php
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: ./data/infra/php.Dockerfile
|
||||||
|
volumes:
|
||||||
|
- ./:/home/shlink/www
|
||||||
|
- ./data/infra/php.ini:/usr/local/etc/php/php.ini
|
||||||
|
links:
|
||||||
|
- shlink_db
|
||||||
|
|
||||||
|
shlink_db:
|
||||||
|
container_name: shlink_db
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: ./data/infra/db.Dockerfile
|
||||||
|
ports:
|
||||||
|
- "3307:3306"
|
||||||
|
volumes:
|
||||||
|
- ./:/home/shlink/www
|
||||||
|
- ./data/infra/database:/var/lib/mysql
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: root
|
||||||
|
MYSQL_DATABASE: shlink
|
||||||
@@ -25,6 +25,11 @@
|
|||||||
"description": "The authentication token that needs to be sent in the Authorization header"
|
"description": "The authentication token that needs to be sent in the Authorization header"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"examples": {
|
||||||
|
"application/json": {
|
||||||
|
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
|
|||||||
@@ -26,10 +26,8 @@
|
|||||||
"description": "A list of tags used to filter the resultset. Only short URLs tagged with at least one of the provided tags will be returned. (Since v1.3.0)",
|
"description": "A list of tags used to filter the resultset. Only short URLs tagged with at least one of the provided tags will be returned. (Since v1.3.0)",
|
||||||
"required": false,
|
"required": false,
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"schema": {
|
"items": {
|
||||||
"items": {
|
"type": "string"
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -70,6 +68,44 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"examples": {
|
||||||
|
"application/json": {
|
||||||
|
"shortUrls": {
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"shortCode": "12C18",
|
||||||
|
"originalUrl": "https://store.steampowered.com",
|
||||||
|
"dateCreated": "2016-08-21T20:34:16+02:00",
|
||||||
|
"visitsCount": 328,
|
||||||
|
"tags": [
|
||||||
|
"games",
|
||||||
|
"tech"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"shortCode": "12Kb3",
|
||||||
|
"originalUrl": "https://shlink.io",
|
||||||
|
"dateCreated": "2016-05-01T20:34:16+02:00",
|
||||||
|
"visitsCount": 1029,
|
||||||
|
"tags": [
|
||||||
|
"shlink"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"shortCode": "123bA",
|
||||||
|
"originalUrl": "https://www.google.com",
|
||||||
|
"dateCreated": "2015-10-01T20:34:16+02:00",
|
||||||
|
"visitsCount": 25,
|
||||||
|
"tags": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pagination": {
|
||||||
|
"currentPage": 5,
|
||||||
|
"pagesCount": 12
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"500": {
|
"500": {
|
||||||
|
|||||||
@@ -28,6 +28,11 @@
|
|||||||
"description": "The original long URL behind the short code."
|
"description": "The original long URL behind the short code."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"examples": {
|
||||||
|
"application/json": {
|
||||||
|
"longUrl": "https://shlink.io"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
|
|||||||
@@ -41,6 +41,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"examples": {
|
||||||
|
"application/json": {
|
||||||
|
"tags": [
|
||||||
|
"games",
|
||||||
|
"tech"
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
|
|||||||
@@ -36,6 +36,32 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"examples": {
|
||||||
|
"application/json": {
|
||||||
|
"visits": {
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"referer": "https://twitter.com",
|
||||||
|
"date": "2015-08-20T05:05:03+04:00",
|
||||||
|
"remoteAddr": "10.20.30.40",
|
||||||
|
"userAgent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0 Mozilla/5.0 (Macintosh; Intel Mac OS X x.y; rv:42.0) Gecko/20100101 Firefox/42.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"referer": "https://t.co",
|
||||||
|
"date": "2015-08-20T05:05:03+04:00",
|
||||||
|
"remoteAddr": "11.22.33.44",
|
||||||
|
"userAgent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"referer": null,
|
||||||
|
"date": "2015-08-20T05:05:03+04:00",
|
||||||
|
"remoteAddr": "110.220.5.6",
|
||||||
|
"userAgent": "some_web_crawler/1.4"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"404": {
|
"404": {
|
||||||
|
|||||||
193
docs/swagger/paths/v1_tags.json
Normal file
193
docs/swagger/paths/v1_tags.json
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
{
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"Tags"
|
||||||
|
],
|
||||||
|
"summary": "List existing tags",
|
||||||
|
"description": "Returns the list of all tags used in any short URL, ordered by name",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"$ref": "../parameters/Authorization.json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "The list of tags",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"tags": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"examples": {
|
||||||
|
"application/json": {
|
||||||
|
"tags": {
|
||||||
|
"data": [
|
||||||
|
"games",
|
||||||
|
"php",
|
||||||
|
"shlink",
|
||||||
|
"tech"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Unexpected error.",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "../definitions/Error.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"post": {
|
||||||
|
"tags": [
|
||||||
|
"Tags"
|
||||||
|
],
|
||||||
|
"summary": "Create tags",
|
||||||
|
"description": "Provided a list of tags, creates all that do not yet exist",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"$ref": "../parameters/Authorization.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "tags[]",
|
||||||
|
"in": "formData",
|
||||||
|
"description": "The list of tag names to create",
|
||||||
|
"required": true,
|
||||||
|
"type": "array"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "The list of tags",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"tags": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"examples": {
|
||||||
|
"application/json": {
|
||||||
|
"tags": {
|
||||||
|
"data": [
|
||||||
|
"games",
|
||||||
|
"php",
|
||||||
|
"shlink",
|
||||||
|
"tech"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Unexpected error.",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "../definitions/Error.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"put": {
|
||||||
|
"tags": [
|
||||||
|
"Tags"
|
||||||
|
],
|
||||||
|
"summary": "Rename tag",
|
||||||
|
"description": "Renames one existing tag",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"$ref": "../parameters/Authorization.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "oldName",
|
||||||
|
"in": "formData",
|
||||||
|
"description": "Current name of the tag",
|
||||||
|
"required": true,
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "newName",
|
||||||
|
"in": "formData",
|
||||||
|
"description": "New name of the tag",
|
||||||
|
"required": true,
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"204": {
|
||||||
|
"description": "The tag has been properly renamed"
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "You have not provided either the oldName or the newName params.",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "../definitions/Error.json"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "There's no tag found with the name provided in oldName param.",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "../definitions/Error.json"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Unexpected error.",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "../definitions/Error.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"delete": {
|
||||||
|
"tags": [
|
||||||
|
"Tags"
|
||||||
|
],
|
||||||
|
"summary": "Delete tags",
|
||||||
|
"description": "Deletes provided list of tags",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"$ref": "../parameters/Authorization.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "tags[]",
|
||||||
|
"in": "query",
|
||||||
|
"description": "The names of the tags to delete",
|
||||||
|
"required": true,
|
||||||
|
"type": "array"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"204": {
|
||||||
|
"description": "Tags properly deleted"
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Unexpected error.",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "../definitions/Error.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
"info": {
|
"info": {
|
||||||
"title": "Shlink",
|
"title": "Shlink",
|
||||||
"description": "Shlink, the self-hosted URL shortener",
|
"description": "Shlink, the self-hosted URL shortener",
|
||||||
"version": "1.2.0"
|
"version": "1.0"
|
||||||
},
|
},
|
||||||
"schemes": [
|
"schemes": [
|
||||||
"http",
|
"http",
|
||||||
@@ -22,17 +22,23 @@
|
|||||||
"/v1/authenticate": {
|
"/v1/authenticate": {
|
||||||
"$ref": "paths/v1_authenticate.json"
|
"$ref": "paths/v1_authenticate.json"
|
||||||
},
|
},
|
||||||
|
|
||||||
"/v1/short-codes": {
|
"/v1/short-codes": {
|
||||||
"$ref": "paths/v1_short-codes.json"
|
"$ref": "paths/v1_short-codes.json"
|
||||||
},
|
},
|
||||||
"/v1/short-codes/{shortCode}": {
|
"/v1/short-codes/{shortCode}": {
|
||||||
"$ref": "paths/v1_short-codes_{shortCode}.json"
|
"$ref": "paths/v1_short-codes_{shortCode}.json"
|
||||||
},
|
},
|
||||||
"/v1/short-codes/{shortCode}/visits": {
|
|
||||||
"$ref": "paths/v1_short-codes_{shortCode}_visits.json"
|
|
||||||
},
|
|
||||||
"/v1/short-codes/{shortCode}/tags": {
|
"/v1/short-codes/{shortCode}/tags": {
|
||||||
"$ref": "paths/v1_short-codes_{shortCode}_tags.json"
|
"$ref": "paths/v1_short-codes_{shortCode}_tags.json"
|
||||||
|
},
|
||||||
|
|
||||||
|
"/v1/tags": {
|
||||||
|
"$ref": "paths/v1_tags.json"
|
||||||
|
},
|
||||||
|
|
||||||
|
"/v1/short-codes/{shortCode}/visits": {
|
||||||
|
"$ref": "paths/v1_short-codes_{shortCode}_visits.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
indocker
Executable file
2
indocker
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
docker exec -it shlink_php /bin/sh -c "cd /home/shlink/www && $*"
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
use Shlinkio\Shlink\CLI\Command;
|
use Shlinkio\Shlink\CLI\Command;
|
||||||
|
use Shlinkio\Shlink\Common;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
'cli' => [
|
'cli' => [
|
||||||
'locale' => env('CLI_LOCALE', 'en'),
|
'locale' => Common\env('CLI_LOCALE', 'en'),
|
||||||
'commands' => [
|
'commands' => [
|
||||||
Command\Shortcode\GenerateShortcodeCommand::class,
|
Command\Shortcode\GenerateShortcodeCommand::class,
|
||||||
Command\Shortcode\ResolveUrlCommand::class,
|
Command\Shortcode\ResolveUrlCommand::class,
|
||||||
@@ -17,6 +18,10 @@ return [
|
|||||||
Command\Api\GenerateKeyCommand::class,
|
Command\Api\GenerateKeyCommand::class,
|
||||||
Command\Api\DisableKeyCommand::class,
|
Command\Api\DisableKeyCommand::class,
|
||||||
Command\Api\ListKeysCommand::class,
|
Command\Api\ListKeysCommand::class,
|
||||||
|
Command\Tag\ListTagsCommand::class,
|
||||||
|
Command\Tag\CreateTagCommand::class,
|
||||||
|
Command\Tag\RenameTagCommand::class,
|
||||||
|
Command\Tag\DeleteTagsCommand::class,
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ return [
|
|||||||
Command\Api\GenerateKeyCommand::class => AnnotatedFactory::class,
|
Command\Api\GenerateKeyCommand::class => AnnotatedFactory::class,
|
||||||
Command\Api\DisableKeyCommand::class => AnnotatedFactory::class,
|
Command\Api\DisableKeyCommand::class => AnnotatedFactory::class,
|
||||||
Command\Api\ListKeysCommand::class => AnnotatedFactory::class,
|
Command\Api\ListKeysCommand::class => AnnotatedFactory::class,
|
||||||
|
Command\Tag\ListTagsCommand::class => AnnotatedFactory::class,
|
||||||
|
Command\Tag\CreateTagCommand::class => AnnotatedFactory::class,
|
||||||
|
Command\Tag\RenameTagCommand::class => AnnotatedFactory::class,
|
||||||
|
Command\Tag\DeleteTagsCommand::class => AnnotatedFactory::class,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -1,15 +1,15 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: Shlink 1.0\n"
|
"Project-Id-Version: Shlink 1.0\n"
|
||||||
"POT-Creation-Date: 2016-10-22 23:12+0200\n"
|
"POT-Creation-Date: 2017-07-16 09:35+0200\n"
|
||||||
"PO-Revision-Date: 2016-10-22 23:13+0200\n"
|
"PO-Revision-Date: 2017-07-16 09:39+0200\n"
|
||||||
"Last-Translator: Alejandro Celaya <alejandro@alejandrocelaya.com>\n"
|
"Last-Translator: Alejandro Celaya <alejandro@alejandrocelaya.com>\n"
|
||||||
"Language-Team: \n"
|
"Language-Team: \n"
|
||||||
"Language: es_ES\n"
|
"Language: es_ES\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Generator: Poedit 1.8.7.1\n"
|
"X-Generator: Poedit 2.0.1\n"
|
||||||
"X-Poedit-Basepath: ..\n"
|
"X-Poedit-Basepath: ..\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
"X-Poedit-SourceCharset: UTF-8\n"
|
"X-Poedit-SourceCharset: UTF-8\n"
|
||||||
@@ -223,6 +223,53 @@ msgstr "URL larga:"
|
|||||||
msgid "Provided short code \"%s\" has an invalid format."
|
msgid "Provided short code \"%s\" has an invalid format."
|
||||||
msgstr "El código corto proporcionado \"%s\" tiene un formato inválido."
|
msgstr "El código corto proporcionado \"%s\" tiene un formato inválido."
|
||||||
|
|
||||||
|
msgid "Creates one or more tags."
|
||||||
|
msgstr "Crea una o más etiquetas."
|
||||||
|
|
||||||
|
msgid "The name of the tags to create"
|
||||||
|
msgstr "El nombre de las etiquetas a crear"
|
||||||
|
|
||||||
|
msgid "You have to provide at least one tag name"
|
||||||
|
msgstr "Debes proporcionar al menos un nombre de etiqueta"
|
||||||
|
|
||||||
|
msgid "Created tags"
|
||||||
|
msgstr "Etiquetas creadas"
|
||||||
|
|
||||||
|
msgid "Deletes one or more tags."
|
||||||
|
msgstr "Elimina una o más etiquetas."
|
||||||
|
|
||||||
|
msgid "The name of the tags to delete"
|
||||||
|
msgstr "El nombre de las etiquetas a eliminar"
|
||||||
|
|
||||||
|
msgid "Deleted tags"
|
||||||
|
msgstr "Etiquetas eliminadas"
|
||||||
|
|
||||||
|
msgid "Lists existing tags."
|
||||||
|
msgstr "Lista las etiquetas existentes."
|
||||||
|
|
||||||
|
#, fuzzy
|
||||||
|
msgid "Name"
|
||||||
|
msgstr "Nombre"
|
||||||
|
|
||||||
|
msgid "No tags yet"
|
||||||
|
msgstr "Aún no hay etiquetas"
|
||||||
|
|
||||||
|
msgid "Renames one existing tag."
|
||||||
|
msgstr "Renombra una etiqueta existente."
|
||||||
|
|
||||||
|
msgid "Current name of the tag."
|
||||||
|
msgstr "Nombre actual de la etiqueta."
|
||||||
|
|
||||||
|
msgid "New name of the tag."
|
||||||
|
msgstr "Nuevo nombre de la etiqueta."
|
||||||
|
|
||||||
|
msgid "Tag properly renamed."
|
||||||
|
msgstr "Etiqueta correctamente renombrada."
|
||||||
|
|
||||||
|
#, php-format
|
||||||
|
msgid "A tag with name \"%s\" was not found"
|
||||||
|
msgstr "Una etiqueta con nombre \"%s\" no ha sido encontrada"
|
||||||
|
|
||||||
msgid "Processes visits where location is not set yet"
|
msgid "Processes visits where location is not set yet"
|
||||||
msgstr "Procesa las visitas donde la localización no ha sido establecida aún"
|
msgstr "Procesa las visitas donde la localización no ha sido establecida aún"
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class DisableKeyCommand extends Command
|
|||||||
{
|
{
|
||||||
$this->apiKeyService = $apiKeyService;
|
$this->apiKeyService = $apiKeyService;
|
||||||
$this->translator = $translator;
|
$this->translator = $translator;
|
||||||
parent::__construct(null);
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function configure()
|
public function configure()
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ class ListKeysCommand extends Command
|
|||||||
$rowData[] = $this->{$formatMethod}($this->getEnabledSymbol($row));
|
$rowData[] = $this->{$formatMethod}($this->getEnabledSymbol($row));
|
||||||
}
|
}
|
||||||
|
|
||||||
$rowData[] = isset($expiration) ? $expiration->format(\DateTime::ISO8601) : '-';
|
$rowData[] = isset($expiration) ? $expiration->format(\DateTime::ATOM) : '-';
|
||||||
$table->addRow($rowData);
|
$table->addRow($rowData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,25 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Shlinkio\Shlink\CLI\Command\Install;
|
namespace Shlinkio\Shlink\CLI\Command\Install;
|
||||||
|
|
||||||
use Shlinkio\Shlink\Common\Util\StringUtilsTrait;
|
use Shlinkio\Shlink\CLI\Install\ConfigCustomizerPluginManagerInterface;
|
||||||
use Shlinkio\Shlink\Core\Service\UrlShortener;
|
use Shlinkio\Shlink\CLI\Install\Plugin;
|
||||||
|
use Shlinkio\Shlink\CLI\Model\CustomizableAppConfig;
|
||||||
use Symfony\Component\Console\Command\Command;
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
use Symfony\Component\Console\Exception\LogicException;
|
||||||
|
use Symfony\Component\Console\Exception\RuntimeException;
|
||||||
use Symfony\Component\Console\Helper\ProcessHelper;
|
use Symfony\Component\Console\Helper\ProcessHelper;
|
||||||
use Symfony\Component\Console\Helper\QuestionHelper;
|
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
use Symfony\Component\Console\Question\ChoiceQuestion;
|
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||||
use Symfony\Component\Console\Question\Question;
|
use Symfony\Component\Console\Question\Question;
|
||||||
|
use Symfony\Component\Filesystem\Exception\IOException;
|
||||||
|
use Symfony\Component\Filesystem\Filesystem;
|
||||||
use Zend\Config\Writer\WriterInterface;
|
use Zend\Config\Writer\WriterInterface;
|
||||||
|
|
||||||
class InstallCommand extends Command
|
class InstallCommand extends Command
|
||||||
{
|
{
|
||||||
use StringUtilsTrait;
|
const GENERATED_CONFIG_PATH = 'config/params/generated_config.php';
|
||||||
|
|
||||||
const DATABASE_DRIVERS = [
|
|
||||||
'MySQL' => 'pdo_mysql',
|
|
||||||
'PostgreSQL' => 'pdo_pgsql',
|
|
||||||
'SQLite' => 'pdo_sqlite',
|
|
||||||
];
|
|
||||||
const SUPPORTED_LANGUAGES = ['en', 'es'];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var InputInterface
|
* @var InputInterface
|
||||||
@@ -43,22 +41,44 @@ class InstallCommand extends Command
|
|||||||
* @var WriterInterface
|
* @var WriterInterface
|
||||||
*/
|
*/
|
||||||
private $configWriter;
|
private $configWriter;
|
||||||
|
/**
|
||||||
|
* @var Filesystem
|
||||||
|
*/
|
||||||
|
private $filesystem;
|
||||||
|
/**
|
||||||
|
* @var ConfigCustomizerPluginManagerInterface
|
||||||
|
*/
|
||||||
|
private $configCustomizers;
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $isUpdate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* InstallCommand constructor.
|
* InstallCommand constructor.
|
||||||
* @param WriterInterface $configWriter
|
* @param WriterInterface $configWriter
|
||||||
* @param callable|null $databaseCreationLogic
|
* @param Filesystem $filesystem
|
||||||
|
* @param bool $isUpdate
|
||||||
|
* @throws LogicException
|
||||||
*/
|
*/
|
||||||
public function __construct(WriterInterface $configWriter)
|
public function __construct(
|
||||||
{
|
WriterInterface $configWriter,
|
||||||
parent::__construct(null);
|
Filesystem $filesystem,
|
||||||
|
ConfigCustomizerPluginManagerInterface $configCustomizers,
|
||||||
|
$isUpdate = false
|
||||||
|
) {
|
||||||
|
parent::__construct();
|
||||||
$this->configWriter = $configWriter;
|
$this->configWriter = $configWriter;
|
||||||
|
$this->isUpdate = $isUpdate;
|
||||||
|
$this->filesystem = $filesystem;
|
||||||
|
$this->configCustomizers = $configCustomizers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function configure()
|
public function configure()
|
||||||
{
|
{
|
||||||
$this->setName('shlink:install')
|
$this
|
||||||
->setDescription('Installs Shlink');
|
->setName('shlink:install')
|
||||||
|
->setDescription('Installs or updates Shlink');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute(InputInterface $input, OutputInterface $output)
|
public function execute(InputInterface $input, OutputInterface $output)
|
||||||
@@ -67,40 +87,58 @@ class InstallCommand extends Command
|
|||||||
$this->output = $output;
|
$this->output = $output;
|
||||||
$this->questionHelper = $this->getHelper('question');
|
$this->questionHelper = $this->getHelper('question');
|
||||||
$this->processHelper = $this->getHelper('process');
|
$this->processHelper = $this->getHelper('process');
|
||||||
$params = [];
|
|
||||||
|
|
||||||
$output->writeln([
|
$output->writeln([
|
||||||
'<info>Welcome to Shlink!!</info>',
|
'<info>Welcome to Shlink!!</info>',
|
||||||
'This process will guide you through the installation.',
|
'This will guide you through the installation process.',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Check if a cached config file exists and drop it if so
|
// Check if a cached config file exists and drop it if so
|
||||||
if (file_exists('data/cache/app_config.php')) {
|
if ($this->filesystem->exists('data/cache/app_config.php')) {
|
||||||
$output->write('Deleting old cached config...');
|
$output->write('Deleting old cached config...');
|
||||||
if (unlink('data/cache/app_config.php')) {
|
try {
|
||||||
|
$this->filesystem->remove('data/cache/app_config.php');
|
||||||
$output->writeln(' <info>Success</info>');
|
$output->writeln(' <info>Success</info>');
|
||||||
} else {
|
} catch (IOException $e) {
|
||||||
$output->writeln(
|
$output->writeln(
|
||||||
' <error>Failed!</error> You will have to manually delete the data/cache/app_config.php file to get'
|
' <error>Failed!</error> You will have to manually delete the data/cache/app_config.php file to get'
|
||||||
. ' new config applied.'
|
. ' new config applied.'
|
||||||
);
|
);
|
||||||
|
if ($output->isVerbose()) {
|
||||||
|
$this->getApplication()->renderException($e, $output);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If running update command, ask the user to import previous config
|
||||||
|
$config = $this->isUpdate ? $this->importConfig() : new CustomizableAppConfig();
|
||||||
|
|
||||||
// Ask for custom config params
|
// Ask for custom config params
|
||||||
$params['DATABASE'] = $this->askDatabase();
|
foreach ([
|
||||||
$params['URL_SHORTENER'] = $this->askUrlShortener();
|
Plugin\DatabaseConfigCustomizerPlugin::class,
|
||||||
$params['LANGUAGE'] = $this->askLanguage();
|
Plugin\UrlShortenerConfigCustomizerPlugin::class,
|
||||||
$params['APP'] = $this->askApplication();
|
Plugin\LanguageConfigCustomizerPlugin::class,
|
||||||
|
Plugin\ApplicationConfigCustomizerPlugin::class,
|
||||||
|
] as $pluginName) {
|
||||||
|
/** @var Plugin\ConfigCustomizerPluginInterface $configCustomizer */
|
||||||
|
$configCustomizer = $this->configCustomizers->get($pluginName);
|
||||||
|
$configCustomizer->process($input, $output, $config);
|
||||||
|
}
|
||||||
|
|
||||||
// Generate config params files
|
// Generate config params files
|
||||||
$config = $this->buildAppConfig($params);
|
$this->configWriter->toFile(self::GENERATED_CONFIG_PATH, $config->getArrayCopy(), false);
|
||||||
$this->configWriter->toFile('config/params/generated_config.php', $config, false);
|
|
||||||
$output->writeln(['<info>Custom configuration properly generated!</info>', '']);
|
$output->writeln(['<info>Custom configuration properly generated!</info>', '']);
|
||||||
|
|
||||||
// Generate database
|
// If current command is not update, generate database
|
||||||
if (! $this->createDatabase()) {
|
if (! $this->isUpdate) {
|
||||||
return;
|
$this->output->writeln('Initializing database...');
|
||||||
|
if (! $this->runCommand(
|
||||||
|
'php vendor/bin/doctrine.php orm:schema-tool:create',
|
||||||
|
'Error generating database.'
|
||||||
|
)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run database migrations
|
// Run database migrations
|
||||||
@@ -116,98 +154,47 @@ class InstallCommand extends Command
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function askDatabase()
|
/**
|
||||||
|
* @return CustomizableAppConfig
|
||||||
|
* @throws RuntimeException
|
||||||
|
*/
|
||||||
|
private function importConfig()
|
||||||
{
|
{
|
||||||
$params = [];
|
$config = new CustomizableAppConfig();
|
||||||
$this->printTitle('DATABASE');
|
|
||||||
|
|
||||||
// Select database type
|
// Ask the user if he/she wants to import an older configuration
|
||||||
$databases = array_keys(self::DATABASE_DRIVERS);
|
$importConfig = $this->questionHelper->ask($this->input, $this->output, new ConfirmationQuestion(
|
||||||
$dbType = $this->questionHelper->ask($this->input, $this->output, new ChoiceQuestion(
|
'<question>Do you want to import previous configuration? (Y/n):</question> '
|
||||||
'<question>Select database type (defaults to ' . $databases[0] . '):</question>',
|
|
||||||
$databases,
|
|
||||||
0
|
|
||||||
));
|
));
|
||||||
$params['DRIVER'] = self::DATABASE_DRIVERS[$dbType];
|
if (! $importConfig) {
|
||||||
|
return $config;
|
||||||
// Ask for connection params if database is not SQLite
|
|
||||||
if ($params['DRIVER'] !== self::DATABASE_DRIVERS['SQLite']) {
|
|
||||||
$params['NAME'] = $this->ask('Database name', 'shlink');
|
|
||||||
$params['USER'] = $this->ask('Database username');
|
|
||||||
$params['PASSWORD'] = $this->ask('Database password');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $params;
|
// Ask the user for the older shlink path
|
||||||
}
|
$keepAsking = true;
|
||||||
|
do {
|
||||||
|
$config->setImportedInstallationPath($this->ask(
|
||||||
|
'Previous shlink installation path from which to import config'
|
||||||
|
));
|
||||||
|
$configFile = $config->getImportedInstallationPath() . '/' . self::GENERATED_CONFIG_PATH;
|
||||||
|
$configExists = $this->filesystem->exists($configFile);
|
||||||
|
|
||||||
protected function askUrlShortener()
|
if (! $configExists) {
|
||||||
{
|
$keepAsking = $this->questionHelper->ask($this->input, $this->output, new ConfirmationQuestion(
|
||||||
$this->printTitle('URL SHORTENER');
|
'Provided path does not seem to be a valid shlink root path. '
|
||||||
|
. '<question>Do you want to try another path? (Y/n):</question> '
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} while (! $configExists && $keepAsking);
|
||||||
|
|
||||||
// Ask for URL shortener params
|
// If after some retries the user has chosen not to test another path, return
|
||||||
return [
|
if (! $configExists) {
|
||||||
'SCHEMA' => $this->questionHelper->ask($this->input, $this->output, new ChoiceQuestion(
|
return $config;
|
||||||
'<question>Select schema for generated short URLs (defaults to http):</question>',
|
}
|
||||||
['http', 'https'],
|
|
||||||
0
|
|
||||||
)),
|
|
||||||
'HOSTNAME' => $this->ask('Hostname for generated URLs'),
|
|
||||||
'CHARS' => $this->ask(
|
|
||||||
'Character set for generated short codes (leave empty to autogenerate one)',
|
|
||||||
null,
|
|
||||||
true
|
|
||||||
) ?: str_shuffle(UrlShortener::DEFAULT_CHARS)
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function askLanguage()
|
// Read the config file
|
||||||
{
|
$config->exchangeArray(include $configFile);
|
||||||
$this->printTitle('LANGUAGE');
|
return $config;
|
||||||
|
|
||||||
return [
|
|
||||||
'DEFAULT' => $this->questionHelper->ask($this->input, $this->output, new ChoiceQuestion(
|
|
||||||
'<question>Select default language for the application in general (defaults to '
|
|
||||||
. self::SUPPORTED_LANGUAGES[0] . '):</question>',
|
|
||||||
self::SUPPORTED_LANGUAGES,
|
|
||||||
0
|
|
||||||
)),
|
|
||||||
'CLI' => $this->questionHelper->ask($this->input, $this->output, new ChoiceQuestion(
|
|
||||||
'<question>Select default language for CLI executions (defaults to '
|
|
||||||
. self::SUPPORTED_LANGUAGES[0] . '):</question>',
|
|
||||||
self::SUPPORTED_LANGUAGES,
|
|
||||||
0
|
|
||||||
)),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function askApplication()
|
|
||||||
{
|
|
||||||
$this->printTitle('APPLICATION');
|
|
||||||
|
|
||||||
return [
|
|
||||||
'SECRET' => $this->ask(
|
|
||||||
'Define a secret string that will be used to sign API tokens (leave empty to autogenerate one)',
|
|
||||||
null,
|
|
||||||
true
|
|
||||||
) ?: $this->generateRandomString(32),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $text
|
|
||||||
*/
|
|
||||||
protected function printTitle($text)
|
|
||||||
{
|
|
||||||
$text = trim($text);
|
|
||||||
$length = strlen($text) + 4;
|
|
||||||
$header = str_repeat('*', $length);
|
|
||||||
|
|
||||||
$this->output->writeln([
|
|
||||||
'',
|
|
||||||
'<info>' . $header . '</info>',
|
|
||||||
'<info>* ' . strtoupper($text) . ' *</info>',
|
|
||||||
'<info>' . $header . '</info>',
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -215,10 +202,11 @@ class InstallCommand extends Command
|
|||||||
* @param string|null $default
|
* @param string|null $default
|
||||||
* @param bool $allowEmpty
|
* @param bool $allowEmpty
|
||||||
* @return string
|
* @return string
|
||||||
|
* @throws RuntimeException
|
||||||
*/
|
*/
|
||||||
protected function ask($text, $default = null, $allowEmpty = false)
|
private function ask($text, $default = null, $allowEmpty = false)
|
||||||
{
|
{
|
||||||
if (isset($default)) {
|
if ($default !== null) {
|
||||||
$text .= ' (defaults to ' . $default . ')';
|
$text .= ' (defaults to ' . $default . ')';
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
@@ -229,79 +217,31 @@ class InstallCommand extends Command
|
|||||||
if (empty($value) && ! $allowEmpty) {
|
if (empty($value) && ! $allowEmpty) {
|
||||||
$this->output->writeln('<error>Value can\'t be empty</error>');
|
$this->output->writeln('<error>Value can\'t be empty</error>');
|
||||||
}
|
}
|
||||||
} while (empty($value) && empty($default) && ! $allowEmpty);
|
} while (empty($value) && $default === null && ! $allowEmpty);
|
||||||
|
|
||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array $params
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
protected function buildAppConfig(array $params)
|
|
||||||
{
|
|
||||||
// Build simple config
|
|
||||||
$config = [
|
|
||||||
'app_options' => [
|
|
||||||
'secret_key' => $params['APP']['SECRET'],
|
|
||||||
],
|
|
||||||
'entity_manager' => [
|
|
||||||
'connection' => [
|
|
||||||
'driver' => $params['DATABASE']['DRIVER'],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'translator' => [
|
|
||||||
'locale' => $params['LANGUAGE']['DEFAULT'],
|
|
||||||
],
|
|
||||||
'cli' => [
|
|
||||||
'locale' => $params['LANGUAGE']['CLI'],
|
|
||||||
],
|
|
||||||
'url_shortener' => [
|
|
||||||
'domain' => [
|
|
||||||
'schema' => $params['URL_SHORTENER']['SCHEMA'],
|
|
||||||
'hostname' => $params['URL_SHORTENER']['HOSTNAME'],
|
|
||||||
],
|
|
||||||
'shortcode_chars' => $params['URL_SHORTENER']['CHARS'],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
// Build dynamic database config
|
|
||||||
if ($params['DATABASE']['DRIVER'] === 'pdo_sqlite') {
|
|
||||||
$config['entity_manager']['connection']['path'] = 'data/database.sqlite';
|
|
||||||
} else {
|
|
||||||
$config['entity_manager']['connection']['user'] = $params['DATABASE']['USER'];
|
|
||||||
$config['entity_manager']['connection']['password'] = $params['DATABASE']['PASSWORD'];
|
|
||||||
$config['entity_manager']['connection']['dbname'] = $params['DATABASE']['NAME'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $config;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createDatabase()
|
|
||||||
{
|
|
||||||
$this->output->writeln('Initializing database...');
|
|
||||||
return $this->runCommand('php vendor/bin/doctrine.php orm:schema-tool:create', 'Error generating database.');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $command
|
* @param string $command
|
||||||
* @param string $errorMessage
|
* @param string $errorMessage
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
protected function runCommand($command, $errorMessage)
|
private function runCommand($command, $errorMessage)
|
||||||
{
|
{
|
||||||
$process = $this->processHelper->run($this->output, $command);
|
$process = $this->processHelper->run($this->output, $command);
|
||||||
if ($process->isSuccessful()) {
|
if ($process->isSuccessful()) {
|
||||||
$this->output->writeln(' <info>Success!</info>');
|
$this->output->writeln(' <info>Success!</info>');
|
||||||
return true;
|
return true;
|
||||||
} else {
|
}
|
||||||
if ($this->output->isVerbose()) {
|
|
||||||
return false;
|
if ($this->output->isVerbose()) {
|
||||||
}
|
|
||||||
$this->output->writeln(
|
|
||||||
' <error>' . $errorMessage . '</error> Run this command with -vvv to see specific error info.'
|
|
||||||
);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->output->writeln(
|
||||||
|
' <error>' . $errorMessage . '</error> Run this command with -vvv to see specific error info.'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace Shlinkio\Shlink\CLI\Command\Install;
|
|
||||||
|
|
||||||
use Zend\Config\Writer\WriterInterface;
|
|
||||||
|
|
||||||
class UpdateCommand extends InstallCommand
|
|
||||||
{
|
|
||||||
public function createDatabase()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
69
module/CLI/src/Command/Tag/CreateTagCommand.php
Normal file
69
module/CLI/src/Command/Tag/CreateTagCommand.php
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
<?php
|
||||||
|
namespace Shlinkio\Shlink\CLI\Command\Tag;
|
||||||
|
|
||||||
|
use Acelaya\ZsmAnnotatedServices\Annotation as DI;
|
||||||
|
use Shlinkio\Shlink\Core\Service\Tag\TagService;
|
||||||
|
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
|
||||||
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use Zend\I18n\Translator\Translator;
|
||||||
|
use Zend\I18n\Translator\TranslatorInterface;
|
||||||
|
|
||||||
|
class CreateTagCommand extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var TagServiceInterface
|
||||||
|
*/
|
||||||
|
private $tagService;
|
||||||
|
/**
|
||||||
|
* @var TranslatorInterface
|
||||||
|
*/
|
||||||
|
private $translator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CreateTagCommand constructor.
|
||||||
|
* @param TagServiceInterface $tagService
|
||||||
|
* @param TranslatorInterface $translator
|
||||||
|
*
|
||||||
|
* @DI\Inject({TagService::class, Translator::class})
|
||||||
|
*/
|
||||||
|
public function __construct(TagServiceInterface $tagService, TranslatorInterface $translator)
|
||||||
|
{
|
||||||
|
$this->tagService = $tagService;
|
||||||
|
$this->translator = $translator;
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->setName('tag:create')
|
||||||
|
->setDescription($this->translator->translate('Creates one or more tags.'))
|
||||||
|
->addOption(
|
||||||
|
'name',
|
||||||
|
't',
|
||||||
|
InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
|
||||||
|
$this->translator->translate('The name of the tags to create')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
|
{
|
||||||
|
$tagNames = $input->getOption('name');
|
||||||
|
if (empty($tagNames)) {
|
||||||
|
$output->writeln(sprintf(
|
||||||
|
'<comment>%s</comment>',
|
||||||
|
$this->translator->translate('You have to provide at least one tag name')
|
||||||
|
));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->tagService->createTags($tagNames);
|
||||||
|
$output->writeln($this->translator->translate('Created tags') . sprintf(': ["<info>%s</info>"]', implode(
|
||||||
|
'</info>", "<info>',
|
||||||
|
$tagNames
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
69
module/CLI/src/Command/Tag/DeleteTagsCommand.php
Normal file
69
module/CLI/src/Command/Tag/DeleteTagsCommand.php
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
<?php
|
||||||
|
namespace Shlinkio\Shlink\CLI\Command\Tag;
|
||||||
|
|
||||||
|
use Acelaya\ZsmAnnotatedServices\Annotation as DI;
|
||||||
|
use Shlinkio\Shlink\Core\Service\Tag\TagService;
|
||||||
|
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
|
||||||
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use Zend\I18n\Translator\Translator;
|
||||||
|
use Zend\I18n\Translator\TranslatorInterface;
|
||||||
|
|
||||||
|
class DeleteTagsCommand extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var TagServiceInterface
|
||||||
|
*/
|
||||||
|
private $tagService;
|
||||||
|
/**
|
||||||
|
* @var TranslatorInterface
|
||||||
|
*/
|
||||||
|
private $translator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ListTagsCommand constructor.
|
||||||
|
* @param TagServiceInterface $tagService
|
||||||
|
* @param TranslatorInterface $translator
|
||||||
|
*
|
||||||
|
* @DI\Inject({TagService::class, Translator::class})
|
||||||
|
*/
|
||||||
|
public function __construct(TagServiceInterface $tagService, TranslatorInterface $translator)
|
||||||
|
{
|
||||||
|
$this->tagService = $tagService;
|
||||||
|
$this->translator = $translator;
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->setName('tag:delete')
|
||||||
|
->setDescription($this->translator->translate('Deletes one or more tags.'))
|
||||||
|
->addOption(
|
||||||
|
'name',
|
||||||
|
't',
|
||||||
|
InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
|
||||||
|
$this->translator->translate('The name of the tags to delete')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
|
{
|
||||||
|
$tagNames = $input->getOption('name');
|
||||||
|
if (empty($tagNames)) {
|
||||||
|
$output->writeln(sprintf(
|
||||||
|
'<comment>%s</comment>',
|
||||||
|
$this->translator->translate('You have to provide at least one tag name')
|
||||||
|
));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->tagService->deleteTags($tagNames);
|
||||||
|
$output->writeln($this->translator->translate('Deleted tags') . sprintf(': ["<info>%s</info>"]', implode(
|
||||||
|
'</info>", "<info>',
|
||||||
|
$tagNames
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
67
module/CLI/src/Command/Tag/ListTagsCommand.php
Normal file
67
module/CLI/src/Command/Tag/ListTagsCommand.php
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
namespace Shlinkio\Shlink\CLI\Command\Tag;
|
||||||
|
|
||||||
|
use Acelaya\ZsmAnnotatedServices\Annotation as DI;
|
||||||
|
use Shlinkio\Shlink\Core\Entity\Tag;
|
||||||
|
use Shlinkio\Shlink\Core\Service\Tag\TagService;
|
||||||
|
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
|
||||||
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
use Symfony\Component\Console\Helper\Table;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use Zend\I18n\Translator\Translator;
|
||||||
|
use Zend\I18n\Translator\TranslatorInterface;
|
||||||
|
|
||||||
|
class ListTagsCommand extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var TagServiceInterface
|
||||||
|
*/
|
||||||
|
private $tagService;
|
||||||
|
/**
|
||||||
|
* @var TranslatorInterface
|
||||||
|
*/
|
||||||
|
private $translator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ListTagsCommand constructor.
|
||||||
|
* @param TagServiceInterface $tagService
|
||||||
|
* @param TranslatorInterface $translator
|
||||||
|
*
|
||||||
|
* @DI\Inject({TagService::class, Translator::class})
|
||||||
|
*/
|
||||||
|
public function __construct(TagServiceInterface $tagService, TranslatorInterface $translator)
|
||||||
|
{
|
||||||
|
$this->tagService = $tagService;
|
||||||
|
$this->translator = $translator;
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->setName('tag:list')
|
||||||
|
->setDescription($this->translator->translate('Lists existing tags.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
|
{
|
||||||
|
$table = new Table($output);
|
||||||
|
$table->setHeaders([$this->translator->translate('Name')])
|
||||||
|
->setRows($this->getTagsRows());
|
||||||
|
|
||||||
|
$table->render();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getTagsRows()
|
||||||
|
{
|
||||||
|
$tags = $this->tagService->listTags();
|
||||||
|
if (empty($tags)) {
|
||||||
|
return [[$this->translator->translate('No tags yet')]];
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_map(function (Tag $tag) {
|
||||||
|
return [$tag->getName()];
|
||||||
|
}, $tags);
|
||||||
|
}
|
||||||
|
}
|
||||||
63
module/CLI/src/Command/Tag/RenameTagCommand.php
Normal file
63
module/CLI/src/Command/Tag/RenameTagCommand.php
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
namespace Shlinkio\Shlink\CLI\Command\Tag;
|
||||||
|
|
||||||
|
use Acelaya\ZsmAnnotatedServices\Annotation as DI;
|
||||||
|
use Shlinkio\Shlink\Core\Exception\EntityDoesNotExistException;
|
||||||
|
use Shlinkio\Shlink\Core\Service\Tag\TagService;
|
||||||
|
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
|
||||||
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use Zend\I18n\Translator\Translator;
|
||||||
|
use Zend\I18n\Translator\TranslatorInterface;
|
||||||
|
|
||||||
|
class RenameTagCommand extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var TagServiceInterface
|
||||||
|
*/
|
||||||
|
private $tagService;
|
||||||
|
/**
|
||||||
|
* @var TranslatorInterface
|
||||||
|
*/
|
||||||
|
private $translator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RenameTagCommand constructor.
|
||||||
|
* @param TagServiceInterface $tagService
|
||||||
|
* @param TranslatorInterface $translator
|
||||||
|
*
|
||||||
|
* @DI\Inject({TagService::class, Translator::class})
|
||||||
|
*/
|
||||||
|
public function __construct(TagServiceInterface $tagService, TranslatorInterface $translator)
|
||||||
|
{
|
||||||
|
$this->tagService = $tagService;
|
||||||
|
$this->translator = $translator;
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->setName('tag:rename')
|
||||||
|
->setDescription($this->translator->translate('Renames one existing tag.'))
|
||||||
|
->addArgument('oldName', InputArgument::REQUIRED, $this->translator->translate('Current name of the tag.'))
|
||||||
|
->addArgument('newName', InputArgument::REQUIRED, $this->translator->translate('New name of the tag.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
|
{
|
||||||
|
$oldName = $input->getArgument('oldName');
|
||||||
|
$newName = $input->getArgument('newName');
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->tagService->renameTag($oldName, $newName);
|
||||||
|
$output->writeln(sprintf('<info>%s</info>', $this->translator->translate('Tag properly renamed.')));
|
||||||
|
} catch (EntityDoesNotExistException $e) {
|
||||||
|
$output->writeln('<error>' . sprintf($this->translator->translate(
|
||||||
|
'A tag with name "%s" was not found'
|
||||||
|
), $oldName) . '</error>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
55
module/CLI/src/Factory/InstallApplicationFactory.php
Normal file
55
module/CLI/src/Factory/InstallApplicationFactory.php
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
namespace Shlinkio\Shlink\CLI\Factory;
|
||||||
|
|
||||||
|
use Acelaya\ZsmAnnotatedServices\Factory\V3\AnnotatedFactory;
|
||||||
|
use Interop\Container\ContainerInterface;
|
||||||
|
use Interop\Container\Exception\ContainerException;
|
||||||
|
use Shlinkio\Shlink\CLI\Command\Install\InstallCommand;
|
||||||
|
use Shlinkio\Shlink\CLI\Install\ConfigCustomizerPluginManager;
|
||||||
|
use Shlinkio\Shlink\CLI\Install\Plugin;
|
||||||
|
use Shlinkio\Shlink\CLI\Install\Plugin\Factory\DefaultConfigCustomizerPluginFactory;
|
||||||
|
use Symfony\Component\Console\Application;
|
||||||
|
use Symfony\Component\Console\Exception\LogicException;
|
||||||
|
use Symfony\Component\Filesystem\Filesystem;
|
||||||
|
use Zend\Config\Writer\PhpArray;
|
||||||
|
use Zend\ServiceManager\Exception\ServiceNotCreatedException;
|
||||||
|
use Zend\ServiceManager\Exception\ServiceNotFoundException;
|
||||||
|
use Zend\ServiceManager\Factory\FactoryInterface;
|
||||||
|
|
||||||
|
class InstallApplicationFactory implements FactoryInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create an object
|
||||||
|
*
|
||||||
|
* @param ContainerInterface $container
|
||||||
|
* @param string $requestedName
|
||||||
|
* @param null|array $options
|
||||||
|
* @return object
|
||||||
|
* @throws LogicException
|
||||||
|
* @throws ServiceNotFoundException if unable to resolve the service.
|
||||||
|
* @throws ServiceNotCreatedException if an exception is raised when
|
||||||
|
* creating a service.
|
||||||
|
* @throws ContainerException if any other error occurs
|
||||||
|
*/
|
||||||
|
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
|
||||||
|
{
|
||||||
|
$isUpdate = $options !== null && isset($options['isUpdate']) ? (bool) $options['isUpdate'] : false;
|
||||||
|
|
||||||
|
$app = new Application();
|
||||||
|
$command = new InstallCommand(
|
||||||
|
new PhpArray(),
|
||||||
|
$container->get(Filesystem::class),
|
||||||
|
new ConfigCustomizerPluginManager($container, ['factories' => [
|
||||||
|
Plugin\DatabaseConfigCustomizerPlugin::class => AnnotatedFactory::class,
|
||||||
|
Plugin\UrlShortenerConfigCustomizerPlugin::class => DefaultConfigCustomizerPluginFactory::class,
|
||||||
|
Plugin\LanguageConfigCustomizerPlugin::class => DefaultConfigCustomizerPluginFactory::class,
|
||||||
|
Plugin\ApplicationConfigCustomizerPlugin::class => DefaultConfigCustomizerPluginFactory::class,
|
||||||
|
]]),
|
||||||
|
$isUpdate
|
||||||
|
);
|
||||||
|
$app->add($command);
|
||||||
|
$app->setDefaultCommand($command->getName());
|
||||||
|
|
||||||
|
return $app;
|
||||||
|
}
|
||||||
|
}
|
||||||
10
module/CLI/src/Install/ConfigCustomizerPluginManager.php
Normal file
10
module/CLI/src/Install/ConfigCustomizerPluginManager.php
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
namespace Shlinkio\Shlink\CLI\Install;
|
||||||
|
|
||||||
|
use Shlinkio\Shlink\CLI\Install\Plugin\ConfigCustomizerPluginInterface;
|
||||||
|
use Zend\ServiceManager\AbstractPluginManager;
|
||||||
|
|
||||||
|
class ConfigCustomizerPluginManager extends AbstractPluginManager implements ConfigCustomizerPluginManagerInterface
|
||||||
|
{
|
||||||
|
protected $instanceOf = ConfigCustomizerPluginInterface::class;
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
namespace Shlinkio\Shlink\CLI\Install;
|
||||||
|
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
|
interface ConfigCustomizerPluginManagerInterface extends ContainerInterface
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
namespace Shlinkio\Shlink\CLI\Install\Plugin;
|
||||||
|
|
||||||
|
use Symfony\Component\Console\Exception\RuntimeException;
|
||||||
|
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use Symfony\Component\Console\Question\Question;
|
||||||
|
|
||||||
|
abstract class AbstractConfigCustomizerPlugin implements ConfigCustomizerPluginInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var QuestionHelper
|
||||||
|
*/
|
||||||
|
protected $questionHelper;
|
||||||
|
|
||||||
|
public function __construct(QuestionHelper $questionHelper)
|
||||||
|
{
|
||||||
|
$this->questionHelper = $questionHelper;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param InputInterface $input
|
||||||
|
* @param OutputInterface $output
|
||||||
|
* @param string $text
|
||||||
|
* @param string|null $default
|
||||||
|
* @param bool $allowEmpty
|
||||||
|
* @return string
|
||||||
|
* @throws RuntimeException
|
||||||
|
*/
|
||||||
|
protected function ask(InputInterface $input, OutputInterface $output, $text, $default = null, $allowEmpty = false)
|
||||||
|
{
|
||||||
|
if ($default !== null) {
|
||||||
|
$text .= ' (defaults to ' . $default . ')';
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
$value = $this->questionHelper->ask($input, $output, new Question(
|
||||||
|
'<question>' . $text . ':</question> ',
|
||||||
|
$default
|
||||||
|
));
|
||||||
|
if (empty($value) && ! $allowEmpty) {
|
||||||
|
$output->writeln('<error>Value can\'t be empty</error>');
|
||||||
|
}
|
||||||
|
} while (empty($value) && $default === null && ! $allowEmpty);
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param OutputInterface $output
|
||||||
|
* @param string $text
|
||||||
|
*/
|
||||||
|
protected function printTitle(OutputInterface $output, $text)
|
||||||
|
{
|
||||||
|
$text = trim($text);
|
||||||
|
$length = strlen($text) + 4;
|
||||||
|
$header = str_repeat('*', $length);
|
||||||
|
|
||||||
|
$output->writeln([
|
||||||
|
'',
|
||||||
|
'<info>' . $header . '</info>',
|
||||||
|
'<info>* ' . strtoupper($text) . ' *</info>',
|
||||||
|
'<info>' . $header . '</info>',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
namespace Shlinkio\Shlink\CLI\Install\Plugin;
|
||||||
|
|
||||||
|
use Shlinkio\Shlink\CLI\Model\CustomizableAppConfig;
|
||||||
|
use Shlinkio\Shlink\Common\Util\StringUtilsTrait;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||||
|
|
||||||
|
class ApplicationConfigCustomizerPlugin extends AbstractConfigCustomizerPlugin
|
||||||
|
{
|
||||||
|
use StringUtilsTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param InputInterface $input
|
||||||
|
* @param OutputInterface $output
|
||||||
|
* @param CustomizableAppConfig $appConfig
|
||||||
|
* @return void
|
||||||
|
* @throws \Symfony\Component\Console\Exception\RuntimeException
|
||||||
|
*/
|
||||||
|
public function process(InputInterface $input, OutputInterface $output, CustomizableAppConfig $appConfig)
|
||||||
|
{
|
||||||
|
$this->printTitle($output, 'APPLICATION');
|
||||||
|
|
||||||
|
if ($appConfig->hasApp() && $this->questionHelper->ask($input, $output, new ConfirmationQuestion(
|
||||||
|
'<question>Do you want to keep imported application config? (Y/n):</question> '
|
||||||
|
))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$appConfig->setApp([
|
||||||
|
'SECRET' => $this->ask(
|
||||||
|
$input,
|
||||||
|
$output,
|
||||||
|
'Define a secret string that will be used to sign API tokens (leave empty to autogenerate one)',
|
||||||
|
null,
|
||||||
|
true
|
||||||
|
) ?: $this->generateRandomString(32),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
namespace Shlinkio\Shlink\CLI\Install\Plugin;
|
||||||
|
|
||||||
|
use Shlinkio\Shlink\CLI\Model\CustomizableAppConfig;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
|
interface ConfigCustomizerPluginInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param InputInterface $input
|
||||||
|
* @param OutputInterface $output
|
||||||
|
* @param CustomizableAppConfig $appConfig
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function process(InputInterface $input, OutputInterface $output, CustomizableAppConfig $appConfig);
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
<?php
|
||||||
|
namespace Shlinkio\Shlink\CLI\Install\Plugin;
|
||||||
|
|
||||||
|
use Acelaya\ZsmAnnotatedServices\Annotation as DI;
|
||||||
|
use Shlinkio\Shlink\CLI\Model\CustomizableAppConfig;
|
||||||
|
use Symfony\Component\Console\Exception\RuntimeException;
|
||||||
|
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use Symfony\Component\Console\Question\ChoiceQuestion;
|
||||||
|
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||||
|
use Symfony\Component\Filesystem\Exception\IOException;
|
||||||
|
use Symfony\Component\Filesystem\Filesystem;
|
||||||
|
|
||||||
|
class DatabaseConfigCustomizerPlugin extends AbstractConfigCustomizerPlugin
|
||||||
|
{
|
||||||
|
const DATABASE_DRIVERS = [
|
||||||
|
'MySQL' => 'pdo_mysql',
|
||||||
|
'PostgreSQL' => 'pdo_pgsql',
|
||||||
|
'SQLite' => 'pdo_sqlite',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Filesystem
|
||||||
|
*/
|
||||||
|
private $filesystem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DatabaseConfigCustomizerPlugin constructor.
|
||||||
|
* @param QuestionHelper $questionHelper
|
||||||
|
* @param Filesystem $filesystem
|
||||||
|
*
|
||||||
|
* @DI\Inject({QuestionHelper::class, Filesystem::class})
|
||||||
|
*/
|
||||||
|
public function __construct(QuestionHelper $questionHelper, Filesystem $filesystem)
|
||||||
|
{
|
||||||
|
parent::__construct($questionHelper);
|
||||||
|
$this->filesystem = $filesystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param InputInterface $input
|
||||||
|
* @param OutputInterface $output
|
||||||
|
* @param CustomizableAppConfig $appConfig
|
||||||
|
* @return void
|
||||||
|
* @throws IOException
|
||||||
|
* @throws RuntimeException
|
||||||
|
*/
|
||||||
|
public function process(InputInterface $input, OutputInterface $output, CustomizableAppConfig $appConfig)
|
||||||
|
{
|
||||||
|
$this->printTitle($output, 'DATABASE');
|
||||||
|
|
||||||
|
if ($appConfig->hasDatabase() && $this->questionHelper->ask($input, $output, new ConfirmationQuestion(
|
||||||
|
'<question>Do you want to keep imported database config? (Y/n):</question> '
|
||||||
|
))) {
|
||||||
|
// If the user selected to keep DB config and is configured to use sqlite, copy DB file
|
||||||
|
if ($appConfig->getDatabase()['DRIVER'] === self::DATABASE_DRIVERS['SQLite']) {
|
||||||
|
try {
|
||||||
|
$this->filesystem->copy(
|
||||||
|
$appConfig->getImportedInstallationPath() . '/' . CustomizableAppConfig::SQLITE_DB_PATH,
|
||||||
|
CustomizableAppConfig::SQLITE_DB_PATH
|
||||||
|
);
|
||||||
|
} catch (IOException $e) {
|
||||||
|
$output->writeln('<error>It wasn\'t possible to import the SQLite database</error>');
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select database type
|
||||||
|
$params = [];
|
||||||
|
$databases = array_keys(self::DATABASE_DRIVERS);
|
||||||
|
$dbType = $this->questionHelper->ask($input, $output, new ChoiceQuestion(
|
||||||
|
'<question>Select database type (defaults to ' . $databases[0] . '):</question>',
|
||||||
|
$databases,
|
||||||
|
0
|
||||||
|
));
|
||||||
|
$params['DRIVER'] = self::DATABASE_DRIVERS[$dbType];
|
||||||
|
|
||||||
|
// Ask for connection params if database is not SQLite
|
||||||
|
if ($params['DRIVER'] !== self::DATABASE_DRIVERS['SQLite']) {
|
||||||
|
$params['NAME'] = $this->ask($input, $output, 'Database name', 'shlink');
|
||||||
|
$params['USER'] = $this->ask($input, $output, 'Database username');
|
||||||
|
$params['PASSWORD'] = $this->ask($input, $output, 'Database password');
|
||||||
|
$params['HOST'] = $this->ask($input, $output, 'Database host', 'localhost');
|
||||||
|
$params['PORT'] = $this->ask($input, $output, 'Database port', $this->getDefaultDbPort($params['DRIVER']));
|
||||||
|
}
|
||||||
|
|
||||||
|
$appConfig->setDatabase($params);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getDefaultDbPort($driver)
|
||||||
|
{
|
||||||
|
return $driver === 'pdo_mysql' ? '3306' : '5432';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
namespace Shlinkio\Shlink\CLI\Install\Plugin\Factory;
|
||||||
|
|
||||||
|
use Interop\Container\ContainerInterface;
|
||||||
|
use Interop\Container\Exception\ContainerException;
|
||||||
|
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||||
|
use Zend\ServiceManager\Exception\ServiceNotCreatedException;
|
||||||
|
use Zend\ServiceManager\Exception\ServiceNotFoundException;
|
||||||
|
use Zend\ServiceManager\Factory\FactoryInterface;
|
||||||
|
|
||||||
|
class DefaultConfigCustomizerPluginFactory implements FactoryInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create an object
|
||||||
|
*
|
||||||
|
* @param ContainerInterface $container
|
||||||
|
* @param string $requestedName
|
||||||
|
* @param null|array $options
|
||||||
|
* @return object
|
||||||
|
* @throws ServiceNotFoundException if unable to resolve the service.
|
||||||
|
* @throws ServiceNotCreatedException if an exception is raised when
|
||||||
|
* creating a service.
|
||||||
|
* @throws ContainerException if any other error occurs
|
||||||
|
*/
|
||||||
|
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
|
||||||
|
{
|
||||||
|
return new $requestedName($container->get(QuestionHelper::class));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
namespace Shlinkio\Shlink\CLI\Install\Plugin;
|
||||||
|
|
||||||
|
use Shlinkio\Shlink\CLI\Model\CustomizableAppConfig;
|
||||||
|
use Symfony\Component\Console\Exception\RuntimeException;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use Symfony\Component\Console\Question\ChoiceQuestion;
|
||||||
|
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||||
|
|
||||||
|
class LanguageConfigCustomizerPlugin extends AbstractConfigCustomizerPlugin
|
||||||
|
{
|
||||||
|
const SUPPORTED_LANGUAGES = ['en', 'es'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param InputInterface $input
|
||||||
|
* @param OutputInterface $output
|
||||||
|
* @param CustomizableAppConfig $appConfig
|
||||||
|
* @return void
|
||||||
|
* @throws RuntimeException
|
||||||
|
*/
|
||||||
|
public function process(InputInterface $input, OutputInterface $output, CustomizableAppConfig $appConfig)
|
||||||
|
{
|
||||||
|
$this->printTitle($output, 'LANGUAGE');
|
||||||
|
|
||||||
|
if ($appConfig->hasLanguage() && $this->questionHelper->ask($input, $output, new ConfirmationQuestion(
|
||||||
|
'<question>Do you want to keep imported language? (Y/n):</question> '
|
||||||
|
))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$appConfig->setLanguage([
|
||||||
|
'DEFAULT' => $this->questionHelper->ask($input, $output, new ChoiceQuestion(
|
||||||
|
'<question>Select default language for the application in general (defaults to '
|
||||||
|
. self::SUPPORTED_LANGUAGES[0] . '):</question>',
|
||||||
|
self::SUPPORTED_LANGUAGES,
|
||||||
|
0
|
||||||
|
)),
|
||||||
|
'CLI' => $this->questionHelper->ask($input, $output, new ChoiceQuestion(
|
||||||
|
'<question>Select default language for CLI executions (defaults to '
|
||||||
|
. self::SUPPORTED_LANGUAGES[0] . '):</question>',
|
||||||
|
self::SUPPORTED_LANGUAGES,
|
||||||
|
0
|
||||||
|
)),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
namespace Shlinkio\Shlink\CLI\Install\Plugin;
|
||||||
|
|
||||||
|
use Shlinkio\Shlink\CLI\Model\CustomizableAppConfig;
|
||||||
|
use Shlinkio\Shlink\Core\Service\UrlShortener;
|
||||||
|
use Symfony\Component\Console\Exception\RuntimeException;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use Symfony\Component\Console\Question\ChoiceQuestion;
|
||||||
|
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||||
|
|
||||||
|
class UrlShortenerConfigCustomizerPlugin extends AbstractConfigCustomizerPlugin
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param InputInterface $input
|
||||||
|
* @param OutputInterface $output
|
||||||
|
* @param CustomizableAppConfig $appConfig
|
||||||
|
* @return void
|
||||||
|
* @throws RuntimeException
|
||||||
|
*/
|
||||||
|
public function process(InputInterface $input, OutputInterface $output, CustomizableAppConfig $appConfig)
|
||||||
|
{
|
||||||
|
$this->printTitle($output, 'URL SHORTENER');
|
||||||
|
|
||||||
|
if ($appConfig->hasUrlShortener() && $this->questionHelper->ask($input, $output, new ConfirmationQuestion(
|
||||||
|
'<question>Do you want to keep imported URL shortener config? (Y/n):</question> '
|
||||||
|
))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ask for URL shortener params
|
||||||
|
$appConfig->setUrlShortener([
|
||||||
|
'SCHEMA' => $this->questionHelper->ask($input, $output, new ChoiceQuestion(
|
||||||
|
'<question>Select schema for generated short URLs (defaults to http):</question>',
|
||||||
|
['http', 'https'],
|
||||||
|
0
|
||||||
|
)),
|
||||||
|
'HOSTNAME' => $this->ask($input, $output, 'Hostname for generated URLs'),
|
||||||
|
'CHARS' => $this->ask(
|
||||||
|
$input,
|
||||||
|
$output,
|
||||||
|
'Character set for generated short codes (leave empty to autogenerate one)',
|
||||||
|
null,
|
||||||
|
true
|
||||||
|
) ?: str_shuffle(UrlShortener::DEFAULT_CHARS)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
265
module/CLI/src/Model/CustomizableAppConfig.php
Normal file
265
module/CLI/src/Model/CustomizableAppConfig.php
Normal file
@@ -0,0 +1,265 @@
|
|||||||
|
<?php
|
||||||
|
namespace Shlinkio\Shlink\CLI\Model;
|
||||||
|
|
||||||
|
use Zend\Stdlib\ArraySerializableInterface;
|
||||||
|
|
||||||
|
final class CustomizableAppConfig implements ArraySerializableInterface
|
||||||
|
{
|
||||||
|
const SQLITE_DB_PATH = 'data/database.sqlite';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $database;
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $urlShortener;
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $language;
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $app;
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $importedInstallationPath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getDatabase()
|
||||||
|
{
|
||||||
|
return $this->database;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $database
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setDatabase(array $database)
|
||||||
|
{
|
||||||
|
$this->database = $database;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasDatabase()
|
||||||
|
{
|
||||||
|
return ! empty($this->database);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getUrlShortener()
|
||||||
|
{
|
||||||
|
return $this->urlShortener;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $urlShortener
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setUrlShortener(array $urlShortener)
|
||||||
|
{
|
||||||
|
$this->urlShortener = $urlShortener;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasUrlShortener()
|
||||||
|
{
|
||||||
|
return ! empty($this->urlShortener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getLanguage()
|
||||||
|
{
|
||||||
|
return $this->language;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $language
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setLanguage(array $language)
|
||||||
|
{
|
||||||
|
$this->language = $language;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasLanguage()
|
||||||
|
{
|
||||||
|
return ! empty($this->language);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getApp()
|
||||||
|
{
|
||||||
|
return $this->app;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $app
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setApp(array $app)
|
||||||
|
{
|
||||||
|
$this->app = $app;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasApp()
|
||||||
|
{
|
||||||
|
return ! empty($this->app);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getImportedInstallationPath()
|
||||||
|
{
|
||||||
|
return $this->importedInstallationPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $importedInstallationPath
|
||||||
|
* @return $this|self
|
||||||
|
*/
|
||||||
|
public function setImportedInstallationPath($importedInstallationPath)
|
||||||
|
{
|
||||||
|
$this->importedInstallationPath = $importedInstallationPath;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasImportedInstallationPath()
|
||||||
|
{
|
||||||
|
return $this->importedInstallationPath !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exchange internal values from provided array
|
||||||
|
*
|
||||||
|
* @param array $array
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function exchangeArray(array $array)
|
||||||
|
{
|
||||||
|
if (isset($array['app_options'], $array['app_options']['secret_key'])) {
|
||||||
|
$this->setApp([
|
||||||
|
'SECRET' => $array['app_options']['secret_key'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($array['entity_manager'], $array['entity_manager']['connection'])) {
|
||||||
|
$this->deserializeDatabase($array['entity_manager']['connection']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($array['translator'], $array['translator']['locale'], $array['cli'], $array['cli']['locale'])) {
|
||||||
|
$this->setLanguage([
|
||||||
|
'DEFAULT' => $array['translator']['locale'],
|
||||||
|
'CLI' => $array['cli']['locale'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($array['url_shortener'])) {
|
||||||
|
$urlShortener = $array['url_shortener'];
|
||||||
|
$this->setUrlShortener([
|
||||||
|
'SCHEMA' => $urlShortener['domain']['schema'],
|
||||||
|
'HOSTNAME' => $urlShortener['domain']['hostname'],
|
||||||
|
'CHARS' => $urlShortener['shortcode_chars'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function deserializeDatabase(array $conn)
|
||||||
|
{
|
||||||
|
if (! isset($conn['driver'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$driver = $conn['driver'];
|
||||||
|
|
||||||
|
$params = ['DRIVER' => $driver];
|
||||||
|
if ($driver !== 'pdo_sqlite') {
|
||||||
|
$params['USER'] = $conn['user'];
|
||||||
|
$params['PASSWORD'] = $conn['password'];
|
||||||
|
$params['NAME'] = $conn['dbname'];
|
||||||
|
$params['HOST'] = $conn['host'];
|
||||||
|
$params['PORT'] = $conn['port'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setDatabase($params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array representation of the object
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getArrayCopy()
|
||||||
|
{
|
||||||
|
$config = [
|
||||||
|
'app_options' => [
|
||||||
|
'secret_key' => $this->app['SECRET'],
|
||||||
|
],
|
||||||
|
'entity_manager' => [
|
||||||
|
'connection' => [
|
||||||
|
'driver' => $this->database['DRIVER'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'translator' => [
|
||||||
|
'locale' => $this->language['DEFAULT'],
|
||||||
|
],
|
||||||
|
'cli' => [
|
||||||
|
'locale' => $this->language['CLI'],
|
||||||
|
],
|
||||||
|
'url_shortener' => [
|
||||||
|
'domain' => [
|
||||||
|
'schema' => $this->urlShortener['SCHEMA'],
|
||||||
|
'hostname' => $this->urlShortener['HOSTNAME'],
|
||||||
|
],
|
||||||
|
'shortcode_chars' => $this->urlShortener['CHARS'],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
// Build dynamic database config based on selected driver
|
||||||
|
if ($this->database['DRIVER'] === 'pdo_sqlite') {
|
||||||
|
$config['entity_manager']['connection']['path'] = self::SQLITE_DB_PATH;
|
||||||
|
} else {
|
||||||
|
$config['entity_manager']['connection']['user'] = $this->database['USER'];
|
||||||
|
$config['entity_manager']['connection']['password'] = $this->database['PASSWORD'];
|
||||||
|
$config['entity_manager']['connection']['dbname'] = $this->database['NAME'];
|
||||||
|
$config['entity_manager']['connection']['host'] = $this->database['HOST'];
|
||||||
|
$config['entity_manager']['connection']['port'] = $this->database['PORT'];
|
||||||
|
|
||||||
|
if ($this->database['DRIVER'] === 'pdo_mysql') {
|
||||||
|
$config['entity_manager']['connection']['driverOptions'] = [
|
||||||
|
\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $config;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<?php
|
||||||
|
return [];
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace ShlinkioTest\Shlink\CLI\Command\Api;
|
namespace ShlinkioTest\Shlink\CLI\Command\Api;
|
||||||
|
|
||||||
use PHPUnit_Framework_TestCase as TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Prophecy\Prophecy\ObjectProphecy;
|
use Prophecy\Prophecy\ObjectProphecy;
|
||||||
use Shlinkio\Shlink\CLI\Command\Api\DisableKeyCommand;
|
use Shlinkio\Shlink\CLI\Command\Api\DisableKeyCommand;
|
||||||
use Shlinkio\Shlink\Common\Exception\InvalidArgumentException;
|
use Shlinkio\Shlink\Common\Exception\InvalidArgumentException;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace ShlinkioTest\Shlink\CLI\Command\Api;
|
namespace ShlinkioTest\Shlink\CLI\Command\Api;
|
||||||
|
|
||||||
use PHPUnit_Framework_TestCase as TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Prophecy\Argument;
|
use Prophecy\Argument;
|
||||||
use Prophecy\Prophecy\ObjectProphecy;
|
use Prophecy\Prophecy\ObjectProphecy;
|
||||||
use Shlinkio\Shlink\CLI\Command\Api\GenerateKeyCommand;
|
use Shlinkio\Shlink\CLI\Command\Api\GenerateKeyCommand;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace ShlinkioTest\Shlink\CLI\Command\Api;
|
namespace ShlinkioTest\Shlink\CLI\Command\Api;
|
||||||
|
|
||||||
use PHPUnit_Framework_TestCase as TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Prophecy\Prophecy\ObjectProphecy;
|
use Prophecy\Prophecy\ObjectProphecy;
|
||||||
use Shlinkio\Shlink\CLI\Command\Api\ListKeysCommand;
|
use Shlinkio\Shlink\CLI\Command\Api\ListKeysCommand;
|
||||||
use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace ShlinkioTest\Shlink\CLI\Command\Config;
|
namespace ShlinkioTest\Shlink\CLI\Command\Config;
|
||||||
|
|
||||||
use PHPUnit_Framework_TestCase as TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Shlinkio\Shlink\CLI\Command\Config\GenerateCharsetCommand;
|
use Shlinkio\Shlink\CLI\Command\Config\GenerateCharsetCommand;
|
||||||
use Shlinkio\Shlink\Core\Service\UrlShortener;
|
use Shlinkio\Shlink\Core\Service\UrlShortener;
|
||||||
use Symfony\Component\Console\Application;
|
use Symfony\Component\Console\Application;
|
||||||
|
|||||||
@@ -1,18 +1,27 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace ShlinkioTest\Shlink\CLI\Command\Install;
|
namespace ShlinkioTest\Shlink\CLI\Command\Install;
|
||||||
|
|
||||||
use PHPUnit_Framework_TestCase as TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Prophecy\Argument;
|
use Prophecy\Argument;
|
||||||
|
use Prophecy\Prophecy\MethodProphecy;
|
||||||
use Prophecy\Prophecy\ObjectProphecy;
|
use Prophecy\Prophecy\ObjectProphecy;
|
||||||
use Shlinkio\Shlink\CLI\Command\Install\InstallCommand;
|
use Shlinkio\Shlink\CLI\Command\Install\InstallCommand;
|
||||||
|
use Shlinkio\Shlink\CLI\Install\ConfigCustomizerPluginManagerInterface;
|
||||||
|
use Shlinkio\Shlink\CLI\Install\Plugin\ConfigCustomizerPluginInterface;
|
||||||
use Symfony\Component\Console\Application;
|
use Symfony\Component\Console\Application;
|
||||||
use Symfony\Component\Console\Helper\ProcessHelper;
|
use Symfony\Component\Console\Helper\ProcessHelper;
|
||||||
use Symfony\Component\Console\Tester\CommandTester;
|
use Symfony\Component\Console\Tester\CommandTester;
|
||||||
|
use Symfony\Component\Filesystem\Exception\IOException;
|
||||||
|
use Symfony\Component\Filesystem\Filesystem;
|
||||||
use Symfony\Component\Process\Process;
|
use Symfony\Component\Process\Process;
|
||||||
use Zend\Config\Writer\WriterInterface;
|
use Zend\Config\Writer\WriterInterface;
|
||||||
|
|
||||||
class InstallCommandTest extends TestCase
|
class InstallCommandTest extends TestCase
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @var InstallCommand
|
||||||
|
*/
|
||||||
|
protected $command;
|
||||||
/**
|
/**
|
||||||
* @var CommandTester
|
* @var CommandTester
|
||||||
*/
|
*/
|
||||||
@@ -21,6 +30,10 @@ class InstallCommandTest extends TestCase
|
|||||||
* @var ObjectProphecy
|
* @var ObjectProphecy
|
||||||
*/
|
*/
|
||||||
protected $configWriter;
|
protected $configWriter;
|
||||||
|
/**
|
||||||
|
* @var ObjectProphecy
|
||||||
|
*/
|
||||||
|
protected $filesystem;
|
||||||
|
|
||||||
public function setUp()
|
public function setUp()
|
||||||
{
|
{
|
||||||
@@ -31,74 +44,96 @@ class InstallCommandTest extends TestCase
|
|||||||
$processHelper->setHelperSet(Argument::any())->willReturn(null);
|
$processHelper->setHelperSet(Argument::any())->willReturn(null);
|
||||||
$processHelper->run(Argument::cetera())->willReturn($processMock->reveal());
|
$processHelper->run(Argument::cetera())->willReturn($processMock->reveal());
|
||||||
|
|
||||||
|
$this->filesystem = $this->prophesize(Filesystem::class);
|
||||||
|
$this->filesystem->exists(Argument::cetera())->willReturn(false);
|
||||||
|
|
||||||
|
$this->configWriter = $this->prophesize(WriterInterface::class);
|
||||||
|
|
||||||
|
$configCustomizer = $this->prophesize(ConfigCustomizerPluginInterface::class);
|
||||||
|
$configCustomizers = $this->prophesize(ConfigCustomizerPluginManagerInterface::class);
|
||||||
|
$configCustomizers->get(Argument::cetera())->willReturn($configCustomizer->reveal());
|
||||||
|
|
||||||
$app = new Application();
|
$app = new Application();
|
||||||
$helperSet = $app->getHelperSet();
|
$helperSet = $app->getHelperSet();
|
||||||
$helperSet->set($processHelper->reveal());
|
$helperSet->set($processHelper->reveal());
|
||||||
$app->setHelperSet($helperSet);
|
$app->setHelperSet($helperSet);
|
||||||
|
$this->command = new InstallCommand(
|
||||||
$this->configWriter = $this->prophesize(WriterInterface::class);
|
$this->configWriter->reveal(),
|
||||||
$command = new InstallCommand($this->configWriter->reveal());
|
$this->filesystem->reveal(),
|
||||||
$app->add($command);
|
$configCustomizers->reveal()
|
||||||
|
|
||||||
$questionHelper = $command->getHelper('question');
|
|
||||||
$questionHelper->setInputStream($this->createInputStream());
|
|
||||||
$this->commandTester = new CommandTester($command);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createInputStream()
|
|
||||||
{
|
|
||||||
$stream = fopen('php://memory', 'r+', false);
|
|
||||||
fputs($stream, <<<CLI_INPUT
|
|
||||||
|
|
||||||
shlink_db
|
|
||||||
alejandro
|
|
||||||
1234
|
|
||||||
0
|
|
||||||
doma.in
|
|
||||||
abc123BCA
|
|
||||||
|
|
||||||
1
|
|
||||||
my_secret
|
|
||||||
CLI_INPUT
|
|
||||||
);
|
);
|
||||||
rewind($stream);
|
$app->add($this->command);
|
||||||
|
|
||||||
return $stream;
|
$this->commandTester = new CommandTester($this->command);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
*/
|
*/
|
||||||
public function testInputIsProperlyParsed()
|
public function generatedConfigIsProperlyPersisted()
|
||||||
{
|
{
|
||||||
$this->configWriter->toFile(Argument::any(), [
|
$this->configWriter->toFile(Argument::any(), Argument::type('array'), false)->shouldBeCalledTimes(1);
|
||||||
'app_options' => [
|
$this->commandTester->execute([]);
|
||||||
'secret_key' => 'my_secret',
|
}
|
||||||
],
|
|
||||||
'entity_manager' => [
|
/**
|
||||||
'connection' => [
|
* @test
|
||||||
'driver' => 'pdo_mysql',
|
*/
|
||||||
'dbname' => 'shlink_db',
|
public function cachedConfigIsDeletedIfExists()
|
||||||
'user' => 'alejandro',
|
{
|
||||||
'password' => '1234',
|
/** @var MethodProphecy $appConfigExists */
|
||||||
],
|
$appConfigExists = $this->filesystem->exists('data/cache/app_config.php')->willReturn(true);
|
||||||
],
|
/** @var MethodProphecy $appConfigRemove */
|
||||||
'translator' => [
|
$appConfigRemove = $this->filesystem->remove('data/cache/app_config.php')->willReturn(null);
|
||||||
'locale' => 'en',
|
|
||||||
],
|
$this->commandTester->execute([]);
|
||||||
'cli' => [
|
|
||||||
'locale' => 'es',
|
$appConfigExists->shouldHaveBeenCalledTimes(1);
|
||||||
],
|
$appConfigRemove->shouldHaveBeenCalledTimes(1);
|
||||||
'url_shortener' => [
|
}
|
||||||
'domain' => [
|
|
||||||
'schema' => 'http',
|
/**
|
||||||
'hostname' => 'doma.in',
|
* @test
|
||||||
],
|
*/
|
||||||
'shortcode_chars' => 'abc123BCA',
|
public function exceptionWhileDeletingCachedConfigCancelsProcess()
|
||||||
],
|
{
|
||||||
], false)->shouldBeCalledTimes(1);
|
/** @var MethodProphecy $appConfigExists */
|
||||||
$this->commandTester->execute([
|
$appConfigExists = $this->filesystem->exists('data/cache/app_config.php')->willReturn(true);
|
||||||
'command' => 'shlink:install',
|
/** @var MethodProphecy $appConfigRemove */
|
||||||
|
$appConfigRemove = $this->filesystem->remove('data/cache/app_config.php')->willThrow(IOException::class);
|
||||||
|
/** @var MethodProphecy $configToFile */
|
||||||
|
$configToFile = $this->configWriter->toFile(Argument::cetera())->willReturn(true);
|
||||||
|
|
||||||
|
$this->commandTester->execute([]);
|
||||||
|
|
||||||
|
$appConfigExists->shouldHaveBeenCalledTimes(1);
|
||||||
|
$appConfigRemove->shouldHaveBeenCalledTimes(1);
|
||||||
|
$configToFile->shouldNotHaveBeenCalled();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function whenCommandIsUpdatePreviousConfigCanBeImported()
|
||||||
|
{
|
||||||
|
$ref = new \ReflectionObject($this->command);
|
||||||
|
$prop = $ref->getProperty('isUpdate');
|
||||||
|
$prop->setAccessible(true);
|
||||||
|
$prop->setValue($this->command, true);
|
||||||
|
|
||||||
|
/** @var MethodProphecy $importedConfigExists */
|
||||||
|
$importedConfigExists = $this->filesystem->exists(
|
||||||
|
__DIR__ . '/../../../test-resources/' . InstallCommand::GENERATED_CONFIG_PATH
|
||||||
|
)->willReturn(true);
|
||||||
|
|
||||||
|
$this->commandTester->setInputs([
|
||||||
|
'',
|
||||||
|
'/foo/bar/wrong_previous_shlink',
|
||||||
|
'',
|
||||||
|
__DIR__ . '/../../../test-resources',
|
||||||
]);
|
]);
|
||||||
|
$this->commandTester->execute([]);
|
||||||
|
|
||||||
|
$importedConfigExists->shouldHaveBeenCalled();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace ShlinkioTest\Shlink\CLI\Command\Shortcode;
|
namespace ShlinkioTest\Shlink\CLI\Command\Shortcode;
|
||||||
|
|
||||||
use PHPUnit_Framework_TestCase as TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Prophecy\Argument;
|
use Prophecy\Argument;
|
||||||
use Prophecy\Prophecy\ObjectProphecy;
|
use Prophecy\Prophecy\ObjectProphecy;
|
||||||
use Shlinkio\Shlink\CLI\Command\Shortcode\GeneratePreviewCommand;
|
use Shlinkio\Shlink\CLI\Command\Shortcode\GeneratePreviewCommand;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace ShlinkioTest\Shlink\CLI\Command\Shortcode;
|
namespace ShlinkioTest\Shlink\CLI\Command\Shortcode;
|
||||||
|
|
||||||
use PHPUnit_Framework_TestCase as TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Prophecy\Argument;
|
use Prophecy\Argument;
|
||||||
use Prophecy\Prophecy\ObjectProphecy;
|
use Prophecy\Prophecy\ObjectProphecy;
|
||||||
use Shlinkio\Shlink\CLI\Command\Shortcode\GenerateShortcodeCommand;
|
use Shlinkio\Shlink\CLI\Command\Shortcode\GenerateShortcodeCommand;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace ShlinkioTest\Shlink\CLI\Command\Shortcode;
|
namespace ShlinkioTest\Shlink\CLI\Command\Shortcode;
|
||||||
|
|
||||||
use PHPUnit_Framework_TestCase as TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Prophecy\Argument;
|
use Prophecy\Argument;
|
||||||
use Prophecy\Prophecy\ObjectProphecy;
|
use Prophecy\Prophecy\ObjectProphecy;
|
||||||
use Shlinkio\Shlink\CLI\Command\Shortcode\GetVisitsCommand;
|
use Shlinkio\Shlink\CLI\Command\Shortcode\GetVisitsCommand;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace ShlinkioTest\Shlink\CLI\Command\Shortcode;
|
namespace ShlinkioTest\Shlink\CLI\Command\Shortcode;
|
||||||
|
|
||||||
use PHPUnit_Framework_TestCase as TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Prophecy\Argument;
|
use Prophecy\Argument;
|
||||||
use Prophecy\Prophecy\ObjectProphecy;
|
use Prophecy\Prophecy\ObjectProphecy;
|
||||||
use Shlinkio\Shlink\CLI\Command\Shortcode\ListShortcodesCommand;
|
use Shlinkio\Shlink\CLI\Command\Shortcode\ListShortcodesCommand;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace ShlinkioTest\Shlink\CLI\Command\Shortcode;
|
namespace ShlinkioTest\Shlink\CLI\Command\Shortcode;
|
||||||
|
|
||||||
use PHPUnit_Framework_TestCase as TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Prophecy\Prophecy\ObjectProphecy;
|
use Prophecy\Prophecy\ObjectProphecy;
|
||||||
use Shlinkio\Shlink\CLI\Command\Shortcode\ResolveUrlCommand;
|
use Shlinkio\Shlink\CLI\Command\Shortcode\ResolveUrlCommand;
|
||||||
use Shlinkio\Shlink\Core\Exception\InvalidShortCodeException;
|
use Shlinkio\Shlink\Core\Exception\InvalidShortCodeException;
|
||||||
|
|||||||
67
module/CLI/test/Command/Tag/CreateTagCommandTest.php
Normal file
67
module/CLI/test/Command/Tag/CreateTagCommandTest.php
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
namespace ShlinkioTest\Shlink\CLI\Command\Tag;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Prophecy\Prophecy\MethodProphecy;
|
||||||
|
use Prophecy\Prophecy\ObjectProphecy;
|
||||||
|
use Shlinkio\Shlink\CLI\Command\Tag\CreateTagCommand;
|
||||||
|
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
|
||||||
|
use Symfony\Component\Console\Application;
|
||||||
|
use Symfony\Component\Console\Tester\CommandTester;
|
||||||
|
use Zend\I18n\Translator\Translator;
|
||||||
|
|
||||||
|
class CreateTagCommandTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var CreateTagCommand
|
||||||
|
*/
|
||||||
|
private $command;
|
||||||
|
/**
|
||||||
|
* @var CommandTester
|
||||||
|
*/
|
||||||
|
private $commandTester;
|
||||||
|
/**
|
||||||
|
* @var ObjectProphecy
|
||||||
|
*/
|
||||||
|
private $tagService;
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$this->tagService = $this->prophesize(TagServiceInterface::class);
|
||||||
|
|
||||||
|
$command = new CreateTagCommand($this->tagService->reveal(), Translator::factory([]));
|
||||||
|
$app = new Application();
|
||||||
|
$app->add($command);
|
||||||
|
|
||||||
|
$this->commandTester = new CommandTester($command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function errorIsReturnedWhenNoTagsAreProvided()
|
||||||
|
{
|
||||||
|
$this->commandTester->execute([]);
|
||||||
|
|
||||||
|
$output = $this->commandTester->getDisplay();
|
||||||
|
$this->assertContains('You have to provide at least one tag name', $output);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function serviceIsInvokedOnSuccess()
|
||||||
|
{
|
||||||
|
$tagNames = ['foo', 'bar'];
|
||||||
|
/** @var MethodProphecy $createTags */
|
||||||
|
$createTags = $this->tagService->createTags($tagNames)->willReturn([]);
|
||||||
|
|
||||||
|
$this->commandTester->execute([
|
||||||
|
'--name' => $tagNames,
|
||||||
|
]);
|
||||||
|
$output = $this->commandTester->getDisplay();
|
||||||
|
|
||||||
|
$this->assertContains(sprintf('Created tags: ["%s"]', implode('", "', $tagNames)), $output);
|
||||||
|
$createTags->shouldHaveBeenCalled();
|
||||||
|
}
|
||||||
|
}
|
||||||
68
module/CLI/test/Command/Tag/DeleteTagsCommandTest.php
Normal file
68
module/CLI/test/Command/Tag/DeleteTagsCommandTest.php
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
<?php
|
||||||
|
namespace ShlinkioTest\Shlink\CLI\Command\Tag;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Prophecy\Prophecy\MethodProphecy;
|
||||||
|
use Prophecy\Prophecy\ObjectProphecy;
|
||||||
|
use Shlinkio\Shlink\CLI\Command\Tag\DeleteTagsCommand;
|
||||||
|
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
|
||||||
|
use Symfony\Component\Console\Application;
|
||||||
|
use Symfony\Component\Console\Tester\CommandTester;
|
||||||
|
use Zend\I18n\Translator\Translator;
|
||||||
|
|
||||||
|
class DeleteTagsCommandTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var DeleteTagsCommand
|
||||||
|
*/
|
||||||
|
private $command;
|
||||||
|
/**
|
||||||
|
* @var CommandTester
|
||||||
|
*/
|
||||||
|
private $commandTester;
|
||||||
|
/**
|
||||||
|
* @var ObjectProphecy
|
||||||
|
*/
|
||||||
|
private $tagService;
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$this->tagService = $this->prophesize(TagServiceInterface::class);
|
||||||
|
|
||||||
|
$command = new DeleteTagsCommand($this->tagService->reveal(), Translator::factory([]));
|
||||||
|
$app = new Application();
|
||||||
|
$app->add($command);
|
||||||
|
|
||||||
|
$this->commandTester = new CommandTester($command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function errorIsReturnedWhenNoTagsAreProvided()
|
||||||
|
{
|
||||||
|
$this->commandTester->execute([]);
|
||||||
|
|
||||||
|
$output = $this->commandTester->getDisplay();
|
||||||
|
$this->assertContains('You have to provide at least one tag name', $output);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function serviceIsInvokedOnSuccess()
|
||||||
|
{
|
||||||
|
$tagNames = ['foo', 'bar'];
|
||||||
|
/** @var MethodProphecy $deleteTags */
|
||||||
|
$deleteTags = $this->tagService->deleteTags($tagNames)->will(function () {
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->commandTester->execute([
|
||||||
|
'--name' => $tagNames,
|
||||||
|
]);
|
||||||
|
$output = $this->commandTester->getDisplay();
|
||||||
|
|
||||||
|
$this->assertContains(sprintf('Deleted tags: ["%s"]', implode('", "', $tagNames)), $output);
|
||||||
|
$deleteTags->shouldHaveBeenCalled();
|
||||||
|
}
|
||||||
|
}
|
||||||
73
module/CLI/test/Command/Tag/ListTagsCommandTest.php
Normal file
73
module/CLI/test/Command/Tag/ListTagsCommandTest.php
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
namespace ShlinkioTest\Shlink\CLI\Command\Tag;
|
||||||
|
|
||||||
|
use Prophecy\Prophecy\MethodProphecy;
|
||||||
|
use Prophecy\Prophecy\ObjectProphecy;
|
||||||
|
use Shlinkio\Shlink\CLI\Command\Tag\ListTagsCommand;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Shlinkio\Shlink\Core\Entity\Tag;
|
||||||
|
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
|
||||||
|
use Symfony\Component\Console\Application;
|
||||||
|
use Symfony\Component\Console\Tester\CommandTester;
|
||||||
|
use Zend\I18n\Translator\Translator;
|
||||||
|
|
||||||
|
class ListTagsCommandTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var ListTagsCommand
|
||||||
|
*/
|
||||||
|
private $command;
|
||||||
|
/**
|
||||||
|
* @var CommandTester
|
||||||
|
*/
|
||||||
|
private $commandTester;
|
||||||
|
/**
|
||||||
|
* @var ObjectProphecy
|
||||||
|
*/
|
||||||
|
private $tagService;
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$this->tagService = $this->prophesize(TagServiceInterface::class);
|
||||||
|
|
||||||
|
$command = new ListTagsCommand($this->tagService->reveal(), Translator::factory([]));
|
||||||
|
$app = new Application();
|
||||||
|
$app->add($command);
|
||||||
|
|
||||||
|
$this->commandTester = new CommandTester($command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function noTagsPrintsEmptyMessage()
|
||||||
|
{
|
||||||
|
/** @var MethodProphecy $listTags */
|
||||||
|
$listTags = $this->tagService->listTags()->willReturn([]);
|
||||||
|
|
||||||
|
$this->commandTester->execute([]);
|
||||||
|
$output = $this->commandTester->getDisplay();
|
||||||
|
|
||||||
|
$this->assertContains('No tags yet', $output);
|
||||||
|
$listTags->shouldHaveBeenCalled();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function listOfTagsIsPrinted()
|
||||||
|
{
|
||||||
|
/** @var MethodProphecy $listTags */
|
||||||
|
$listTags = $this->tagService->listTags()->willReturn([
|
||||||
|
(new Tag())->setName('foo'),
|
||||||
|
(new Tag())->setName('bar'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->commandTester->execute([]);
|
||||||
|
$output = $this->commandTester->getDisplay();
|
||||||
|
|
||||||
|
$this->assertContains('foo', $output);
|
||||||
|
$this->assertContains('bar', $output);
|
||||||
|
$listTags->shouldHaveBeenCalled();
|
||||||
|
}
|
||||||
|
}
|
||||||
80
module/CLI/test/Command/Tag/RenameTagCommandTest.php
Normal file
80
module/CLI/test/Command/Tag/RenameTagCommandTest.php
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
<?php
|
||||||
|
namespace ShlinkioTest\Shlink\CLI\Command\Tag;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Prophecy\Prophecy\MethodProphecy;
|
||||||
|
use Prophecy\Prophecy\ObjectProphecy;
|
||||||
|
use Shlinkio\Shlink\CLI\Command\Tag\RenameTagCommand;
|
||||||
|
use Shlinkio\Shlink\Core\Entity\Tag;
|
||||||
|
use Shlinkio\Shlink\Core\Exception\EntityDoesNotExistException;
|
||||||
|
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
|
||||||
|
use Symfony\Component\Console\Application;
|
||||||
|
use Symfony\Component\Console\Tester\CommandTester;
|
||||||
|
use Zend\I18n\Translator\Translator;
|
||||||
|
|
||||||
|
class RenameTagCommandTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var RenameTagCommand
|
||||||
|
*/
|
||||||
|
private $command;
|
||||||
|
/**
|
||||||
|
* @var CommandTester
|
||||||
|
*/
|
||||||
|
private $commandTester;
|
||||||
|
/**
|
||||||
|
* @var ObjectProphecy
|
||||||
|
*/
|
||||||
|
private $tagService;
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$this->tagService = $this->prophesize(TagServiceInterface::class);
|
||||||
|
|
||||||
|
$command = new RenameTagCommand($this->tagService->reveal(), Translator::factory([]));
|
||||||
|
$app = new Application();
|
||||||
|
$app->add($command);
|
||||||
|
|
||||||
|
$this->commandTester = new CommandTester($command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function errorIsPrintedIfExceptionIsThrown()
|
||||||
|
{
|
||||||
|
$oldName = 'foo';
|
||||||
|
$newName = 'bar';
|
||||||
|
/** @var MethodProphecy $renameTag */
|
||||||
|
$renameTag = $this->tagService->renameTag($oldName, $newName)->willThrow(EntityDoesNotExistException::class);
|
||||||
|
|
||||||
|
$this->commandTester->execute([
|
||||||
|
'oldName' => $oldName,
|
||||||
|
'newName' => $newName,
|
||||||
|
]);
|
||||||
|
$output = $this->commandTester->getDisplay();
|
||||||
|
|
||||||
|
$this->assertContains('A tag with name "foo" was not found', $output);
|
||||||
|
$renameTag->shouldHaveBeenCalled();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function successIsPrintedIfNoErrorOccurs()
|
||||||
|
{
|
||||||
|
$oldName = 'foo';
|
||||||
|
$newName = 'bar';
|
||||||
|
/** @var MethodProphecy $renameTag */
|
||||||
|
$renameTag = $this->tagService->renameTag($oldName, $newName)->willReturn(new Tag());
|
||||||
|
|
||||||
|
$this->commandTester->execute([
|
||||||
|
'oldName' => $oldName,
|
||||||
|
'newName' => $newName,
|
||||||
|
]);
|
||||||
|
$output = $this->commandTester->getDisplay();
|
||||||
|
|
||||||
|
$this->assertContains('Tag properly renamed', $output);
|
||||||
|
$renameTag->shouldHaveBeenCalled();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace ShlinkioTest\Shlink\CLI\Command\Visit;
|
namespace ShlinkioTest\Shlink\CLI\Command\Visit;
|
||||||
|
|
||||||
use PHPUnit_Framework_TestCase as TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Prophecy\Argument;
|
use Prophecy\Argument;
|
||||||
use Prophecy\Prophecy\ObjectProphecy;
|
use Prophecy\Prophecy\ObjectProphecy;
|
||||||
use Shlinkio\Shlink\CLI\Command\Visit\ProcessVisitsCommand;
|
use Shlinkio\Shlink\CLI\Command\Visit\ProcessVisitsCommand;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace ShlinkioTest\Shlink\CLI;
|
namespace ShlinkioTest\Shlink\CLI;
|
||||||
|
|
||||||
use PHPUnit_Framework_TestCase as TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Shlinkio\Shlink\CLI\ConfigProvider;
|
use Shlinkio\Shlink\CLI\ConfigProvider;
|
||||||
|
|
||||||
class ConfigProviderTest extends TestCase
|
class ConfigProviderTest extends TestCase
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace ShlinkioTest\Shlink\CLI\Factory;
|
namespace ShlinkioTest\Shlink\CLI\Factory;
|
||||||
|
|
||||||
use PHPUnit_Framework_TestCase as TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Shlinkio\Shlink\CLI\Factory\ApplicationFactory;
|
use Shlinkio\Shlink\CLI\Factory\ApplicationFactory;
|
||||||
use Shlinkio\Shlink\Core\Options\AppOptions;
|
use Shlinkio\Shlink\Core\Options\AppOptions;
|
||||||
use Symfony\Component\Console\Application;
|
use Symfony\Component\Console\Application;
|
||||||
|
|||||||
33
module/CLI/test/Factory/InstallApplicationFactoryTest.php
Normal file
33
module/CLI/test/Factory/InstallApplicationFactoryTest.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
namespace ShlinkioTest\Shlink\CLI\Factory;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Shlinkio\Shlink\CLI\Factory\InstallApplicationFactory;
|
||||||
|
use Symfony\Component\Console\Application;
|
||||||
|
use Symfony\Component\Filesystem\Filesystem;
|
||||||
|
use Zend\ServiceManager\ServiceManager;
|
||||||
|
|
||||||
|
class InstallApplicationFactoryTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var InstallApplicationFactory
|
||||||
|
*/
|
||||||
|
private $factory;
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$this->factory = new InstallApplicationFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function serviceIsCreated()
|
||||||
|
{
|
||||||
|
$instance = $this->factory->__invoke(new ServiceManager(['services' => [
|
||||||
|
Filesystem::class => $this->prophesize(Filesystem::class)->reveal(),
|
||||||
|
]]), '');
|
||||||
|
|
||||||
|
$this->assertInstanceOf(Application::class, $instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
<?php
|
||||||
|
namespace ShlinkioTest\Shlink\CLI\Install\Plugin;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Prophecy\Argument;
|
||||||
|
use Prophecy\Prophecy\MethodProphecy;
|
||||||
|
use Prophecy\Prophecy\ObjectProphecy;
|
||||||
|
use Shlinkio\Shlink\CLI\Install\Plugin\ApplicationConfigCustomizerPlugin;
|
||||||
|
use Shlinkio\Shlink\CLI\Model\CustomizableAppConfig;
|
||||||
|
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||||
|
use Symfony\Component\Console\Input\ArrayInput;
|
||||||
|
use Symfony\Component\Console\Output\NullOutput;
|
||||||
|
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||||
|
|
||||||
|
class ApplicationConfigCustomizerPluginTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var ApplicationConfigCustomizerPlugin
|
||||||
|
*/
|
||||||
|
private $plugin;
|
||||||
|
/**
|
||||||
|
* @var ObjectProphecy
|
||||||
|
*/
|
||||||
|
private $questionHelper;
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$this->questionHelper = $this->prophesize(QuestionHelper::class);
|
||||||
|
$this->plugin = new ApplicationConfigCustomizerPlugin($this->questionHelper->reveal());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function configIsRequestedToTheUser()
|
||||||
|
{
|
||||||
|
/** @var MethodProphecy $askSecret */
|
||||||
|
$askSecret = $this->questionHelper->ask(Argument::cetera())->willReturn('the_secret');
|
||||||
|
$config = new CustomizableAppConfig();
|
||||||
|
|
||||||
|
$this->plugin->process(new ArrayInput([]), new NullOutput(), $config);
|
||||||
|
|
||||||
|
$this->assertTrue($config->hasApp());
|
||||||
|
$this->assertEquals([
|
||||||
|
'SECRET' => 'the_secret',
|
||||||
|
], $config->getApp());
|
||||||
|
$askSecret->shouldHaveBeenCalledTimes(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function overwriteIsRequestedIfValueIsAlreadySet()
|
||||||
|
{
|
||||||
|
/** @var MethodProphecy $ask */
|
||||||
|
$ask = $this->questionHelper->ask(Argument::cetera())->will(function (array $args) {
|
||||||
|
$last = array_pop($args);
|
||||||
|
return $last instanceof ConfirmationQuestion ? false : 'the_new_secret';
|
||||||
|
});
|
||||||
|
$config = new CustomizableAppConfig();
|
||||||
|
$config->setApp([
|
||||||
|
'SECRET' => 'foo',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->plugin->process(new ArrayInput([]), new NullOutput(), $config);
|
||||||
|
|
||||||
|
$this->assertEquals([
|
||||||
|
'SECRET' => 'the_new_secret',
|
||||||
|
], $config->getApp());
|
||||||
|
$ask->shouldHaveBeenCalledTimes(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function existingValueIsKeptIfRequested()
|
||||||
|
{
|
||||||
|
/** @var MethodProphecy $ask */
|
||||||
|
$ask = $this->questionHelper->ask(Argument::cetera())->willReturn(true);
|
||||||
|
|
||||||
|
$config = new CustomizableAppConfig();
|
||||||
|
$config->setApp([
|
||||||
|
'SECRET' => 'foo',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->plugin->process(new ArrayInput([]), new NullOutput(), $config);
|
||||||
|
|
||||||
|
$this->assertEquals([
|
||||||
|
'SECRET' => 'foo',
|
||||||
|
], $config->getApp());
|
||||||
|
$ask->shouldHaveBeenCalledTimes(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
<?php
|
||||||
|
namespace ShlinkioTest\Shlink\CLI\Install\Plugin;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Prophecy\Argument;
|
||||||
|
use Prophecy\Prophecy\MethodProphecy;
|
||||||
|
use Prophecy\Prophecy\ObjectProphecy;
|
||||||
|
use Shlinkio\Shlink\CLI\Install\Plugin\DatabaseConfigCustomizerPlugin;
|
||||||
|
use Shlinkio\Shlink\CLI\Model\CustomizableAppConfig;
|
||||||
|
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||||
|
use Symfony\Component\Console\Input\ArrayInput;
|
||||||
|
use Symfony\Component\Console\Output\NullOutput;
|
||||||
|
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||||
|
use Symfony\Component\Filesystem\Filesystem;
|
||||||
|
|
||||||
|
class DatabaseConfigCustomizerPluginTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var DatabaseConfigCustomizerPlugin
|
||||||
|
*/
|
||||||
|
private $plugin;
|
||||||
|
/**
|
||||||
|
* @var ObjectProphecy
|
||||||
|
*/
|
||||||
|
private $questionHelper;
|
||||||
|
/**
|
||||||
|
* @var ObjectProphecy
|
||||||
|
*/
|
||||||
|
private $filesystem;
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$this->questionHelper = $this->prophesize(QuestionHelper::class);
|
||||||
|
$this->filesystem = $this->prophesize(Filesystem::class);
|
||||||
|
|
||||||
|
$this->plugin = new DatabaseConfigCustomizerPlugin(
|
||||||
|
$this->questionHelper->reveal(),
|
||||||
|
$this->filesystem->reveal()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function configIsRequestedToTheUser()
|
||||||
|
{
|
||||||
|
/** @var MethodProphecy $askSecret */
|
||||||
|
$askSecret = $this->questionHelper->ask(Argument::cetera())->willReturn('MySQL');
|
||||||
|
$config = new CustomizableAppConfig();
|
||||||
|
|
||||||
|
$this->plugin->process(new ArrayInput([]), new NullOutput(), $config);
|
||||||
|
|
||||||
|
$this->assertTrue($config->hasDatabase());
|
||||||
|
$this->assertEquals([
|
||||||
|
'DRIVER' => 'pdo_mysql',
|
||||||
|
'NAME' => 'MySQL',
|
||||||
|
'USER' => 'MySQL',
|
||||||
|
'PASSWORD' => 'MySQL',
|
||||||
|
'HOST' => 'MySQL',
|
||||||
|
'PORT' => 'MySQL',
|
||||||
|
], $config->getDatabase());
|
||||||
|
$askSecret->shouldHaveBeenCalledTimes(6);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function overwriteIsRequestedIfValueIsAlreadySet()
|
||||||
|
{
|
||||||
|
/** @var MethodProphecy $ask */
|
||||||
|
$ask = $this->questionHelper->ask(Argument::cetera())->will(function (array $args) {
|
||||||
|
$last = array_pop($args);
|
||||||
|
return $last instanceof ConfirmationQuestion ? false : 'MySQL';
|
||||||
|
});
|
||||||
|
$config = new CustomizableAppConfig();
|
||||||
|
$config->setDatabase([
|
||||||
|
'DRIVER' => 'pdo_pgsql',
|
||||||
|
'NAME' => 'MySQL',
|
||||||
|
'USER' => 'MySQL',
|
||||||
|
'PASSWORD' => 'MySQL',
|
||||||
|
'HOST' => 'MySQL',
|
||||||
|
'PORT' => 'MySQL',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->plugin->process(new ArrayInput([]), new NullOutput(), $config);
|
||||||
|
|
||||||
|
$this->assertEquals([
|
||||||
|
'DRIVER' => 'pdo_mysql',
|
||||||
|
'NAME' => 'MySQL',
|
||||||
|
'USER' => 'MySQL',
|
||||||
|
'PASSWORD' => 'MySQL',
|
||||||
|
'HOST' => 'MySQL',
|
||||||
|
'PORT' => 'MySQL',
|
||||||
|
], $config->getDatabase());
|
||||||
|
$ask->shouldHaveBeenCalledTimes(7);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function existingValueIsKeptIfRequested()
|
||||||
|
{
|
||||||
|
/** @var MethodProphecy $ask */
|
||||||
|
$ask = $this->questionHelper->ask(Argument::cetera())->willReturn(true);
|
||||||
|
|
||||||
|
$config = new CustomizableAppConfig();
|
||||||
|
$config->setDatabase([
|
||||||
|
'DRIVER' => 'pdo_pgsql',
|
||||||
|
'NAME' => 'MySQL',
|
||||||
|
'USER' => 'MySQL',
|
||||||
|
'PASSWORD' => 'MySQL',
|
||||||
|
'HOST' => 'MySQL',
|
||||||
|
'PORT' => 'MySQL',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->plugin->process(new ArrayInput([]), new NullOutput(), $config);
|
||||||
|
|
||||||
|
$this->assertEquals([
|
||||||
|
'DRIVER' => 'pdo_pgsql',
|
||||||
|
'NAME' => 'MySQL',
|
||||||
|
'USER' => 'MySQL',
|
||||||
|
'PASSWORD' => 'MySQL',
|
||||||
|
'HOST' => 'MySQL',
|
||||||
|
'PORT' => 'MySQL',
|
||||||
|
], $config->getDatabase());
|
||||||
|
$ask->shouldHaveBeenCalledTimes(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function sqliteDatabaseIsImportedWhenRequested()
|
||||||
|
{
|
||||||
|
/** @var MethodProphecy $ask */
|
||||||
|
$ask = $this->questionHelper->ask(Argument::cetera())->willReturn(true);
|
||||||
|
/** @var MethodProphecy $copy */
|
||||||
|
$copy = $this->filesystem->copy(Argument::cetera())->willReturn(null);
|
||||||
|
|
||||||
|
$config = new CustomizableAppConfig();
|
||||||
|
$config->setDatabase([
|
||||||
|
'DRIVER' => 'pdo_sqlite',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->plugin->process(new ArrayInput([]), new NullOutput(), $config);
|
||||||
|
|
||||||
|
$this->assertEquals([
|
||||||
|
'DRIVER' => 'pdo_sqlite',
|
||||||
|
], $config->getDatabase());
|
||||||
|
$ask->shouldHaveBeenCalledTimes(1);
|
||||||
|
$copy->shouldHaveBeenCalledTimes(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
namespace ShlinkioTest\Shlink\CLI\Install\Plugin\Factory;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Shlinkio\Shlink\CLI\Install\Plugin\ApplicationConfigCustomizerPlugin;
|
||||||
|
use Shlinkio\Shlink\CLI\Install\Plugin\Factory\DefaultConfigCustomizerPluginFactory;
|
||||||
|
use Shlinkio\Shlink\CLI\Install\Plugin\LanguageConfigCustomizerPlugin;
|
||||||
|
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||||
|
use Zend\ServiceManager\ServiceManager;
|
||||||
|
|
||||||
|
class DefaultConfigCustomizerPluginFactoryTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var DefaultConfigCustomizerPluginFactory
|
||||||
|
*/
|
||||||
|
protected $factory;
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$this->factory = new DefaultConfigCustomizerPluginFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function createsProperService()
|
||||||
|
{
|
||||||
|
$instance = $this->factory->__invoke(new ServiceManager(['services' => [
|
||||||
|
QuestionHelper::class => $this->prophesize(QuestionHelper::class)->reveal(),
|
||||||
|
]]), ApplicationConfigCustomizerPlugin::class);
|
||||||
|
$this->assertInstanceOf(ApplicationConfigCustomizerPlugin::class, $instance);
|
||||||
|
|
||||||
|
$instance = $this->factory->__invoke(new ServiceManager(['services' => [
|
||||||
|
QuestionHelper::class => $this->prophesize(QuestionHelper::class)->reveal(),
|
||||||
|
]]), LanguageConfigCustomizerPlugin::class);
|
||||||
|
$this->assertInstanceOf(LanguageConfigCustomizerPlugin::class, $instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
<?php
|
||||||
|
namespace ShlinkioTest\Shlink\CLI\Install\Plugin;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Prophecy\Argument;
|
||||||
|
use Prophecy\Prophecy\MethodProphecy;
|
||||||
|
use Prophecy\Prophecy\ObjectProphecy;
|
||||||
|
use Shlinkio\Shlink\CLI\Install\Plugin\LanguageConfigCustomizerPlugin;
|
||||||
|
use Shlinkio\Shlink\CLI\Model\CustomizableAppConfig;
|
||||||
|
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||||
|
use Symfony\Component\Console\Input\ArrayInput;
|
||||||
|
use Symfony\Component\Console\Output\NullOutput;
|
||||||
|
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||||
|
|
||||||
|
class LanguageConfigCustomizerPluginTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var LanguageConfigCustomizerPlugin
|
||||||
|
*/
|
||||||
|
protected $plugin;
|
||||||
|
/**
|
||||||
|
* @var ObjectProphecy
|
||||||
|
*/
|
||||||
|
protected $questionHelper;
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$this->questionHelper = $this->prophesize(QuestionHelper::class);
|
||||||
|
$this->plugin = new LanguageConfigCustomizerPlugin($this->questionHelper->reveal());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function configIsRequestedToTheUser()
|
||||||
|
{
|
||||||
|
/** @var MethodProphecy $askSecret */
|
||||||
|
$askSecret = $this->questionHelper->ask(Argument::cetera())->willReturn('en');
|
||||||
|
$config = new CustomizableAppConfig();
|
||||||
|
|
||||||
|
$this->plugin->process(new ArrayInput([]), new NullOutput(), $config);
|
||||||
|
|
||||||
|
$this->assertTrue($config->hasLanguage());
|
||||||
|
$this->assertEquals([
|
||||||
|
'DEFAULT' => 'en',
|
||||||
|
'CLI' => 'en',
|
||||||
|
], $config->getLanguage());
|
||||||
|
$askSecret->shouldHaveBeenCalledTimes(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function overwriteIsRequestedIfValueIsAlreadySet()
|
||||||
|
{
|
||||||
|
/** @var MethodProphecy $ask */
|
||||||
|
$ask = $this->questionHelper->ask(Argument::cetera())->will(function (array $args) {
|
||||||
|
$last = array_pop($args);
|
||||||
|
return $last instanceof ConfirmationQuestion ? false : 'es';
|
||||||
|
});
|
||||||
|
$config = new CustomizableAppConfig();
|
||||||
|
$config->setLanguage([
|
||||||
|
'DEFAULT' => 'en',
|
||||||
|
'CLI' => 'en',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->plugin->process(new ArrayInput([]), new NullOutput(), $config);
|
||||||
|
|
||||||
|
$this->assertEquals([
|
||||||
|
'DEFAULT' => 'es',
|
||||||
|
'CLI' => 'es',
|
||||||
|
], $config->getLanguage());
|
||||||
|
$ask->shouldHaveBeenCalledTimes(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function existingValueIsKeptIfRequested()
|
||||||
|
{
|
||||||
|
/** @var MethodProphecy $ask */
|
||||||
|
$ask = $this->questionHelper->ask(Argument::cetera())->willReturn(true);
|
||||||
|
|
||||||
|
$config = new CustomizableAppConfig();
|
||||||
|
$config->setLanguage([
|
||||||
|
'DEFAULT' => 'es',
|
||||||
|
'CLI' => 'es',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->plugin->process(new ArrayInput([]), new NullOutput(), $config);
|
||||||
|
|
||||||
|
$this->assertEquals([
|
||||||
|
'DEFAULT' => 'es',
|
||||||
|
'CLI' => 'es',
|
||||||
|
], $config->getLanguage());
|
||||||
|
$ask->shouldHaveBeenCalledTimes(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
<?php
|
||||||
|
namespace ShlinkioTest\Shlink\CLI\Install\Plugin;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Prophecy\Argument;
|
||||||
|
use Prophecy\Prophecy\MethodProphecy;
|
||||||
|
use Prophecy\Prophecy\ObjectProphecy;
|
||||||
|
use Shlinkio\Shlink\CLI\Install\Plugin\UrlShortenerConfigCustomizerPlugin;
|
||||||
|
use Shlinkio\Shlink\CLI\Model\CustomizableAppConfig;
|
||||||
|
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||||
|
use Symfony\Component\Console\Input\ArrayInput;
|
||||||
|
use Symfony\Component\Console\Output\NullOutput;
|
||||||
|
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||||
|
|
||||||
|
class UrlShortenerConfigCustomizerPluginTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var UrlShortenerConfigCustomizerPlugin
|
||||||
|
*/
|
||||||
|
private $plugin;
|
||||||
|
/**
|
||||||
|
* @var ObjectProphecy
|
||||||
|
*/
|
||||||
|
private $questionHelper;
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$this->questionHelper = $this->prophesize(QuestionHelper::class);
|
||||||
|
$this->plugin = new UrlShortenerConfigCustomizerPlugin($this->questionHelper->reveal());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function configIsRequestedToTheUser()
|
||||||
|
{
|
||||||
|
/** @var MethodProphecy $askSecret */
|
||||||
|
$askSecret = $this->questionHelper->ask(Argument::cetera())->willReturn('something');
|
||||||
|
$config = new CustomizableAppConfig();
|
||||||
|
|
||||||
|
$this->plugin->process(new ArrayInput([]), new NullOutput(), $config);
|
||||||
|
|
||||||
|
$this->assertTrue($config->hasUrlShortener());
|
||||||
|
$this->assertEquals([
|
||||||
|
'SCHEMA' => 'something',
|
||||||
|
'HOSTNAME' => 'something',
|
||||||
|
'CHARS' => 'something',
|
||||||
|
], $config->getUrlShortener());
|
||||||
|
$askSecret->shouldHaveBeenCalledTimes(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function overwriteIsRequestedIfValueIsAlreadySet()
|
||||||
|
{
|
||||||
|
/** @var MethodProphecy $ask */
|
||||||
|
$ask = $this->questionHelper->ask(Argument::cetera())->will(function (array $args) {
|
||||||
|
$last = array_pop($args);
|
||||||
|
return $last instanceof ConfirmationQuestion ? false : 'foo';
|
||||||
|
});
|
||||||
|
$config = new CustomizableAppConfig();
|
||||||
|
$config->setUrlShortener([
|
||||||
|
'SCHEMA' => 'bar',
|
||||||
|
'HOSTNAME' => 'bar',
|
||||||
|
'CHARS' => 'bar',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->plugin->process(new ArrayInput([]), new NullOutput(), $config);
|
||||||
|
|
||||||
|
$this->assertEquals([
|
||||||
|
'SCHEMA' => 'foo',
|
||||||
|
'HOSTNAME' => 'foo',
|
||||||
|
'CHARS' => 'foo',
|
||||||
|
], $config->getUrlShortener());
|
||||||
|
$ask->shouldHaveBeenCalledTimes(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function existingValueIsKeptIfRequested()
|
||||||
|
{
|
||||||
|
/** @var MethodProphecy $ask */
|
||||||
|
$ask = $this->questionHelper->ask(Argument::cetera())->willReturn(true);
|
||||||
|
|
||||||
|
$config = new CustomizableAppConfig();
|
||||||
|
$config->setUrlShortener([
|
||||||
|
'SCHEMA' => 'foo',
|
||||||
|
'HOSTNAME' => 'foo',
|
||||||
|
'CHARS' => 'foo',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->plugin->process(new ArrayInput([]), new NullOutput(), $config);
|
||||||
|
|
||||||
|
$this->assertEquals([
|
||||||
|
'SCHEMA' => 'foo',
|
||||||
|
'HOSTNAME' => 'foo',
|
||||||
|
'CHARS' => 'foo',
|
||||||
|
], $config->getUrlShortener());
|
||||||
|
$ask->shouldHaveBeenCalledTimes(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
<?php
|
|
||||||
use Shlinkio\Shlink\Common\Middleware;
|
|
||||||
|
|
||||||
return [
|
|
||||||
|
|
||||||
'middleware_pipeline' => [
|
|
||||||
'pre-routing' => [
|
|
||||||
'middleware' => [
|
|
||||||
Middleware\LocaleMiddleware::class,
|
|
||||||
],
|
|
||||||
'priority' => 5,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
|
|
||||||
];
|
|
||||||
@@ -1,36 +1,36 @@
|
|||||||
<?php
|
<?php
|
||||||
if (! function_exists('env')) {
|
namespace Shlinkio\Shlink\Common;
|
||||||
/**
|
|
||||||
* Gets the value of an environment variable. Supports boolean, empty and null.
|
|
||||||
* This is basically Laravel's env helper
|
|
||||||
*
|
|
||||||
* @param string $key
|
|
||||||
* @param mixed $default
|
|
||||||
* @return mixed
|
|
||||||
* @link https://github.com/laravel/framework/blob/5.2/src/Illuminate/Foundation/helpers.php#L369
|
|
||||||
*/
|
|
||||||
function env($key, $default = null)
|
|
||||||
{
|
|
||||||
$value = getenv($key);
|
|
||||||
if ($value === false) {
|
|
||||||
return $default;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (strtolower($value)) {
|
/**
|
||||||
case 'true':
|
* Gets the value of an environment variable. Supports boolean, empty and null.
|
||||||
case '(true)':
|
* This is basically Laravel's env helper
|
||||||
return true;
|
*
|
||||||
case 'false':
|
* @param string $key
|
||||||
case '(false)':
|
* @param mixed $default
|
||||||
return false;
|
* @return mixed
|
||||||
case 'empty':
|
* @link https://github.com/laravel/framework/blob/5.2/src/Illuminate/Foundation/helpers.php#L369
|
||||||
case '(empty)':
|
*/
|
||||||
return '';
|
function env($key, $default = null)
|
||||||
case 'null':
|
{
|
||||||
case '(null)':
|
$value = getenv($key);
|
||||||
return null;
|
if ($value === false) {
|
||||||
}
|
return $default;
|
||||||
|
|
||||||
return trim($value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (strtolower($value)) {
|
||||||
|
case 'true':
|
||||||
|
case '(true)':
|
||||||
|
return true;
|
||||||
|
case 'false':
|
||||||
|
case '(false)':
|
||||||
|
return false;
|
||||||
|
case 'empty':
|
||||||
|
case '(empty)':
|
||||||
|
return '';
|
||||||
|
case 'null':
|
||||||
|
case '(null)':
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return trim($value);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ namespace Shlinkio\Shlink\Common\Factory;
|
|||||||
use Doctrine\Common\Cache;
|
use Doctrine\Common\Cache;
|
||||||
use Interop\Container\ContainerInterface;
|
use Interop\Container\ContainerInterface;
|
||||||
use Interop\Container\Exception\ContainerException;
|
use Interop\Container\Exception\ContainerException;
|
||||||
|
use Shlinkio\Shlink\Common;
|
||||||
use Shlinkio\Shlink\Core\Options\AppOptions;
|
use Shlinkio\Shlink\Core\Options\AppOptions;
|
||||||
use Zend\ServiceManager\Exception\ServiceNotCreatedException;
|
use Zend\ServiceManager\Exception\ServiceNotCreatedException;
|
||||||
use Zend\ServiceManager\Exception\ServiceNotFoundException;
|
use Zend\ServiceManager\Exception\ServiceNotFoundException;
|
||||||
@@ -48,15 +49,14 @@ class CacheFactory implements FactoryInterface
|
|||||||
{
|
{
|
||||||
// Try to get the adapter from config
|
// Try to get the adapter from config
|
||||||
$config = $container->get('config');
|
$config = $container->get('config');
|
||||||
if (isset($config['cache'])
|
if (isset($config['cache'], $config['cache']['adapter'])
|
||||||
&& isset($config['cache']['adapter'])
|
|
||||||
&& in_array($config['cache']['adapter'], self::VALID_CACHE_ADAPTERS)
|
&& in_array($config['cache']['adapter'], self::VALID_CACHE_ADAPTERS)
|
||||||
) {
|
) {
|
||||||
return $this->resolveCacheAdapter($config['cache']);
|
return $this->resolveCacheAdapter($config['cache']);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the adapter has not been set in config, create one based on environment
|
// If the adapter has not been set in config, create one based on environment
|
||||||
return env('APP_ENV', 'pro') === 'pro' ? new Cache\ApcuCache() : new Cache\ArrayCache();
|
return Common\env('APP_ENV', 'pro') === 'pro' ? new Cache\ApcuCache() : new Cache\ArrayCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -80,7 +80,7 @@ class CacheFactory implements FactoryInterface
|
|||||||
if (! isset($server['host'])) {
|
if (! isset($server['host'])) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$port = isset($server['port']) ? intval($server['port']) : 11211;
|
$port = isset($server['port']) ? (int) $server['port'] : 11211;
|
||||||
|
|
||||||
$memcached->addServer($server['host'], $port);
|
$memcached->addServer($server['host'], $port);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
namespace Shlinkio\Shlink\Common\Factory;
|
||||||
|
|
||||||
|
use Interop\Container\ContainerInterface;
|
||||||
|
use Interop\Container\Exception\ContainerException;
|
||||||
|
use Zend\Diactoros\Response\EmptyResponse;
|
||||||
|
use Zend\Expressive\Middleware\ImplicitOptionsMiddleware;
|
||||||
|
use Zend\ServiceManager\Exception\ServiceNotCreatedException;
|
||||||
|
use Zend\ServiceManager\Exception\ServiceNotFoundException;
|
||||||
|
use Zend\ServiceManager\Factory\FactoryInterface;
|
||||||
|
|
||||||
|
class EmptyResponseImplicitOptionsMiddlewareFactory implements FactoryInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create an object
|
||||||
|
*
|
||||||
|
* @param ContainerInterface $container
|
||||||
|
* @param string $requestedName
|
||||||
|
* @param null|array $options
|
||||||
|
* @return object
|
||||||
|
* @throws ServiceNotFoundException if unable to resolve the service.
|
||||||
|
* @throws ServiceNotCreatedException if an exception is raised when
|
||||||
|
* creating a service.
|
||||||
|
* @throws ContainerException if any other error occurs
|
||||||
|
*/
|
||||||
|
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
|
||||||
|
{
|
||||||
|
return new ImplicitOptionsMiddleware(new EmptyResponse());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,10 +2,11 @@
|
|||||||
namespace Shlinkio\Shlink\Common\Middleware;
|
namespace Shlinkio\Shlink\Common\Middleware;
|
||||||
|
|
||||||
use Acelaya\ZsmAnnotatedServices\Annotation\Inject;
|
use Acelaya\ZsmAnnotatedServices\Annotation\Inject;
|
||||||
|
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||||
|
use Interop\Http\ServerMiddleware\MiddlewareInterface;
|
||||||
use Psr\Http\Message\ResponseInterface as Response;
|
use Psr\Http\Message\ResponseInterface as Response;
|
||||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
use Zend\I18n\Translator\Translator;
|
use Zend\I18n\Translator\Translator;
|
||||||
use Zend\Stratigility\MiddlewareInterface;
|
|
||||||
|
|
||||||
class LocaleMiddleware implements MiddlewareInterface
|
class LocaleMiddleware implements MiddlewareInterface
|
||||||
{
|
{
|
||||||
@@ -25,40 +26,26 @@ class LocaleMiddleware implements MiddlewareInterface
|
|||||||
$this->translator = $translator;
|
$this->translator = $translator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process an incoming request and/or response.
|
* Process an incoming server request and return a response, optionally delegating
|
||||||
*
|
* to the next middleware component to create the response.
|
||||||
* Accepts a server-side request and a response instance, and does
|
|
||||||
* something with them.
|
|
||||||
*
|
|
||||||
* If the response is not complete and/or further processing would not
|
|
||||||
* interfere with the work done in the middleware, or if the middleware
|
|
||||||
* wants to delegate to another process, it can use the `$out` callable
|
|
||||||
* if present.
|
|
||||||
*
|
|
||||||
* If the middleware does not return a value, execution of the current
|
|
||||||
* request is considered complete, and the response instance provided will
|
|
||||||
* be considered the response to return.
|
|
||||||
*
|
|
||||||
* Alternately, the middleware may return a response instance.
|
|
||||||
*
|
|
||||||
* Often, middleware will `return $out();`, with the assumption that a
|
|
||||||
* later middleware will return a response.
|
|
||||||
*
|
*
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @param Response $response
|
* @param DelegateInterface $delegate
|
||||||
* @param null|callable $out
|
*
|
||||||
* @return null|Response
|
* @return Response
|
||||||
*/
|
*/
|
||||||
public function __invoke(Request $request, Response $response, callable $out = null)
|
public function process(Request $request, DelegateInterface $delegate)
|
||||||
{
|
{
|
||||||
if (! $request->hasHeader('Accept-Language')) {
|
if (! $request->hasHeader('Accept-Language')) {
|
||||||
return $out($request, $response);
|
return $delegate->process($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
$locale = $request->getHeaderLine('Accept-Language');
|
$locale = $request->getHeaderLine('Accept-Language');
|
||||||
$this->translator->setLocale($this->normalizeLocale($locale));
|
$this->translator->setLocale($this->normalizeLocale($locale));
|
||||||
return $out($request, $response);
|
return $delegate->process($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ trait StringUtilsTrait
|
|||||||
$charactersLength = strlen($characters);
|
$charactersLength = strlen($characters);
|
||||||
$randomString = '';
|
$randomString = '';
|
||||||
for ($i = 0; $i < $length; $i++) {
|
for ($i = 0; $i < $length; $i++) {
|
||||||
$randomString .= $characters[rand(0, $charactersLength - 1)];
|
$randomString .= $characters[mt_rand(0, $charactersLength - 1)];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $randomString;
|
return $randomString;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace ShlinkioTest\Shlink\Common;
|
namespace ShlinkioTest\Shlink\Common;
|
||||||
|
|
||||||
use PHPUnit_Framework_TestCase as TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Shlinkio\Shlink\Common\ConfigProvider;
|
use Shlinkio\Shlink\Common\ConfigProvider;
|
||||||
|
|
||||||
class ConfigProviderTest extends TestCase
|
class ConfigProviderTest extends TestCase
|
||||||
@@ -23,7 +23,6 @@ class ConfigProviderTest extends TestCase
|
|||||||
{
|
{
|
||||||
$config = $this->configProvider->__invoke();
|
$config = $this->configProvider->__invoke();
|
||||||
|
|
||||||
$this->assertArrayHasKey('middleware_pipeline', $config);
|
|
||||||
$this->assertArrayHasKey('dependencies', $config);
|
$this->assertArrayHasKey('dependencies', $config);
|
||||||
$this->assertArrayHasKey('twig', $config);
|
$this->assertArrayHasKey('twig', $config);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use Doctrine\Common\Cache\ArrayCache;
|
|||||||
use Doctrine\Common\Cache\FilesystemCache;
|
use Doctrine\Common\Cache\FilesystemCache;
|
||||||
use Doctrine\Common\Cache\MemcachedCache;
|
use Doctrine\Common\Cache\MemcachedCache;
|
||||||
use Doctrine\Common\Cache\RedisCache;
|
use Doctrine\Common\Cache\RedisCache;
|
||||||
use PHPUnit_Framework_TestCase as TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Shlinkio\Shlink\Common\Factory\CacheFactory;
|
use Shlinkio\Shlink\Common\Factory\CacheFactory;
|
||||||
use Shlinkio\Shlink\Core\Options\AppOptions;
|
use Shlinkio\Shlink\Core\Options\AppOptions;
|
||||||
use Zend\ServiceManager\ServiceManager;
|
use Zend\ServiceManager\ServiceManager;
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
namespace ShlinkioTest\Shlink\Common\Factory;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Shlinkio\Shlink\Common\Factory\EmptyResponseImplicitOptionsMiddlewareFactory;
|
||||||
|
use Zend\Diactoros\Response\EmptyResponse;
|
||||||
|
use Zend\Expressive\Middleware\ImplicitOptionsMiddleware;
|
||||||
|
use Zend\ServiceManager\ServiceManager;
|
||||||
|
|
||||||
|
class EmptyResponseImplicitOptionsMiddlewareFactoryTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var EmptyResponseImplicitOptionsMiddlewareFactory
|
||||||
|
*/
|
||||||
|
protected $factory;
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$this->factory = new EmptyResponseImplicitOptionsMiddlewareFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function serviceIsCreated()
|
||||||
|
{
|
||||||
|
$instance = $this->factory->__invoke(new ServiceManager(), '');
|
||||||
|
$this->assertInstanceOf(ImplicitOptionsMiddleware::class, $instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function responsePrototypeIsEmptyResponse()
|
||||||
|
{
|
||||||
|
$instance = $this->factory->__invoke(new ServiceManager(), '');
|
||||||
|
|
||||||
|
$ref = new \ReflectionObject($instance);
|
||||||
|
$prop = $ref->getProperty('response');
|
||||||
|
$prop->setAccessible(true);
|
||||||
|
$this->assertInstanceOf(EmptyResponse::class, $prop->getValue($instance));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
namespace ShlinkioTest\Shlink\Common\Factory;
|
namespace ShlinkioTest\Shlink\Common\Factory;
|
||||||
|
|
||||||
use Doctrine\ORM\EntityManager;
|
use Doctrine\ORM\EntityManager;
|
||||||
use PHPUnit_Framework_TestCase as TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Shlinkio\Shlink\Common\Factory\EntityManagerFactory;
|
use Shlinkio\Shlink\Common\Factory\EntityManagerFactory;
|
||||||
use Zend\ServiceManager\ServiceManager;
|
use Zend\ServiceManager\ServiceManager;
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
namespace ShlinkioTest\Shlink\Common\Factory;
|
namespace ShlinkioTest\Shlink\Common\Factory;
|
||||||
|
|
||||||
use Monolog\Logger;
|
use Monolog\Logger;
|
||||||
use PHPUnit_Framework_TestCase as TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Shlinkio\Shlink\Common\Factory\LoggerFactory;
|
use Shlinkio\Shlink\Common\Factory\LoggerFactory;
|
||||||
use Zend\ServiceManager\ServiceManager;
|
use Zend\ServiceManager\ServiceManager;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace ShlinkioTest\Shlink\Common\Factory;
|
namespace ShlinkioTest\Shlink\Common\Factory;
|
||||||
|
|
||||||
use PHPUnit_Framework_TestCase as TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Shlinkio\Shlink\Common\Factory\TranslatorFactory;
|
use Shlinkio\Shlink\Common\Factory\TranslatorFactory;
|
||||||
use Zend\I18n\Translator\Translator;
|
use Zend\I18n\Translator\Translator;
|
||||||
use Zend\ServiceManager\ServiceManager;
|
use Zend\ServiceManager\ServiceManager;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace ShlinkioTest\Shlink\Common\Image;
|
namespace ShlinkioTest\Shlink\Common\Image;
|
||||||
|
|
||||||
use PHPUnit_Framework_TestCase as TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Shlinkio\Shlink\Common\Image\ImageBuilder;
|
use Shlinkio\Shlink\Common\Image\ImageBuilder;
|
||||||
use Shlinkio\Shlink\Common\Image\ImageBuilderFactory;
|
use Shlinkio\Shlink\Common\Image\ImageBuilderFactory;
|
||||||
use Zend\ServiceManager\ServiceManager;
|
use Zend\ServiceManager\ServiceManager;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
namespace ShlinkioTest\Shlink\Common\Image;
|
namespace ShlinkioTest\Shlink\Common\Image;
|
||||||
|
|
||||||
use mikehaertl\wkhtmlto\Image;
|
use mikehaertl\wkhtmlto\Image;
|
||||||
use PHPUnit_Framework_TestCase as TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Shlinkio\Shlink\Common\Image\ImageFactory;
|
use Shlinkio\Shlink\Common\Image\ImageFactory;
|
||||||
use Zend\ServiceManager\ServiceManager;
|
use Zend\ServiceManager\ServiceManager;
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace ShlinkioTest\Shlink\Common\Middleware;
|
namespace ShlinkioTest\Shlink\Common\Middleware;
|
||||||
|
|
||||||
use PHPUnit_Framework_TestCase as TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Shlinkio\Shlink\Common\Middleware\LocaleMiddleware;
|
use Shlinkio\Shlink\Common\Middleware\LocaleMiddleware;
|
||||||
use Zend\Diactoros\Response;
|
use ShlinkioTest\Shlink\Common\Util\TestUtils;
|
||||||
use Zend\Diactoros\ServerRequestFactory;
|
use Zend\Diactoros\ServerRequestFactory;
|
||||||
use Zend\I18n\Translator\Translator;
|
use Zend\I18n\Translator\Translator;
|
||||||
|
|
||||||
@@ -30,9 +30,7 @@ class LocaleMiddlewareTest extends TestCase
|
|||||||
public function whenNoHeaderIsPresentLocaleIsNotChanged()
|
public function whenNoHeaderIsPresentLocaleIsNotChanged()
|
||||||
{
|
{
|
||||||
$this->assertEquals('ru', $this->translator->getLocale());
|
$this->assertEquals('ru', $this->translator->getLocale());
|
||||||
$this->middleware->__invoke(ServerRequestFactory::fromGlobals(), new Response(), function ($req, $resp) {
|
$this->middleware->process(ServerRequestFactory::fromGlobals(), TestUtils::createDelegateMock()->reveal());
|
||||||
return $resp;
|
|
||||||
});
|
|
||||||
$this->assertEquals('ru', $this->translator->getLocale());
|
$this->assertEquals('ru', $this->translator->getLocale());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,9 +41,7 @@ class LocaleMiddlewareTest extends TestCase
|
|||||||
{
|
{
|
||||||
$this->assertEquals('ru', $this->translator->getLocale());
|
$this->assertEquals('ru', $this->translator->getLocale());
|
||||||
$request = ServerRequestFactory::fromGlobals()->withHeader('Accept-Language', 'es');
|
$request = ServerRequestFactory::fromGlobals()->withHeader('Accept-Language', 'es');
|
||||||
$this->middleware->__invoke($request, new Response(), function ($req, $resp) {
|
$this->middleware->process($request, TestUtils::createDelegateMock()->reveal());
|
||||||
return $resp;
|
|
||||||
});
|
|
||||||
$this->assertEquals('es', $this->translator->getLocale());
|
$this->assertEquals('es', $this->translator->getLocale());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,18 +50,16 @@ class LocaleMiddlewareTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function localeGetsNormalized()
|
public function localeGetsNormalized()
|
||||||
{
|
{
|
||||||
|
$delegate = TestUtils::createDelegateMock();
|
||||||
|
|
||||||
$this->assertEquals('ru', $this->translator->getLocale());
|
$this->assertEquals('ru', $this->translator->getLocale());
|
||||||
|
|
||||||
$request = ServerRequestFactory::fromGlobals()->withHeader('Accept-Language', 'es_ES');
|
$request = ServerRequestFactory::fromGlobals()->withHeader('Accept-Language', 'es_ES');
|
||||||
$this->middleware->__invoke($request, new Response(), function ($req, $resp) {
|
$this->middleware->process($request, $delegate->reveal());
|
||||||
return $resp;
|
|
||||||
});
|
|
||||||
$this->assertEquals('es', $this->translator->getLocale());
|
$this->assertEquals('es', $this->translator->getLocale());
|
||||||
|
|
||||||
$request = ServerRequestFactory::fromGlobals()->withHeader('Accept-Language', 'en-US');
|
$request = ServerRequestFactory::fromGlobals()->withHeader('Accept-Language', 'en-US');
|
||||||
$this->middleware->__invoke($request, new Response(), function ($req, $resp) {
|
$this->middleware->process($request, $delegate->reveal());
|
||||||
return $resp;
|
|
||||||
});
|
|
||||||
$this->assertEquals('en', $this->translator->getLocale());
|
$this->assertEquals('en', $this->translator->getLocale());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace ShlinkioTest\Shlink\Common\Paginator;
|
namespace ShlinkioTest\Shlink\Common\Paginator;
|
||||||
|
|
||||||
use PHPUnit_Framework_TestCase as TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Prophecy\Prophecy\ObjectProphecy;
|
use Prophecy\Prophecy\ObjectProphecy;
|
||||||
use Shlinkio\Shlink\Common\Paginator\Adapter\PaginableRepositoryAdapter;
|
use Shlinkio\Shlink\Common\Paginator\Adapter\PaginableRepositoryAdapter;
|
||||||
use Shlinkio\Shlink\Common\Repository\PaginableRepositoryInterface;
|
use Shlinkio\Shlink\Common\Repository\PaginableRepositoryInterface;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ namespace ShlinkioTest\Shlink\Common\Service;
|
|||||||
use GuzzleHttp\Client;
|
use GuzzleHttp\Client;
|
||||||
use GuzzleHttp\Exception\TransferException;
|
use GuzzleHttp\Exception\TransferException;
|
||||||
use GuzzleHttp\Psr7\Response;
|
use GuzzleHttp\Psr7\Response;
|
||||||
use PHPUnit_Framework_TestCase as TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Prophecy\Prophecy\ObjectProphecy;
|
use Prophecy\Prophecy\ObjectProphecy;
|
||||||
use Shlinkio\Shlink\Common\Service\IpLocationResolver;
|
use Shlinkio\Shlink\Common\Service\IpLocationResolver;
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user