Skip to content

Commit af86338

Browse files
authored
Retrieve older content versions (#10)
* Added function to retrieve older content versions
1 parent f21e0e5 commit af86338

File tree

8 files changed

+534
-9
lines changed

8 files changed

+534
-9
lines changed

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,14 @@ $createdPage = $searchResults->getResultAt(0);
6666
$resultContent = $client->content()->get(1234567890);
6767
```
6868

69+
#### Fetch old versions of a page or comment by content id
70+
```php
71+
/* @var $client CloudPlayDev\ConfluenceClient\ConfluenceClient */
72+
73+
//Get a page or comment in a specific version
74+
$resultContentInVersion2 = $client->content()->get(1234567890, 2);
75+
```
76+
6977
#### Fetch page descendants
7078
```php
7179
use CloudPlayDev\ConfluenceClient\Api\Content;
@@ -76,6 +84,17 @@ use CloudPlayDev\ConfluenceClient\Api\Content;
7684
$childContent = $client->content()->children($page, Content::CONTENT_TYPE_PAGE); //\CloudPlayDev\ConfluenceClient\Entity\ContentSearchResult
7785
```
7886

87+
#### Fetch content history
88+
```php
89+
use CloudPlayDev\ConfluenceClient\Api\Content;
90+
/* @var $client CloudPlayDev\ConfluenceClient\ConfluenceClient */
91+
/* @var $page CloudPlayDev\ConfluenceClient\Entity\ContentPage */
92+
93+
//get child content
94+
$pageId = 2323232323;
95+
$historyData = $client->content()->history($pageId); //\CloudPlayDev\ConfluenceClient\Entity\ContentSearchResult
96+
```
97+
7998
### Manipulating content
8099

81100
#### Create new page

src/Api/Content.php

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
namespace CloudPlayDev\ConfluenceClient\Api;
55

66
use CloudPlayDev\ConfluenceClient\Entity\AbstractContent;
7+
use CloudPlayDev\ConfluenceClient\Entity\ContentHistory;
78
use CloudPlayDev\ConfluenceClient\Entity\ContentSearchResult;
89
use CloudPlayDev\ConfluenceClient\Entity\ContentBody;
10+
use CloudPlayDev\ConfluenceClient\Entity\Hydratable;
911
use CloudPlayDev\ConfluenceClient\Exception\ConfluencePhpClientException;
1012
use Http\Client\Exception as HttpClientException;
1113
use JsonException;
@@ -43,19 +45,28 @@ class Content extends AbstractApi
4345
* default value for expand query parameter
4446
*/
4547
private const DEFAULT_EXPAND = 'space,version,body.storage,container';
48+
private const DEFAULT_HISTORY_EXPAND = 'content,content.space,content.version,content.body.storage,content.container';
4649

4750
/**
4851
* @see https://docs.atlassian.com/atlassian-confluence/REST/6.6.0/#content-getContent
49-
* @param int $contentId
50-
* @return AbstractContent|null
5152
* @throws ConfluencePhpClientException
53+
* @throws HttpClientException
54+
* @throws JsonException
5255
*/
53-
public function get(int $contentId): ?AbstractContent
56+
public function get(int $contentId, ?int $version = null): ?AbstractContent
5457
{
55-
$response = $this->httpGet(
56-
self::getRestfulUri('content', $contentId),
57-
['expand' => self::DEFAULT_EXPAND]
58-
);
58+
$fetchUri = ['content', $contentId];
59+
$parameter = ['expand' => self::DEFAULT_EXPAND];
60+
61+
if ($version !== null) {
62+
$fetchUri[] = 'version';
63+
$fetchUri[] = $version;
64+
65+
$parameter = ['expand' => self::DEFAULT_HISTORY_EXPAND];
66+
}
67+
68+
$response = $this->httpGet(self::getRestfulUri(...$fetchUri), $parameter);
69+
5970
return $this->hydrateResponse($response, AbstractContent::class);
6071
}
6172

@@ -69,7 +80,7 @@ public function get(int $contentId): ?AbstractContent
6980
public function find(array $searchParameter): ContentSearchResult
7081
{
7182
$allowedSearchParameter = ['title', 'spaceKey', 'type', 'id'];
72-
$queryParameter = array_filter($searchParameter, static function(string $searchKey) use ($allowedSearchParameter) {
83+
$queryParameter = array_filter($searchParameter, static function (string $searchKey) use ($allowedSearchParameter) {
7384
return in_array($searchKey, $allowedSearchParameter, true);
7485
}, ARRAY_FILTER_USE_KEY);
7586

@@ -138,7 +149,7 @@ public function create(AbstractContent $content): AbstractContent
138149
];
139150

140151
if (count($content->getAncestors()) > 0) {
141-
$ancestorsData = array_map(static function(int $id) {
152+
$ancestorsData = array_map(static function (int $id) {
142153
return ['id' => $id];
143154
}, $content->getAncestors());
144155

@@ -236,4 +247,19 @@ public function convert(ContentBody $convertBody, string $to = 'view', ?Abstract
236247
);
237248

238249
}
250+
251+
/**
252+
* Returns the history of a particular piece of content, sorted by version number in descending order.
253+
*
254+
* @param int $contentId
255+
* @return Hydratable
256+
* @throws HttpClientException
257+
*/
258+
public function history(int $contentId): Hydratable
259+
{
260+
return $this->hydrateResponse(
261+
$this->httpGet(self::getRestfulUri('content', $contentId, 'history')),
262+
ContentHistory::class
263+
);
264+
}
239265
}

src/Entity/AbstractContent.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ abstract class AbstractContent implements Hydratable
3232
private ?int $containerId = null;
3333
private string $containerType = Content::CONTENT_TYPE_PAGE;
3434

35+
private bool $isLatest = true;
36+
3537
/**
3638
* @return string
3739
*/
@@ -271,6 +273,11 @@ public function addAncestor(int $id): self
271273
*/
272274
public static function load(array $data): self
273275
{
276+
/* handle older content versions */
277+
if(isset($data['content'], $data['when'])) {
278+
return self::load($data['content']);
279+
}
280+
274281
Assert::true(isset($data['id'],
275282
$data['type'],
276283
$data['title'],
@@ -302,8 +309,24 @@ public static function load(array $data): self
302309
$content->setContent((string)$data['body']['storage']['value']);
303310
}
304311

312+
if(isset($data['status'])) {
313+
Assert::string($data['status']);
314+
$content->setLatest($data['status'] === 'current');
315+
}
316+
305317
return $content;
306318
}
307319

320+
public function isLatest(): bool
321+
{
322+
return $this->isLatest;
323+
}
324+
325+
protected function setLatest(bool $isLatest): AbstractContent
326+
{
327+
$this->isLatest = $isLatest;
328+
return $this;
329+
}
330+
308331

309332
}

src/Entity/ContentHistory.php

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace CloudPlayDev\ConfluenceClient\Entity;
5+
6+
use CloudPlayDev\ConfluenceClient\Exception\HydrationException;
7+
use DateTimeImmutable;
8+
use DateTimeInterface;
9+
use Webmozart\Assert\Assert;
10+
11+
class ContentHistory implements Hydratable
12+
{
13+
14+
15+
private DateTimeInterface $createdDate;
16+
private DateTimeInterface $updatedDate;
17+
private bool $isLatest = false;
18+
private User $createdBy;
19+
private User $updatedBy;
20+
21+
private int $lastVersionNumber;
22+
23+
/**
24+
* @throws HydrationException
25+
*/
26+
public static function load(array $data): ContentHistory
27+
{
28+
$contentHistory = new self;
29+
Assert::keyExists($data, 'createdDate');
30+
Assert::keyExists($data, 'createdBy');
31+
Assert::keyExists($data, 'lastUpdated');
32+
Assert::isArray($data['createdBy']);
33+
Assert::isArray($data['lastUpdated']);
34+
35+
Assert::keyExists($data['lastUpdated'], 'by');
36+
Assert::isArray($data['lastUpdated']['by']);
37+
38+
if(isset($data['latest'])) {
39+
Assert::boolean($data['latest']);
40+
$contentHistory->setLatest($data['latest']);
41+
}
42+
43+
$contentHistory->setCreatedDate(self::getDateTimeFromString($data['createdDate']));
44+
$contentHistory->setCreatedBy(User::load($data['createdBy']));
45+
$contentHistory->setUpdatedBy(User::load($data['lastUpdated']['by']));
46+
47+
$contentHistory->setUpdatedDate(self::getDateTimeFromString($data['lastUpdated']['when']));
48+
49+
$contentHistory->setLastVersionNumber($data['lastUpdated']['number']);
50+
51+
return $contentHistory;
52+
}
53+
54+
/**
55+
* @throws HydrationException
56+
*/
57+
private static function getDateTimeFromString(string $dateString): DateTimeInterface
58+
{
59+
$dateTimeImmutable = DateTimeImmutable::createFromFormat('Y-m-d\TH:i:s.vZ', $dateString);
60+
if($dateTimeImmutable === false) {
61+
throw new HydrationException('Invalid date string: ' . $dateString);
62+
}
63+
64+
return $dateTimeImmutable;
65+
}
66+
67+
private function setLatest(bool $latest): ContentHistory
68+
{
69+
$this->isLatest = $latest;
70+
return $this;
71+
}
72+
73+
private function setCreatedDate(DateTimeInterface $createFromFormat): ContentHistory
74+
{
75+
$this->createdDate = $createFromFormat;
76+
return $this;
77+
}
78+
79+
private function setCreatedBy(User $user): ContentHistory
80+
{
81+
$this->createdBy = $user;
82+
return $this;
83+
}
84+
85+
private function setUpdatedBy(User $user): ContentHistory
86+
{
87+
$this->updatedBy = $user;
88+
return $this;
89+
}
90+
91+
public function setUpdatedDate(DateTimeInterface $updatedDate): ContentHistory
92+
{
93+
$this->updatedDate = $updatedDate;
94+
return $this;
95+
}
96+
97+
public function getUpdatedDate(): DateTimeInterface
98+
{
99+
return $this->updatedDate;
100+
}
101+
102+
public function getCreatedDate(): DateTimeInterface
103+
{
104+
return $this->createdDate;
105+
}
106+
107+
public function isLatest(): bool
108+
{
109+
return $this->isLatest;
110+
}
111+
112+
public function getCreatedBy(): User
113+
{
114+
return $this->createdBy;
115+
}
116+
117+
public function getUpdatedBy(): User
118+
{
119+
return $this->updatedBy;
120+
}
121+
122+
public function getLastVersionNumber(): int
123+
{
124+
return $this->lastVersionNumber;
125+
}
126+
127+
public function setLastVersionNumber(int $lastVersionNumber): void
128+
{
129+
$this->lastVersionNumber = $lastVersionNumber;
130+
}
131+
132+
133+
}

0 commit comments

Comments
 (0)