mirror of
https://github.com/nextcloud/richdocuments.git
synced 2025-12-17 21:12:14 +01:00
feat(AI): generate presentations with AI
Signed-off-by: Elizabeth Danzberger <elizabeth@elzody.dev>
This commit is contained in:
parent
4c15cfefd6
commit
53376d304e
13 changed files with 493 additions and 29 deletions
BIN
emptyTemplates/ai/pitch.odp
Normal file
BIN
emptyTemplates/ai/pitch.odp
Normal file
Binary file not shown.
BIN
emptyTemplates/ai/security.odp
Normal file
BIN
emptyTemplates/ai/security.odp
Normal file
Binary file not shown.
BIN
emptyTemplates/ai/triangle.odp
Normal file
BIN
emptyTemplates/ai/triangle.odp
Normal file
Binary file not shown.
|
|
@ -7,6 +7,10 @@
|
||||||
namespace OCA\Richdocuments\Service;
|
namespace OCA\Richdocuments\Service;
|
||||||
|
|
||||||
use OCA\Richdocuments\AppInfo\Application;
|
use OCA\Richdocuments\AppInfo\Application;
|
||||||
|
use OCA\Richdocuments\TaskProcessing\Presentation\LayoutType;
|
||||||
|
use OCA\Richdocuments\TaskProcessing\Presentation\Presentation;
|
||||||
|
use OCA\Richdocuments\TaskProcessing\Presentation\Slides\TitleContentSlide;
|
||||||
|
use OCA\Richdocuments\TaskProcessing\Presentation\Slides\TitleSlide;
|
||||||
use OCA\Richdocuments\TemplateManager;
|
use OCA\Richdocuments\TemplateManager;
|
||||||
use OCP\IConfig;
|
use OCP\IConfig;
|
||||||
use OCP\TaskProcessing\Exception\Exception;
|
use OCP\TaskProcessing\Exception\Exception;
|
||||||
|
|
@ -20,11 +24,40 @@ use RuntimeException;
|
||||||
|
|
||||||
class SlideDeckService {
|
class SlideDeckService {
|
||||||
public const PROMPT = <<<EOF
|
public const PROMPT = <<<EOF
|
||||||
Draft a presentation slide deck with headlines and a maximum of 5 bullet points per headline.
|
Draft a presentation with slides based on the following JSON.
|
||||||
Use the following JSON structure for your whole output and output only the JSON array:
|
Replace the title, subtitle, and content with your own.
|
||||||
|
If the content is an array of bullet point strings, replace them as necessary and always use at least four of them. Do not place any dot (.) or hyphen (-) before the bullet points.
|
||||||
|
Choose one of the following three presentation styles and replace the "presentationStyle" field with your choice: security, pitch, triangle
|
||||||
|
Security is dark and serious, pitch is light and playful, and triangle is light and geometric.
|
||||||
|
The slide titles should not contain more than three words.
|
||||||
|
Use the following JSON structure for your entire output.
|
||||||
|
Output 5 or more of the JSON objects, and use each of them at least once.
|
||||||
|
Output only the JSON array:
|
||||||
|
|
||||||
```
|
```
|
||||||
[{"headline": "Headline 1", "points": ["Bullet point 1", "Bullet point 2"]}, {"headline": "Headline 2", "points": ["Bullet point 1", "Bullet point 2"]}]
|
[
|
||||||
|
{
|
||||||
|
"layout": 0,
|
||||||
|
"title": "Presentation title",
|
||||||
|
"subtitle": "Presentation subtitle"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"layout": 1,
|
||||||
|
"title": "Slide title",
|
||||||
|
"content": "Paragraph or other longer text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"layout": 1,
|
||||||
|
"title": "Slide title",
|
||||||
|
"content": [
|
||||||
|
"Bullet point one",
|
||||||
|
"Bullet point two",
|
||||||
|
"Bullet point three",
|
||||||
|
"Bullet point four"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{ "presentationStyle": "" },
|
||||||
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
Only output the JSON array. Do not wrap it with spaces, new lines or backticks (`).
|
Only output the JSON array. Do not wrap it with spaces, new lines or backticks (`).
|
||||||
|
|
@ -45,19 +78,21 @@ EOF;
|
||||||
|
|
||||||
$ooxml = $this->config->getAppValue(Application::APPNAME, 'doc_format', 'ooxml') === 'ooxml';
|
$ooxml = $this->config->getAppValue(Application::APPNAME, 'doc_format', 'ooxml') === 'ooxml';
|
||||||
$format = $ooxml ? 'pptx' : 'odp';
|
$format = $ooxml ? 'pptx' : 'odp';
|
||||||
$emptyPresentation = $this->getBlankPresentation($format);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$parsedStructure = $this->parseModelJSON($rawModelOutput);
|
[$presentationStyle, $parsedStructure] = $this->parseModelJSON($rawModelOutput);
|
||||||
} catch (\JsonException) {
|
} catch (\JsonException) {
|
||||||
throw new RuntimeException('LLM generated faulty JSON data');
|
throw new RuntimeException('LLM generated faulty JSON data');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$emptyPresentation = $this->getPresentationTemplate($presentationStyle);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$transformedPresentation = $this->remoteService->transformDocumentStructure(
|
$transformedPresentation = $this->remoteService->transformDocumentStructure(
|
||||||
'presentation.' . $format,
|
'presentation.' . $format,
|
||||||
$emptyPresentation,
|
$emptyPresentation,
|
||||||
$parsedStructure
|
$parsedStructure,
|
||||||
|
$format
|
||||||
);
|
);
|
||||||
|
|
||||||
return $transformedPresentation;
|
return $transformedPresentation;
|
||||||
|
|
@ -81,37 +116,49 @@ EOF;
|
||||||
flags: JSON_THROW_ON_ERROR
|
flags: JSON_THROW_ON_ERROR
|
||||||
);
|
);
|
||||||
|
|
||||||
$slideCommands = [];
|
$layoutTypes = array_column(LayoutType::cases(), 'value');
|
||||||
foreach ($modelJSON as $index => $slide) {
|
$presentation = new Presentation();
|
||||||
if (count($slideCommands) > 0) {
|
|
||||||
$slideCommands[] = [ 'JumpToSlide' => 'last' ];
|
foreach ($modelJSON as $index => $slideJSON) {
|
||||||
$slideCommands[] = [ 'InsertMasterSlide' => 0 ];
|
if ($slideJSON['presentationStyle']) {
|
||||||
} else {
|
$presentation->setStyle($slideJSON['presentationStyle']);
|
||||||
$slideCommands[] = [ 'JumpToSlide' => $index];
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$slideCommands[] = [ 'ChangeLayoutByName' => 'AUTOLAYOUT_TITLE_CONTENT' ];
|
$validLayout = array_key_exists($slideJSON['layout'], $layoutTypes);
|
||||||
$slideCommands[] = [ 'SetText.0' => $slide['headline'] ];
|
|
||||||
|
|
||||||
$editTextObjectCommands = [
|
if (!$validLayout) {
|
||||||
[ 'SelectParagraph' => 0 ],
|
continue;
|
||||||
[ 'InsertText' => implode(PHP_EOL, $slide['points']) ],
|
|
||||||
];
|
|
||||||
|
|
||||||
$slideCommands[] = [ 'EditTextObject.1' => $editTextObjectCommands ];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return [ 'SlideCommands' => $slideCommands ];
|
$slideLayout = LayoutType::from($layoutTypes[$slideJSON['layout']]);
|
||||||
|
|
||||||
|
$slide = match ($slideLayout) {
|
||||||
|
LayoutType::Title => new TitleSlide($index, $slideJSON['title'], $slideJSON['subtitle']),
|
||||||
|
|
||||||
|
LayoutType::TitleContent => new TitleContentSlide($index, $slideJSON['title'], $slideJSON['content']),
|
||||||
|
|
||||||
|
default => null,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (is_null($slide)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$presentation->addSlide($slide);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$presentation->getStyle(), $presentation->getSlideCommands()];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a blank presentation file in memory
|
* Creates a presentation file in memory
|
||||||
*
|
*
|
||||||
* @param string $format
|
* @param string $name
|
||||||
* @return resource
|
* @return resource
|
||||||
*/
|
*/
|
||||||
private function getBlankPresentation(string $format) {
|
private function getPresentationTemplate(string $name = '') {
|
||||||
$emptyPresentationContent = $this->templateManager->getEmptyFileContent($format);
|
$emptyPresentationContent = $this->templateManager->getAITemplate($name);
|
||||||
$memoryStream = fopen('php://memory', 'r+');
|
$memoryStream = fopen('php://memory', 'r+');
|
||||||
|
|
||||||
if (!$memoryStream) {
|
if (!$memoryStream) {
|
||||||
|
|
|
||||||
12
lib/TaskProcessing/Presentation/ISlide.php
Normal file
12
lib/TaskProcessing/Presentation/ISlide.php
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace OCA\Richdocuments\TaskProcessing\Presentation;
|
||||||
|
|
||||||
|
interface ISlide {
|
||||||
|
public function getPosition(): int;
|
||||||
|
public function getSlideCommands(): array;
|
||||||
|
}
|
||||||
13
lib/TaskProcessing/Presentation/LayoutType.php
Normal file
13
lib/TaskProcessing/Presentation/LayoutType.php
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace OCA\Richdocuments\TaskProcessing\Presentation;
|
||||||
|
|
||||||
|
enum LayoutType: string {
|
||||||
|
case Title = 'AUTOLAYOUT_TITLE';
|
||||||
|
case TitleContent = 'AUTOLAYOUT_TITLE_CONTENT';
|
||||||
|
case Title2Content = 'AUTOLAYOUT_TITLE_2CONTENT';
|
||||||
|
}
|
||||||
56
lib/TaskProcessing/Presentation/Presentation.php
Normal file
56
lib/TaskProcessing/Presentation/Presentation.php
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace OCA\Richdocuments\TaskProcessing\Presentation;
|
||||||
|
|
||||||
|
class Presentation {
|
||||||
|
/** @var ISlide[] $slides */
|
||||||
|
private $slides = [];
|
||||||
|
|
||||||
|
/** @var string $style */
|
||||||
|
private $style = 'security';
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ISlide $slide Slide to be inserted into the presentation
|
||||||
|
*/
|
||||||
|
public function addSlide(ISlide $slide): void {
|
||||||
|
$this->slides[] = $slide;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ISlide[] Array of slides in the presentation
|
||||||
|
*/
|
||||||
|
public function getSlides(): array {
|
||||||
|
return $this->slides;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setStyle(string $style): void {
|
||||||
|
$this->style = $style;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStyle(): string {
|
||||||
|
return $this->style;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array Slide commands to be passed to an external API
|
||||||
|
*/
|
||||||
|
public function getSlideCommands(): array {
|
||||||
|
$slideCommands = array_map(
|
||||||
|
function (ISlide $slide) {
|
||||||
|
return $slide->getSlideCommands();
|
||||||
|
},
|
||||||
|
$this->getSlides(),
|
||||||
|
);
|
||||||
|
|
||||||
|
$slideCommands = array_merge([], ...$slideCommands);
|
||||||
|
|
||||||
|
return [ 'SlideCommands' => $slideCommands ];
|
||||||
|
}
|
||||||
|
}
|
||||||
75
lib/TaskProcessing/Presentation/Slides/TitleContentSlide.php
Normal file
75
lib/TaskProcessing/Presentation/Slides/TitleContentSlide.php
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace OCA\Richdocuments\TaskProcessing\Presentation\Slides;
|
||||||
|
|
||||||
|
use OCA\Richdocuments\TaskProcessing\Presentation\ISlide;
|
||||||
|
|
||||||
|
class TitleContentSlide implements ISlide {
|
||||||
|
private int $position;
|
||||||
|
private string $title;
|
||||||
|
private string|array $content;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
int $position,
|
||||||
|
string $title,
|
||||||
|
string|array $content) {
|
||||||
|
$this->position = $position;
|
||||||
|
$this->title = $title;
|
||||||
|
$this->content = $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle(): string {
|
||||||
|
return $this->title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContent(): string|array {
|
||||||
|
return $this->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPosition(): int {
|
||||||
|
return $this->position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSlideCommands(): array {
|
||||||
|
$slideCommands = [];
|
||||||
|
|
||||||
|
if ($this->getPosition() > 1) {
|
||||||
|
$slideCommands[] = [ 'DuplicateSlide' => $this->getPosition() - 1 ];
|
||||||
|
}
|
||||||
|
|
||||||
|
$slideCommands[] = [ 'JumpToSlide' => $this->getPosition() ];
|
||||||
|
|
||||||
|
$slideCommands[] = [
|
||||||
|
'EditTextObject.0' => [
|
||||||
|
'SelectParagraph' => 0,
|
||||||
|
'InsertText' => $this->getTitle(),
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
if (is_array($this->getContent())) {
|
||||||
|
$slideCommands[] = [
|
||||||
|
'EditTextObject.1' => [
|
||||||
|
'SelectText' => [],
|
||||||
|
'UnoCommand' => '.uno:Cut',
|
||||||
|
'InsertText' => implode(PHP_EOL, array_map(function ($bulletPoint) {
|
||||||
|
return '• ' . $bulletPoint;
|
||||||
|
}, $this->getContent())),
|
||||||
|
]
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
$slideCommands[] = [
|
||||||
|
'EditTextObject.1' => [
|
||||||
|
'SelectText' => [],
|
||||||
|
'UnoCommand' => '.uno:Cut',
|
||||||
|
'InsertText' => $this->getContent(),
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $slideCommands;
|
||||||
|
}
|
||||||
|
}
|
||||||
56
lib/TaskProcessing/Presentation/Slides/TitleSlide.php
Normal file
56
lib/TaskProcessing/Presentation/Slides/TitleSlide.php
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace OCA\Richdocuments\TaskProcessing\Presentation\Slides;
|
||||||
|
|
||||||
|
use OCA\Richdocuments\TaskProcessing\Presentation\ISlide;
|
||||||
|
|
||||||
|
class TitleSlide implements ISlide {
|
||||||
|
private int $position;
|
||||||
|
private string $title;
|
||||||
|
private string $subtitle;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
int $position,
|
||||||
|
string $title,
|
||||||
|
string $subtitle) {
|
||||||
|
$this->position = $position;
|
||||||
|
$this->title = $title;
|
||||||
|
$this->subtitle = $subtitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle(): string {
|
||||||
|
return $this->title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSubtitle(): string {
|
||||||
|
return $this->subtitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPosition(): int {
|
||||||
|
return $this->position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSlideCommands(): array {
|
||||||
|
$slideCommands = [];
|
||||||
|
|
||||||
|
$slideCommands[] = [
|
||||||
|
'EditTextObject.0' => [
|
||||||
|
'SelectParagraph' => 0,
|
||||||
|
'InsertText' => $this->getTitle(),
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$slideCommands[] = [
|
||||||
|
'EditTextObject.1' => [
|
||||||
|
'SelectParagraph' => 0,
|
||||||
|
'InsertText' => $this->getSubtitle(),
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
return $slideCommands;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -80,11 +80,28 @@ class SlideDeckGenerationProvider implements ISynchronousProvider {
|
||||||
throw new \RuntimeException('Invalid input, expected "text" key with string value');
|
throw new \RuntimeException('Invalid input, expected "text" key with string value');
|
||||||
}
|
}
|
||||||
|
|
||||||
$response = $this->slideDeckService->generateSlideDeck(
|
$response = $this->withRetry(function () use ($userId, $input) {
|
||||||
|
return $this->slideDeckService->generateSlideDeck(
|
||||||
$userId,
|
$userId,
|
||||||
$input['text'],
|
$input['text'],
|
||||||
);
|
);
|
||||||
|
});
|
||||||
|
|
||||||
return ['slide_deck' => $response];
|
return ['slide_deck' => $response];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function withRetry(callable $action, $maxAttempts = 2) {
|
||||||
|
$attempt = 0;
|
||||||
|
|
||||||
|
while ($attempt < $maxAttempts) {
|
||||||
|
try {
|
||||||
|
$attempt += 1;
|
||||||
|
return $action();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
if ($attempt === $maxAttempts) {
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -471,6 +471,21 @@ class TemplateManager {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getAITemplate(?string $templateName = 'security'): string {
|
||||||
|
$emptyAITemplates = __DIR__ . '/../emptyTemplates/ai/';
|
||||||
|
$fullTemplatePath = $emptyAITemplates . $templateName . '.odp';
|
||||||
|
|
||||||
|
if (file_exists($fullTemplatePath)) {
|
||||||
|
$emptyFileContent = file_get_contents($fullTemplatePath);
|
||||||
|
|
||||||
|
if ($emptyFileContent !== false) {
|
||||||
|
return $emptyFileContent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return default content for empty files of a given filename by file extension
|
* Return default content for empty files of a given filename by file extension
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tests\Richdocuments;
|
||||||
|
|
||||||
|
use OCA\Richdocuments\TaskProcessing\Presentation\Slides\TitleContentSlide;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class TitleContentSlideTest extends TestCase {
|
||||||
|
public function setUp(): void {
|
||||||
|
parent::setUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCreateWithStringContent(): void {
|
||||||
|
$slide = new TitleContentSlide(0, 'Title', 'Content');
|
||||||
|
|
||||||
|
$this->assertInstanceOf(TitleContentSlide::class, $slide);
|
||||||
|
|
||||||
|
$this->assertEquals($slide->getPosition(), 0);
|
||||||
|
$this->assertEquals($slide->getTitle(), 'Title');
|
||||||
|
|
||||||
|
$this->assertIsString($slide->getContent());
|
||||||
|
$this->assertEquals($slide->getContent(), 'Content');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCreateWithArrayContent(): void {
|
||||||
|
$slide = new TitleContentSlide(0, 'Title', ['Content', 'Content']);
|
||||||
|
|
||||||
|
$this->assertInstanceOf(TitleContentSlide::class, $slide);
|
||||||
|
|
||||||
|
$this->assertEquals($slide->getPosition(), 0);
|
||||||
|
$this->assertEquals($slide->getTitle(), 'Title');
|
||||||
|
|
||||||
|
$this->assertIsArray($slide->getContent());
|
||||||
|
$this->assertEquals($slide->getContent(), ['Content', 'Content']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSlideCommandsWithStringContent(): void {
|
||||||
|
$slide = new TitleContentSlide(0, 'Title', 'Content');
|
||||||
|
|
||||||
|
$expectedSlideCommands = [
|
||||||
|
[ 'JumpToSlide' => 0 ],
|
||||||
|
|
||||||
|
[
|
||||||
|
'EditTextObject.0' => [
|
||||||
|
'SelectParagraph' => 0,
|
||||||
|
'InsertText' => 'Title',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'EditTextObject.1' => [
|
||||||
|
'SelectParagraph' => 0,
|
||||||
|
'InsertText' => 'Content',
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->assertJsonStringEqualsJsonString(
|
||||||
|
json_encode($expectedSlideCommands),
|
||||||
|
json_encode($slide->getSlideCommands()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSlideCommandsWithArrayContent(): void {
|
||||||
|
$slide = new TitleContentSlide(0, 'Title', ['Content', 'Content']);
|
||||||
|
|
||||||
|
$expectedSlideCommands = [
|
||||||
|
// Jump to slide at $this->getPosition()
|
||||||
|
[ 'JumpToSlide' => 0 ],
|
||||||
|
|
||||||
|
[
|
||||||
|
'EditTextObject.0' => [
|
||||||
|
'SelectParagraph' => 0,
|
||||||
|
'InsertText' => 'Title',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'EditTextObject.1' => [
|
||||||
|
'SelectParagraph' => 0,
|
||||||
|
'InsertText' => 'Content' . PHP_EOL . 'Content',
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->assertJsonStringEqualsJsonString(
|
||||||
|
json_encode($expectedSlideCommands),
|
||||||
|
json_encode($slide->getSlideCommands()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSlideCommandsWithPosition(): void {
|
||||||
|
$slide = new TitleContentSlide(2, 'Title', 'Content');
|
||||||
|
|
||||||
|
$expectedSlideCommands = [
|
||||||
|
// Duplicates slide at index $this->getPosition() - 1
|
||||||
|
[ 'DuplicateSlide' => 1 ],
|
||||||
|
|
||||||
|
// Jump to slide at $this->getPosition()
|
||||||
|
[ 'JumpToSlide' => 2 ],
|
||||||
|
|
||||||
|
[
|
||||||
|
'EditTextObject.0' => [
|
||||||
|
'SelectParagraph' => 0,
|
||||||
|
'InsertText' => 'Title',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'EditTextObject.1' => [
|
||||||
|
'SelectParagraph' => 0,
|
||||||
|
'InsertText' => 'Content',
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->assertJsonStringEqualsJsonString(
|
||||||
|
json_encode($expectedSlideCommands),
|
||||||
|
json_encode($slide->getSlideCommands()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tests\Richdocuments;
|
||||||
|
|
||||||
|
use OCA\Richdocuments\TaskProcessing\Presentation\Slides\TitleSlide;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class TitleSlideTest extends TestCase {
|
||||||
|
public function setUp(): void {
|
||||||
|
parent::setUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCreateTitleSlide(): void {
|
||||||
|
$slide = new TitleSlide(0, 'Title', 'Subtitle');
|
||||||
|
|
||||||
|
$this->assertInstanceOf(TitleSlide::class, $slide);
|
||||||
|
|
||||||
|
$this->assertEquals($slide->getPosition(), 0);
|
||||||
|
$this->assertEquals($slide->getTitle(), 'Title');
|
||||||
|
$this->assertEquals($slide->getSubtitle(), 'Subtitle');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSlideCommands(): void {
|
||||||
|
$slide = new TitleSlide(0, 'Title', 'Subtitle');
|
||||||
|
|
||||||
|
$expectedSlideCommands = [
|
||||||
|
[
|
||||||
|
'EditTextObject.0' => [
|
||||||
|
'SelectParagraph' => 0,
|
||||||
|
'InsertText' => 'Title',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'EditTextObject.1' => [
|
||||||
|
'SelectParagraph' => 0,
|
||||||
|
'InsertText' => 'Subtitle',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->assertJsonStringEqualsJsonString(
|
||||||
|
json_encode($expectedSlideCommands),
|
||||||
|
json_encode($slide->getSlideCommands()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue