diff options
Diffstat (limited to 'application/api/controllers')
-rw-r--r-- | application/api/controllers/ApiController.php | 19 | ||||
-rw-r--r-- | application/api/controllers/History.php | 70 | ||||
-rw-r--r-- | application/api/controllers/Links.php | 112 |
3 files changed, 199 insertions, 2 deletions
diff --git a/application/api/controllers/ApiController.php b/application/api/controllers/ApiController.php index 1dd47f17..3be85b98 100644 --- a/application/api/controllers/ApiController.php +++ b/application/api/controllers/ApiController.php | |||
@@ -2,6 +2,7 @@ | |||
2 | 2 | ||
3 | namespace Shaarli\Api\Controllers; | 3 | namespace Shaarli\Api\Controllers; |
4 | 4 | ||
5 | use Shaarli\Config\ConfigManager; | ||
5 | use \Slim\Container; | 6 | use \Slim\Container; |
6 | 7 | ||
7 | /** | 8 | /** |
@@ -19,7 +20,7 @@ abstract class ApiController | |||
19 | protected $ci; | 20 | protected $ci; |
20 | 21 | ||
21 | /** | 22 | /** |
22 | * @var \ConfigManager | 23 | * @var ConfigManager |
23 | */ | 24 | */ |
24 | protected $conf; | 25 | protected $conf; |
25 | 26 | ||
@@ -29,6 +30,11 @@ abstract class ApiController | |||
29 | protected $linkDb; | 30 | protected $linkDb; |
30 | 31 | ||
31 | /** | 32 | /** |
33 | * @var \History | ||
34 | */ | ||
35 | protected $history; | ||
36 | |||
37 | /** | ||
32 | * @var int|null JSON style option. | 38 | * @var int|null JSON style option. |
33 | */ | 39 | */ |
34 | protected $jsonStyle; | 40 | protected $jsonStyle; |
@@ -45,10 +51,21 @@ abstract class ApiController | |||
45 | $this->ci = $ci; | 51 | $this->ci = $ci; |
46 | $this->conf = $ci->get('conf'); | 52 | $this->conf = $ci->get('conf'); |
47 | $this->linkDb = $ci->get('db'); | 53 | $this->linkDb = $ci->get('db'); |
54 | $this->history = $ci->get('history'); | ||
48 | if ($this->conf->get('dev.debug', false)) { | 55 | if ($this->conf->get('dev.debug', false)) { |
49 | $this->jsonStyle = JSON_PRETTY_PRINT; | 56 | $this->jsonStyle = JSON_PRETTY_PRINT; |
50 | } else { | 57 | } else { |
51 | $this->jsonStyle = null; | 58 | $this->jsonStyle = null; |
52 | } | 59 | } |
53 | } | 60 | } |
61 | |||
62 | /** | ||
63 | * Get the container. | ||
64 | * | ||
65 | * @return Container | ||
66 | */ | ||
67 | public function getCi() | ||
68 | { | ||
69 | return $this->ci; | ||
70 | } | ||
54 | } | 71 | } |
diff --git a/application/api/controllers/History.php b/application/api/controllers/History.php new file mode 100644 index 00000000..2ff9deaf --- /dev/null +++ b/application/api/controllers/History.php | |||
@@ -0,0 +1,70 @@ | |||
1 | <?php | ||
2 | |||
3 | |||
4 | namespace Shaarli\Api\Controllers; | ||
5 | |||
6 | use Shaarli\Api\Exceptions\ApiBadParametersException; | ||
7 | use Slim\Http\Request; | ||
8 | use Slim\Http\Response; | ||
9 | |||
10 | /** | ||
11 | * Class History | ||
12 | * | ||
13 | * REST API Controller: /history | ||
14 | * | ||
15 | * @package Shaarli\Api\Controllers | ||
16 | */ | ||
17 | class History extends ApiController | ||
18 | { | ||
19 | /** | ||
20 | * Service providing operation regarding Shaarli datastore and settings. | ||
21 | * | ||
22 | * @param Request $request Slim request. | ||
23 | * @param Response $response Slim response. | ||
24 | * | ||
25 | * @return Response response. | ||
26 | * | ||
27 | * @throws ApiBadParametersException Invalid parameters. | ||
28 | */ | ||
29 | public function getHistory($request, $response) | ||
30 | { | ||
31 | $history = $this->history->getHistory(); | ||
32 | |||
33 | // Return history operations from the {offset}th, starting from {since}. | ||
34 | $since = \DateTime::createFromFormat(\DateTime::ATOM, $request->getParam('since')); | ||
35 | $offset = $request->getParam('offset'); | ||
36 | if (empty($offset)) { | ||
37 | $offset = 0; | ||
38 | } | ||
39 | else if (ctype_digit($offset)) { | ||
40 | $offset = (int) $offset; | ||
41 | } else { | ||
42 | throw new ApiBadParametersException('Invalid offset'); | ||
43 | } | ||
44 | |||
45 | // limit parameter is either a number of links or 'all' for everything. | ||
46 | $limit = $request->getParam('limit'); | ||
47 | if (empty($limit)) { | ||
48 | $limit = count($history); | ||
49 | } else if (ctype_digit($limit)) { | ||
50 | $limit = (int) $limit; | ||
51 | } else { | ||
52 | throw new ApiBadParametersException('Invalid limit'); | ||
53 | } | ||
54 | |||
55 | $out = []; | ||
56 | $i = 0; | ||
57 | foreach ($history as $entry) { | ||
58 | if ((! empty($since) && $entry['datetime'] <= $since) || count($out) >= $limit) { | ||
59 | break; | ||
60 | } | ||
61 | if (++$i > $offset) { | ||
62 | $out[$i] = $entry; | ||
63 | $out[$i]['datetime'] = $out[$i]['datetime']->format(\DateTime::ATOM); | ||
64 | } | ||
65 | } | ||
66 | $out = array_values($out); | ||
67 | |||
68 | return $response->withJson($out, 200, $this->jsonStyle); | ||
69 | } | ||
70 | } | ||
diff --git a/application/api/controllers/Links.php b/application/api/controllers/Links.php index d4f1a09c..eb78dd26 100644 --- a/application/api/controllers/Links.php +++ b/application/api/controllers/Links.php | |||
@@ -97,11 +97,121 @@ class Links extends ApiController | |||
97 | */ | 97 | */ |
98 | public function getLink($request, $response, $args) | 98 | public function getLink($request, $response, $args) |
99 | { | 99 | { |
100 | if (! isset($this->linkDb[$args['id']])) { | 100 | if (!isset($this->linkDb[$args['id']])) { |
101 | throw new ApiLinkNotFoundException(); | 101 | throw new ApiLinkNotFoundException(); |
102 | } | 102 | } |
103 | $index = index_url($this->ci['environment']); | 103 | $index = index_url($this->ci['environment']); |
104 | $out = ApiUtils::formatLink($this->linkDb[$args['id']], $index); | 104 | $out = ApiUtils::formatLink($this->linkDb[$args['id']], $index); |
105 | |||
105 | return $response->withJson($out, 200, $this->jsonStyle); | 106 | return $response->withJson($out, 200, $this->jsonStyle); |
106 | } | 107 | } |
108 | |||
109 | /** | ||
110 | * Creates a new link from posted request body. | ||
111 | * | ||
112 | * @param Request $request Slim request. | ||
113 | * @param Response $response Slim response. | ||
114 | * | ||
115 | * @return Response response. | ||
116 | */ | ||
117 | public function postLink($request, $response) | ||
118 | { | ||
119 | $data = $request->getParsedBody(); | ||
120 | $link = ApiUtils::buildLinkFromRequest($data, $this->conf->get('privacy.default_private_links')); | ||
121 | // duplicate by URL, return 409 Conflict | ||
122 | if (! empty($link['url']) && ! empty($dup = $this->linkDb->getLinkFromUrl($link['url']))) { | ||
123 | return $response->withJson( | ||
124 | ApiUtils::formatLink($dup, index_url($this->ci['environment'])), | ||
125 | 409, | ||
126 | $this->jsonStyle | ||
127 | ); | ||
128 | } | ||
129 | |||
130 | $link['id'] = $this->linkDb->getNextId(); | ||
131 | $link['shorturl'] = link_small_hash($link['created'], $link['id']); | ||
132 | |||
133 | // note: general relative URL | ||
134 | if (empty($link['url'])) { | ||
135 | $link['url'] = '?' . $link['shorturl']; | ||
136 | } | ||
137 | |||
138 | if (empty($link['title'])) { | ||
139 | $link['title'] = $link['url']; | ||
140 | } | ||
141 | |||
142 | $this->linkDb[$link['id']] = $link; | ||
143 | $this->linkDb->save($this->conf->get('resource.page_cache')); | ||
144 | $this->history->addLink($link); | ||
145 | $out = ApiUtils::formatLink($link, index_url($this->ci['environment'])); | ||
146 | $redirect = $this->ci->router->relativePathFor('getLink', ['id' => $link['id']]); | ||
147 | return $response->withAddedHeader('Location', $redirect) | ||
148 | ->withJson($out, 201, $this->jsonStyle); | ||
149 | } | ||
150 | |||
151 | /** | ||
152 | * Updates an existing link from posted request body. | ||
153 | * | ||
154 | * @param Request $request Slim request. | ||
155 | * @param Response $response Slim response. | ||
156 | * @param array $args Path parameters. including the ID. | ||
157 | * | ||
158 | * @return Response response. | ||
159 | * | ||
160 | * @throws ApiLinkNotFoundException generating a 404 error. | ||
161 | */ | ||
162 | public function putLink($request, $response, $args) | ||
163 | { | ||
164 | if (! isset($this->linkDb[$args['id']])) { | ||
165 | throw new ApiLinkNotFoundException(); | ||
166 | } | ||
167 | |||
168 | $index = index_url($this->ci['environment']); | ||
169 | $data = $request->getParsedBody(); | ||
170 | |||
171 | $requestLink = ApiUtils::buildLinkFromRequest($data, $this->conf->get('privacy.default_private_links')); | ||
172 | // duplicate URL on a different link, return 409 Conflict | ||
173 | if (! empty($requestLink['url']) | ||
174 | && ! empty($dup = $this->linkDb->getLinkFromUrl($requestLink['url'])) | ||
175 | && $dup['id'] != $args['id'] | ||
176 | ) { | ||
177 | return $response->withJson( | ||
178 | ApiUtils::formatLink($dup, $index), | ||
179 | 409, | ||
180 | $this->jsonStyle | ||
181 | ); | ||
182 | } | ||
183 | |||
184 | $responseLink = $this->linkDb[$args['id']]; | ||
185 | $responseLink = ApiUtils::updateLink($responseLink, $requestLink); | ||
186 | $this->linkDb[$responseLink['id']] = $responseLink; | ||
187 | $this->linkDb->save($this->conf->get('resource.page_cache')); | ||
188 | $this->history->updateLink($responseLink); | ||
189 | |||
190 | $out = ApiUtils::formatLink($responseLink, $index); | ||
191 | return $response->withJson($out, 200, $this->jsonStyle); | ||
192 | } | ||
193 | |||
194 | /** | ||
195 | * Delete an existing link by its ID. | ||
196 | * | ||
197 | * @param Request $request Slim request. | ||
198 | * @param Response $response Slim response. | ||
199 | * @param array $args Path parameters. including the ID. | ||
200 | * | ||
201 | * @return Response response. | ||
202 | * | ||
203 | * @throws ApiLinkNotFoundException generating a 404 error. | ||
204 | */ | ||
205 | public function deleteLink($request, $response, $args) | ||
206 | { | ||
207 | if (! isset($this->linkDb[$args['id']])) { | ||
208 | throw new ApiLinkNotFoundException(); | ||
209 | } | ||
210 | $link = $this->linkDb[$args['id']]; | ||
211 | unset($this->linkDb[(int) $args['id']]); | ||
212 | $this->linkDb->save($this->conf->get('resource.page_cache')); | ||
213 | $this->history->deleteLink($link); | ||
214 | |||
215 | return $response->withStatus(204); | ||
216 | } | ||
107 | } | 217 | } |