| 
									
										
										
										
											2021-12-19 20:56:27 +08:00
										 |  |  | <?php | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace BookStack\Entities\Tools; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-19 23:40:52 +08:00
										 |  |  | use BookStack\Actions\Tag; | 
					
						
							|  |  |  | use BookStack\Entities\Models\Book; | 
					
						
							| 
									
										
										
										
											2022-09-28 21:14:51 +08:00
										 |  |  | use BookStack\Entities\Models\Bookshelf; | 
					
						
							| 
									
										
										
										
											2021-12-19 23:40:52 +08:00
										 |  |  | use BookStack\Entities\Models\Chapter; | 
					
						
							| 
									
										
										
										
											2021-12-19 20:56:27 +08:00
										 |  |  | use BookStack\Entities\Models\Entity; | 
					
						
							| 
									
										
										
										
											2022-10-24 19:12:48 +08:00
										 |  |  | use BookStack\Entities\Models\HasCoverImage; | 
					
						
							| 
									
										
										
										
											2021-12-19 20:56:27 +08:00
										 |  |  | use BookStack\Entities\Models\Page; | 
					
						
							| 
									
										
										
										
											2021-12-20 03:20:31 +08:00
										 |  |  | use BookStack\Entities\Repos\BookRepo; | 
					
						
							| 
									
										
										
										
											2021-12-19 23:40:52 +08:00
										 |  |  | use BookStack\Entities\Repos\ChapterRepo; | 
					
						
							| 
									
										
										
										
											2021-12-19 20:56:27 +08:00
										 |  |  | use BookStack\Entities\Repos\PageRepo; | 
					
						
							| 
									
										
										
										
											2021-12-20 03:20:31 +08:00
										 |  |  | use BookStack\Uploads\Image; | 
					
						
							|  |  |  | use BookStack\Uploads\ImageService; | 
					
						
							|  |  |  | use Illuminate\Http\UploadedFile; | 
					
						
							| 
									
										
										
										
											2021-12-19 20:56:27 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | class Cloner | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2022-06-19 23:57:33 +08:00
										 |  |  |     protected PageRepo $pageRepo; | 
					
						
							|  |  |  |     protected ChapterRepo $chapterRepo; | 
					
						
							|  |  |  |     protected BookRepo $bookRepo; | 
					
						
							|  |  |  |     protected ImageService $imageService; | 
					
						
							| 
									
										
										
										
											2021-12-20 03:20:31 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     public function __construct(PageRepo $pageRepo, ChapterRepo $chapterRepo, BookRepo $bookRepo, ImageService $imageService) | 
					
						
							| 
									
										
										
										
											2021-12-19 20:56:27 +08:00
										 |  |  |     { | 
					
						
							|  |  |  |         $this->pageRepo = $pageRepo; | 
					
						
							| 
									
										
										
										
											2021-12-19 23:40:52 +08:00
										 |  |  |         $this->chapterRepo = $chapterRepo; | 
					
						
							| 
									
										
										
										
											2021-12-20 03:20:31 +08:00
										 |  |  |         $this->bookRepo = $bookRepo; | 
					
						
							|  |  |  |         $this->imageService = $imageService; | 
					
						
							| 
									
										
										
										
											2021-12-19 20:56:27 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Clone the given page into the given parent using the provided name. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function clonePage(Page $original, Entity $parent, string $newName): Page | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $copyPage = $this->pageRepo->getNewDraftPage($parent); | 
					
						
							| 
									
										
										
										
											2022-06-14 00:20:21 +08:00
										 |  |  |         $pageData = $this->entityToInputData($original); | 
					
						
							| 
									
										
										
										
											2021-12-19 20:56:27 +08:00
										 |  |  |         $pageData['name'] = $newName; | 
					
						
							| 
									
										
										
										
											2021-12-19 23:40:52 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return $this->pageRepo->publishDraft($copyPage, $pageData); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Clone the given page into the given parent using the provided name. | 
					
						
							|  |  |  |      * Clones all child pages. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function cloneChapter(Chapter $original, Book $parent, string $newName): Chapter | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2022-06-14 00:20:21 +08:00
										 |  |  |         $chapterDetails = $this->entityToInputData($original); | 
					
						
							| 
									
										
										
										
											2021-12-19 23:40:52 +08:00
										 |  |  |         $chapterDetails['name'] = $newName; | 
					
						
							| 
									
										
										
										
											2021-12-19 20:56:27 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-19 23:40:52 +08:00
										 |  |  |         $copyChapter = $this->chapterRepo->create($chapterDetails, $parent); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (userCan('page-create', $copyChapter)) { | 
					
						
							|  |  |  |             /** @var Page $page */ | 
					
						
							|  |  |  |             foreach ($original->getVisiblePages() as $page) { | 
					
						
							|  |  |  |                 $this->clonePage($page, $copyChapter, $page->name); | 
					
						
							| 
									
										
										
										
											2021-12-19 20:56:27 +08:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-19 23:40:52 +08:00
										 |  |  |         return $copyChapter; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-20 03:20:31 +08:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Clone the given book. | 
					
						
							|  |  |  |      * Clones all child chapters & pages. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function cloneBook(Book $original, string $newName): Book | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2022-06-14 00:20:21 +08:00
										 |  |  |         $bookDetails = $this->entityToInputData($original); | 
					
						
							| 
									
										
										
										
											2021-12-20 03:20:31 +08:00
										 |  |  |         $bookDetails['name'] = $newName; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-28 21:14:51 +08:00
										 |  |  |         // Clone book
 | 
					
						
							| 
									
										
										
										
											2021-12-20 03:20:31 +08:00
										 |  |  |         $copyBook = $this->bookRepo->create($bookDetails); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-28 21:14:51 +08:00
										 |  |  |         // Clone contents
 | 
					
						
							| 
									
										
										
										
											2021-12-20 03:20:31 +08:00
										 |  |  |         $directChildren = $original->getDirectChildren(); | 
					
						
							|  |  |  |         foreach ($directChildren as $child) { | 
					
						
							|  |  |  |             if ($child instanceof Chapter && userCan('chapter-create', $copyBook)) { | 
					
						
							|  |  |  |                 $this->cloneChapter($child, $copyBook, $child->name); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if ($child instanceof Page && !$child->draft && userCan('page-create', $copyBook)) { | 
					
						
							|  |  |  |                 $this->clonePage($child, $copyBook, $child->name); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-28 21:14:51 +08:00
										 |  |  |         // Clone bookshelf relationships
 | 
					
						
							|  |  |  |         /** @var Bookshelf $shelf */ | 
					
						
							|  |  |  |         foreach ($original->shelves as $shelf) { | 
					
						
							|  |  |  |             if (userCan('bookshelf-update', $shelf)) { | 
					
						
							|  |  |  |                 $shelf->appendBook($copyBook); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-14 00:20:21 +08:00
										 |  |  |         return $copyBook; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Convert an entity to a raw data array of input data. | 
					
						
							| 
									
										
										
										
											2022-06-20 01:14:53 +08:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2022-06-14 00:20:21 +08:00
										 |  |  |      * @return array<string, mixed> | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function entityToInputData(Entity $entity): array | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $inputData = $entity->getAttributes(); | 
					
						
							|  |  |  |         $inputData['tags'] = $this->entityTagsToInputArray($entity); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Add a cover to the data if existing on the original entity
 | 
					
						
							| 
									
										
										
										
											2022-10-24 19:12:48 +08:00
										 |  |  |         if ($entity instanceof HasCoverImage) { | 
					
						
							|  |  |  |             $cover = $entity->cover()->first(); | 
					
						
							|  |  |  |             if ($cover) { | 
					
						
							|  |  |  |                 $inputData['image'] = $this->imageToUploadedFile($cover); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-12-20 03:20:31 +08:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-14 00:20:21 +08:00
										 |  |  |         return $inputData; | 
					
						
							| 
									
										
										
										
											2021-12-20 03:20:31 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-14 22:55:44 +08:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Copy the permission settings from the source entity to the target entity. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function copyEntityPermissions(Entity $sourceEntity, Entity $targetEntity): void | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2022-10-08 20:52:59 +08:00
										 |  |  |         $permissions = $sourceEntity->permissions()->get(['role_id', 'view', 'create', 'update', 'delete'])->toArray(); | 
					
						
							| 
									
										
										
										
											2022-06-14 22:55:44 +08:00
										 |  |  |         $targetEntity->permissions()->delete(); | 
					
						
							|  |  |  |         $targetEntity->permissions()->createMany($permissions); | 
					
						
							|  |  |  |         $targetEntity->rebuildPermissions(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-20 03:20:31 +08:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Convert an image instance to an UploadedFile instance to mimic | 
					
						
							|  |  |  |      * a file being uploaded. | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2022-06-20 01:45:48 +08:00
										 |  |  |     protected function imageToUploadedFile(Image $image): ?UploadedFile | 
					
						
							| 
									
										
										
										
											2021-12-20 03:20:31 +08:00
										 |  |  |     { | 
					
						
							|  |  |  |         $imgData = $this->imageService->getImageData($image); | 
					
						
							| 
									
										
										
										
											2022-06-20 01:44:34 +08:00
										 |  |  |         $tmpImgFilePath = tempnam(sys_get_temp_dir(), 'bs_cover_clone_'); | 
					
						
							| 
									
										
										
										
											2021-12-20 03:20:31 +08:00
										 |  |  |         file_put_contents($tmpImgFilePath, $imgData); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return new UploadedFile($tmpImgFilePath, basename($image->path)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-19 23:40:52 +08:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Convert the tags on the given entity to the raw format | 
					
						
							|  |  |  |      * that's used for incoming request data. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     protected function entityTagsToInputArray(Entity $entity): array | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $tags = []; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /** @var Tag $tag */ | 
					
						
							|  |  |  |         foreach ($entity->tags as $tag) { | 
					
						
							|  |  |  |             $tags[] = ['name' => $tag->name, 'value' => $tag->value]; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return $tags; | 
					
						
							| 
									
										
										
										
											2021-12-19 20:56:27 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-12-21 01:40:27 +08:00
										 |  |  | } |