| 
									
										
										
										
											2022-08-21 04:09:07 +08:00
										 |  |  | <?php | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace BookStack\References; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 05:00:32 +08:00
										 |  |  | use BookStack\Entities\Models\Book; | 
					
						
							| 
									
										
										
										
											2022-08-21 04:09:07 +08:00
										 |  |  | use BookStack\Entities\Models\Entity; | 
					
						
							|  |  |  | use BookStack\Entities\Models\Page; | 
					
						
							|  |  |  | use BookStack\Entities\Repos\RevisionRepo; | 
					
						
							| 
									
										
										
										
											2023-11-14 23:46:32 +08:00
										 |  |  | use BookStack\Util\HtmlDocument; | 
					
						
							| 
									
										
										
										
											2022-08-21 04:09:07 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-22 01:05:19 +08:00
										 |  |  | class ReferenceUpdater | 
					
						
							| 
									
										
										
										
											2022-08-21 04:09:07 +08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-11-14 23:46:32 +08:00
										 |  |  |     public function __construct( | 
					
						
							|  |  |  |         protected ReferenceFetcher $referenceFetcher, | 
					
						
							|  |  |  |         protected RevisionRepo $revisionRepo | 
					
						
							|  |  |  |     ) { | 
					
						
							| 
									
										
										
										
											2022-08-21 04:09:07 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public function updateEntityPageReferences(Entity $entity, string $oldLink) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2022-08-31 05:00:32 +08:00
										 |  |  |         $references = $this->getReferencesToUpdate($entity); | 
					
						
							| 
									
										
										
										
											2022-08-21 04:09:07 +08:00
										 |  |  |         $newLink = $entity->getUrl(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /** @var Reference $reference */ | 
					
						
							|  |  |  |         foreach ($references as $reference) { | 
					
						
							|  |  |  |             /** @var Page $page */ | 
					
						
							|  |  |  |             $page = $reference->from; | 
					
						
							|  |  |  |             $this->updateReferencesWithinPage($page, $oldLink, $newLink); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-31 05:00:32 +08:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * @return Reference[] | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     protected function getReferencesToUpdate(Entity $entity): array | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         /** @var Reference[] $references */ | 
					
						
							|  |  |  |         $references = $this->referenceFetcher->getPageReferencesToEntity($entity)->values()->all(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if ($entity instanceof Book) { | 
					
						
							|  |  |  |             $pages = $entity->pages()->get(['id']); | 
					
						
							|  |  |  |             $chapters = $entity->chapters()->get(['id']); | 
					
						
							|  |  |  |             $children = $pages->concat($chapters); | 
					
						
							|  |  |  |             foreach ($children as $bookChild) { | 
					
						
							|  |  |  |                 $childRefs = $this->referenceFetcher->getPageReferencesToEntity($bookChild)->values()->all(); | 
					
						
							|  |  |  |                 array_push($references, ...$childRefs); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $deduped = []; | 
					
						
							|  |  |  |         foreach ($references as $reference) { | 
					
						
							|  |  |  |             $key = $reference->from_id . ':' . $reference->from_type; | 
					
						
							|  |  |  |             $deduped[$key] = $reference; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return array_values($deduped); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-21 04:09:07 +08:00
										 |  |  |     protected function updateReferencesWithinPage(Page $page, string $oldLink, string $newLink) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $page = (clone $page)->refresh(); | 
					
						
							| 
									
										
										
										
											2022-08-21 18:29:34 +08:00
										 |  |  |         $html = $this->updateLinksInHtml($page->html, $oldLink, $newLink); | 
					
						
							|  |  |  |         $markdown = $this->updateLinksInMarkdown($page->markdown, $oldLink, $newLink); | 
					
						
							| 
									
										
										
										
											2022-08-21 04:09:07 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         $page->html = $html; | 
					
						
							|  |  |  |         $page->markdown = $markdown; | 
					
						
							|  |  |  |         $page->revision_count++; | 
					
						
							|  |  |  |         $page->save(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-21 18:29:34 +08:00
										 |  |  |         $summary = trans('entities.pages_references_update_revision'); | 
					
						
							| 
									
										
										
										
											2022-08-21 04:09:07 +08:00
										 |  |  |         $this->revisionRepo->storeNewForPage($page, $summary); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-08-21 18:29:34 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     protected function updateLinksInMarkdown(string $markdown, string $oldLink, string $newLink): string | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (empty($markdown)) { | 
					
						
							|  |  |  |             return $markdown; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-22 01:05:19 +08:00
										 |  |  |         $commonLinkRegex = '/(\[.*?\]\()' . preg_quote($oldLink, '/') . '(.*?\))/i'; | 
					
						
							| 
									
										
										
										
											2022-08-21 18:29:34 +08:00
										 |  |  |         $markdown = preg_replace($commonLinkRegex, '$1' . $newLink . '$2', $markdown); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-22 01:05:19 +08:00
										 |  |  |         $referenceLinkRegex = '/(\[.*?\]:\s?)' . preg_quote($oldLink, '/') . '(.*?)($|\s)/i'; | 
					
						
							| 
									
										
										
										
											2022-08-21 18:29:34 +08:00
										 |  |  |         $markdown = preg_replace($referenceLinkRegex, '$1' . $newLink . '$2$3', $markdown); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return $markdown; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     protected function updateLinksInHtml(string $html, string $oldLink, string $newLink): string | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (empty($html)) { | 
					
						
							|  |  |  |             return $html; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-14 23:46:32 +08:00
										 |  |  |         $doc = new HtmlDocument($html); | 
					
						
							|  |  |  |         $anchors = $doc->queryXPath('//a[@href]'); | 
					
						
							| 
									
										
										
										
											2022-08-21 18:29:34 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         /** @var \DOMElement $anchor */ | 
					
						
							|  |  |  |         foreach ($anchors as $anchor) { | 
					
						
							|  |  |  |             $link = $anchor->getAttribute('href'); | 
					
						
							|  |  |  |             $updated = str_ireplace($oldLink, $newLink, $link); | 
					
						
							|  |  |  |             $anchor->setAttribute('href', $updated); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-14 23:46:32 +08:00
										 |  |  |         return $doc->getBodyInnerHtml(); | 
					
						
							| 
									
										
										
										
											2022-08-21 18:29:34 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-08-30 00:46:41 +08:00
										 |  |  | } |