Skip to content

Commit 952a8c9

Browse files
author
Artem Stepin
committed
* Option to get content children $client->content()->children()
* Option to get content descendants `$client->content()->descendants()` * Page comments can also be retrieved `$client->content()->children($page, Content::CONTENT_TYPE_COMMENT)` * The complete body is now retrieved as default
1 parent dc70b65 commit 952a8c9

File tree

7 files changed

+190
-126
lines changed

7 files changed

+190
-126
lines changed

README.md

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,17 @@ $ php composer.phar require cloudplaydev/confluence-php-client
2020
declare(strict_types=1);
2121

2222
use CloudPlayDev\ConfluenceClient\ConfluenceClient;
23-
use CloudPlayDev\ConfluenceClient\Entity\Content as ContentEntity;
23+
use CloudPlayDev\ConfluenceClient\Api\Content;
24+
use CloudPlayDev\ConfluenceClient\Entity\ContentPage;
2425

2526
//Create the Confluence Client
2627
$client = new ConfluenceClient('https://url-to-conluence');
2728

2829
//authenticate with a private access token
2930
$client->authenticate('NjU2OTA4NDI2MTY5OkBznOUO8YjaUF7KoOruZRXhILJ9');
3031

31-
//Create a confluence content
32-
$page = new ContentEntity();
32+
//Create a confluence content page
33+
$page = new ContentPage();
3334

3435
//Configure your page
3536
$page->setSpace('testSpaceKey')
@@ -49,14 +50,10 @@ $createdPage = $client->content()->findOneBy([
4950
$createdPage->setContent('some new content');
5051
$client->content()->update($createdPage);
5152

53+
//get child content
54+
$childContent = $client->content()->children($createdPage, Content::CONTENT_TYPE_PAGE);
5255

5356

54-
```
55-
56-
57-
### Get your development instance
5857

59-
Atlassian changed the way to work on Confluence/Jira, now in order to create your plugin, you have to get a [Developer Account](http://go.atlassian.com/cloud-dev) and create your own instance. All the steps to create your environment are defined on the [documentation page](https://developer.atlassian.com/static/connect/docs/latest/guides/development-setup.html).
60-
61-
Once you have access to your own Atlassian Cloud instance and you put it in developer mode, we can continue and let the instance contact us.
58+
```
6259

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "cloudplaydev/confluence-php-client",
33
"description": "Confluence API PHP Client",
4-
"version": "0.1.0",
4+
"version": "0.1.1",
55
"license": "MIT",
66
"keywords": [
77
"Atlassian",

src/Api/Content.php

Lines changed: 135 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,43 +3,69 @@
33

44
namespace CloudPlayDev\ConfluenceClient\Api;
55

6-
use CloudPlayDev\ConfluenceClient\Entity\Content as ContentEntity;
6+
use CloudPlayDev\ConfluenceClient\Entity\AbstractContent;
7+
use CloudPlayDev\ConfluenceClient\Entity\ContentComment;
8+
use CloudPlayDev\ConfluenceClient\Entity\ContentPage;
79
use CloudPlayDev\ConfluenceClient\Exception\Exception;
810
use CloudPlayDev\ConfluenceClient\Exception\RequestException;
911
use Http\Client\Exception as HttpClientException;
1012
use JsonException;
1113
use Psr\Http\Message\ResponseInterface;
14+
use function assert;
1215
use function count;
1316
use function in_array;
1417
use function is_array;
18+
use function is_int;
19+
use function is_string;
1520

21+
/**
22+
* Class Content
23+
* @package CloudPlayDev\ConfluenceClient\Api
24+
*/
1625
class Content extends AbstractApi
1726
{
1827
/**
19-
* @param int|null $contentId
28+
* ContentType for confluence attachments
29+
*/
30+
public const CONTENT_TYPE_ATTACHMENT = 'attachment';
31+
32+
/**
33+
* ContentType for confluence comments
34+
*/
35+
public const CONTENT_TYPE_COMMENT = 'comment';
36+
37+
/**
38+
* ContentType for confluence page content
39+
*/
40+
public const CONTENT_TYPE_PAGE = 'page';
41+
42+
/**
43+
* @param string|int|null ...$parameter
2044
* @return string
2145
*/
22-
private static function getContentUri(?int $contentId = null): string
46+
private static function getContentUri(...$parameter): string
2347
{
2448
$uri = 'content';
25-
if (null !== $contentId) {
26-
$uri .= '/' . $contentId;
49+
$parameterString = implode('/', array_filter($parameter));
50+
51+
if (!empty($parameterString)) {
52+
$uri .= '/' . $parameterString;
2753
}
2854

29-
return $uri;
55+
return $uri . '?';
3056
}
3157

3258
/**
3359
* @param int $contentId
34-
* @return ContentEntity
60+
* @return AbstractContent
3561
* @throws Exception
3662
* @throws JsonException
3763
* @throws RequestException
3864
* @throws HttpClientException
3965
*/
40-
public function findOneById(int $contentId): ContentEntity
66+
public function findOneById(int $contentId): AbstractContent
4167
{
42-
$response = $this->get(self::getContentUri($contentId));
68+
$response = $this->get(self::getContentUri($contentId), ['expand' => 'space,version,body.storage']);
4369

4470
if ($response->getStatusCode() !== 200) {
4571
throw new RequestException($response);
@@ -49,13 +75,13 @@ public function findOneById(int $contentId): ContentEntity
4975
}
5076

5177
/**
52-
* @param ContentEntity $page
78+
* @param AbstractContent $page
5379
* @return ResponseInterface
5480
* @throws Exception
5581
* @throws JsonException
5682
* @throws HttpClientException
5783
*/
58-
public function update(ContentEntity $page): ResponseInterface
84+
public function update(AbstractContent $page): ResponseInterface
5985
{
6086
$contentId = $page->getId();
6187
if (null === $contentId) {
@@ -80,13 +106,13 @@ public function update(ContentEntity $page): ResponseInterface
80106
}
81107

82108
/**
83-
* @param ContentEntity $page
84-
* @return ContentEntity
109+
* @param AbstractContent $page
110+
* @return AbstractContent
85111
* @throws Exception
86112
* @throws HttpClientException
87113
* @throws JsonException
88114
*/
89-
public function create(ContentEntity $page): ContentEntity
115+
public function create(AbstractContent $page): AbstractContent
90116
{
91117
if (null !== $page->getId()) {
92118
throw new Exception('Only new pages can be created.');
@@ -115,10 +141,10 @@ public function create(ContentEntity $page): ContentEntity
115141
}
116142

117143
/**
118-
* @param ContentEntity $page
144+
* @param AbstractContent $page
119145
* @return ResponseInterface
120146
*/
121-
public function remove(ContentEntity $page): ResponseInterface
147+
public function remove(AbstractContent $page): ResponseInterface
122148
{
123149
$contentId = $page->getId();
124150
if (null === $contentId) {
@@ -128,43 +154,95 @@ public function remove(ContentEntity $page): ResponseInterface
128154
}
129155

130156
/**
131-
* @param array{title?: string, spaceKey?: string, type?: string, id?: int|string, expand?: string} $searchParameter
132-
* @return ContentEntity|null
157+
* @param AbstractContent $content
158+
* @param string|null $contentType
159+
* @return AbstractContent[]
160+
* @throws HttpClientException
161+
* @throws JsonException
162+
*/
163+
public function children(AbstractContent $content, ?string $contentType = null): array
164+
{
165+
return $this->parseSearchResults(
166+
$this->get(
167+
self::getContentUri($content->getId(), 'child', $contentType),
168+
['expand' => 'space,version,body.storage']
169+
),
170+
);
171+
}
172+
173+
/**
174+
* @param AbstractContent $content
175+
* @param string|null $contentType
176+
* @return AbstractContent[]
177+
* @throws HttpClientException
178+
* @throws JsonException
179+
*/
180+
public function descendants(AbstractContent $content, ?string $contentType = null): array
181+
{
182+
return $this->parseSearchResults($this->get(self::getContentUri($content->getId(), 'descendant', $contentType)));
183+
}
184+
185+
/**
186+
* @param array{title?: string, spaceKey?: string, type?: string, id?: int|string} $searchParameter
187+
* @return AbstractContent|null
133188
* @throws Exception
134189
* @throws JsonException
135190
*/
136-
public function findOneBy(array $searchParameter): ?ContentEntity
191+
public function findOneBy(array $searchParameter): ?AbstractContent
137192
{
138-
$allowedSearchParameter = ['title', 'spaceKey', 'type', 'id', 'expand'];
193+
$allowedSearchParameter = ['title', 'spaceKey', 'type', 'id'];
139194
$queryParameter = array_filter($searchParameter, static function (string $searchKey) use ($allowedSearchParameter) {
140195
return in_array($searchKey, $allowedSearchParameter, true);
141196
}, ARRAY_FILTER_USE_KEY);
142197

198+
$queryParameter['expand'] = 'space,version,body.storage';
199+
143200
$searchResponse = $this->get('content?', $queryParameter);
144201

145202
if ($searchResponse->getStatusCode() !== 200) {
146203
throw new RequestException($searchResponse);
147204
}
148205

149-
$decodedSearchResponse = (array)json_decode($searchResponse->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR);
206+
$searchResults = $this->parseSearchResults($searchResponse);
207+
if (count($searchResults) > 0) {
208+
return reset($searchResults);
209+
}
210+
211+
return null;
212+
}
213+
214+
/**
215+
* @param ResponseInterface $response
216+
* @return AbstractContent[]
217+
* @throws JsonException
218+
*/
219+
private function parseSearchResults(ResponseInterface $response): array
220+
{
221+
$decodedSearchResponse = json_decode($response->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR);
222+
assert(is_array($decodedSearchResponse));
223+
assert(isset($decodedSearchResponse['results'], $decodedSearchResponse['size']));
224+
assert(is_array($decodedSearchResponse['results']));
225+
assert(is_int($decodedSearchResponse['size']));
226+
227+
$results = [];
228+
if ($decodedSearchResponse['size'] >= 1 && count($decodedSearchResponse['results']) >= 1) {
150229

151-
if (isset($decodedSearchResponse['results'], $decodedSearchResponse['size']) && is_array($decodedSearchResponse['results']) && $decodedSearchResponse['size'] >= 1 && count($decodedSearchResponse['results']) >= 1) {
152-
$firstPage = (array)reset($decodedSearchResponse['results']);
153-
if (isset($firstPage['id'])) {
154-
return $this->findOneById((int)$firstPage['id']);
230+
foreach ($decodedSearchResponse['results'] as $resultEntity) {
231+
assert(is_array($resultEntity));
232+
$results[] = $this->deserializeContent($resultEntity);
155233
}
156234
}
157235

158-
return null;
236+
return $results;
159237
}
160238

161239
/**
162240
* @param ResponseInterface $response
163-
* @return ContentEntity
241+
* @return AbstractContent
164242
* @throws Exception
165243
* @throws JsonException
166244
*/
167-
private function deserialize(ResponseInterface $response): ContentEntity
245+
private function deserialize(ResponseInterface $response): AbstractContent
168246
{
169247
$responseData = $response->getBody()->getContents();
170248

@@ -179,29 +257,42 @@ private function deserialize(ResponseInterface $response): ContentEntity
179257

180258
/**
181259
* @param mixed[] $decodedData
182-
* @return ContentEntity
260+
* @return AbstractContent
183261
* @throws Exception
184262
*/
185-
private function deserializeContent(array $decodedData): ContentEntity
263+
private function deserializeContent(array $decodedData): AbstractContent
186264
{
187-
if (!isset($decodedData['id'],
265+
assert(isset($decodedData['id'],
188266
$decodedData['type'],
189267
$decodedData['title'],
190-
$decodedData['_links']['self'],
191-
$decodedData['space']['key'],
192-
$decodedData['version']['number'])
193-
) {
194-
throw new Exception('Invalid content data');
268+
$decodedData['_links']['self']));
269+
assert(is_string($decodedData['type']));
270+
271+
switch ($decodedData['type']) {
272+
case self::CONTENT_TYPE_PAGE:
273+
$content = new ContentPage();
274+
break;
275+
case self::CONTENT_TYPE_COMMENT:
276+
$content = new ContentComment();
277+
break;
278+
default:
279+
throw new Exception('Invalid content type: ' . $decodedData['type']);
195280
}
196281

197-
$page = new ContentEntity();
198-
$page->setId((int)$decodedData['id']);
199-
$page->setType((string)$decodedData['type']);
200-
$page->setTitle((string)$decodedData['title']);
201-
$page->setUrl((string)$decodedData['_links']['self']);
202-
$page->setSpace(str_replace('/rest/api/space/', '', (string)$decodedData['space']['key']));
203-
$page->setVersion((int)$decodedData['version']['number']);
282+
$content->setId((int)$decodedData['id']);
283+
$content->setType($decodedData['type']);
284+
$content->setTitle((string)$decodedData['title']);
285+
$content->setUrl((string)$decodedData['_links']['self']);
286+
if (isset($decodedData['space']['key'])) {
287+
$content->setSpace((string)$decodedData['space']['key']);
288+
}
289+
if (isset($decodedData['version']['number'])) {
290+
$content->setVersion((int)$decodedData['version']['number']);
291+
}
292+
if(isset($decodedData['body']['storage']['value'])) {
293+
$content->setContent($decodedData['body']['storage']['value']);
294+
}
204295

205-
return $page;
296+
return $content;
206297
}
207298
}

0 commit comments

Comments
 (0)