1c3437056SNickeau<?php 2c3437056SNickeau 3c3437056SNickeau 4c3437056SNickeaunamespace ComboStrap; 5c3437056SNickeau 6c3437056SNickeauuse ModificationDate; 7c3437056SNickeau 8c3437056SNickeau/** 9c3437056SNickeau * The class that manage the replication 10c3437056SNickeau * Class Replicate 11c3437056SNickeau * @package ComboStrap 12c3437056SNickeau * 13c3437056SNickeau * The database can also be seen as a {@link MetadataStore} 14c3437056SNickeau * and an {@link Index} 15c3437056SNickeau */ 16c3437056SNickeauclass DatabasePageRow 17c3437056SNickeau{ 18c3437056SNickeau 19c3437056SNickeau 20c3437056SNickeau /** 21c3437056SNickeau * The list of attributes that are set 22c3437056SNickeau * at build time 23c3437056SNickeau * used in the build functions such as {@link DatabasePageRow::getDatabaseRowFromPage()} 24c3437056SNickeau * to build the sql 25c3437056SNickeau */ 26c3437056SNickeau private const PAGE_BUILD_ATTRIBUTES = 27c3437056SNickeau [ 28c3437056SNickeau self::ROWID, 29c3437056SNickeau DokuwikiId::DOKUWIKI_ID_ATTRIBUTE, 30c3437056SNickeau self::ANALYTICS_ATTRIBUTE, 31c3437056SNickeau PageDescription::PROPERTY_NAME, 32c3437056SNickeau Canonical::PROPERTY_NAME, 33c3437056SNickeau ResourceName::PROPERTY_NAME, 34c3437056SNickeau PageTitle::TITLE, 35c3437056SNickeau PageH1::PROPERTY_NAME, 36c3437056SNickeau PagePublicationDate::PROPERTY_NAME, 37c3437056SNickeau ModificationDate::PROPERTY_NAME, 38c3437056SNickeau PageCreationDate::PROPERTY_NAME, 39c3437056SNickeau PagePath::PROPERTY_NAME, 40c3437056SNickeau StartDate::PROPERTY_NAME, 41c3437056SNickeau EndDate::PROPERTY_NAME, 42c3437056SNickeau Region::PROPERTY_NAME, 43c3437056SNickeau Lang::PROPERTY_NAME, 44c3437056SNickeau PageType::PROPERTY_NAME, 45c3437056SNickeau PageId::PROPERTY_NAME, 46c3437056SNickeau PageId::PAGE_ID_ABBR_ATTRIBUTE, 47c3437056SNickeau \ReplicationDate::PROPERTY_NAME, 48c3437056SNickeau BacklinkCount::PROPERTY_NAME 49c3437056SNickeau ]; 50c3437056SNickeau const ANALYTICS_ATTRIBUTE = "analytics"; 51c3437056SNickeau 52c3437056SNickeau /** 53c3437056SNickeau * For whatever reason, the row id is lowercase 54c3437056SNickeau */ 55c3437056SNickeau const ROWID = "rowid"; 56c3437056SNickeau 57c3437056SNickeau const CANONICAL = MetadataDbStore::CANONICAL; 58c3437056SNickeau 59c3437056SNickeau /** 60c3437056SNickeau * @var Page 61c3437056SNickeau */ 62c3437056SNickeau private $page; 63c3437056SNickeau /** 64c3437056SNickeau * @var Sqlite|null 65c3437056SNickeau */ 66c3437056SNickeau private $sqlite; 67c3437056SNickeau 68c3437056SNickeau /** 69c3437056SNickeau * @var array 70c3437056SNickeau */ 71c3437056SNickeau private $row; 72c3437056SNickeau 73c3437056SNickeau /** 74c3437056SNickeau * Replicate constructor. 75c3437056SNickeau */ 76c3437056SNickeau public function __construct() 77c3437056SNickeau { 78c3437056SNickeau 79c3437056SNickeau /** 80c3437056SNickeau * Persist on the DB 81c3437056SNickeau */ 82c3437056SNickeau $this->sqlite = Sqlite::createOrGetSqlite(); 83c3437056SNickeau 84c3437056SNickeau 85c3437056SNickeau } 86c3437056SNickeau 87c3437056SNickeau /** 88c3437056SNickeau * Delete the cache, 89c3437056SNickeau * Process the analytics 90c3437056SNickeau * Save it in the Db 91c3437056SNickeau * Delete from the page to refresh if any 92c3437056SNickeau * 93c3437056SNickeau * If you want the analytics: 94c3437056SNickeau * * from the cache use {@link self::getAnalyticsFromFs()} 95c3437056SNickeau * * from the db use {@link self::getAnalyticsFromDb()} 96c3437056SNickeau * 97c3437056SNickeau * 98c3437056SNickeau * @throws ExceptionCombo 99c3437056SNickeau */ 100c3437056SNickeau public function replicate(): DatabasePageRow 101c3437056SNickeau { 102c3437056SNickeau if ($this->sqlite === null) { 103c3437056SNickeau throw new ExceptionCombo("Sqlite is mandatory for database replication"); 104c3437056SNickeau } 105c3437056SNickeau 106c3437056SNickeau if (!$this->page->exists()) { 107c3437056SNickeau throw new ExceptionCombo("You can't replicate the non-existing page ($this->page) on the file system"); 108c3437056SNickeau } 109c3437056SNickeau 110c3437056SNickeau 111c3437056SNickeau /** 112c3437056SNickeau * Page Replication should appears 113c3437056SNickeau */ 114c3437056SNickeau $this->replicatePage(); 115c3437056SNickeau 116c3437056SNickeau /** 117c3437056SNickeau * @var Metadata $tabularMetadataToSync 118c3437056SNickeau */ 119c3437056SNickeau $tabularMetadataToSync = [ 120c3437056SNickeau (new References()), 121c3437056SNickeau (new Aliases()) 122c3437056SNickeau ]; 123c3437056SNickeau $fsStore = MetadataDokuWikiStore::getOrCreateFromResource($this->page); 124c3437056SNickeau $dbStore = MetadataDbStore::getOrCreateFromResource($this->page); 125c3437056SNickeau foreach ($tabularMetadataToSync as $tabular) { 126c3437056SNickeau $tabular 127c3437056SNickeau ->setResource($this->page) 128c3437056SNickeau ->setReadStore($fsStore) 129c3437056SNickeau ->buildFromReadStore() 130c3437056SNickeau ->setWriteStore($dbStore) 131c3437056SNickeau ->persist(); 132c3437056SNickeau } 133c3437056SNickeau 134c3437056SNickeau /** 135c3437056SNickeau * Analytics (derived) 136c3437056SNickeau * Should appear at the end of the replication because it is based 137c3437056SNickeau * on the previous replication (ie backlink count) 138c3437056SNickeau */ 139c3437056SNickeau $this->replicateAnalytics(); 140c3437056SNickeau 141c3437056SNickeau 142c3437056SNickeau return $this; 143c3437056SNickeau 144c3437056SNickeau } 145c3437056SNickeau 146c3437056SNickeau /** 147c3437056SNickeau * @throws ExceptionCombo 148c3437056SNickeau */ 149c3437056SNickeau public function replicateAndRebuild() 150c3437056SNickeau { 151c3437056SNickeau $this->replicate(); 152c3437056SNickeau $this->rebuild(); 153c3437056SNickeau return $this; 154c3437056SNickeau } 155c3437056SNickeau 156c3437056SNickeau private function addPageIdMeta(array &$metaRecord) 157c3437056SNickeau { 158c3437056SNickeau $metaRecord[PageId::PROPERTY_NAME] = $this->page->getPageId(); 159c3437056SNickeau $metaRecord[PageId::PAGE_ID_ABBR_ATTRIBUTE] = $this->page->getPageIdAbbr(); 160c3437056SNickeau } 161c3437056SNickeau 162c3437056SNickeau public static function createFromPageId(string $pageId): DatabasePageRow 163c3437056SNickeau { 164c3437056SNickeau $databasePage = new DatabasePageRow(); 165c3437056SNickeau $row = $databasePage->getDatabaseRowFromPageId($pageId); 166c3437056SNickeau if ($row != null) { 167c3437056SNickeau $databasePage->setRow($row); 168c3437056SNickeau } 169c3437056SNickeau return $databasePage; 170c3437056SNickeau } 171c3437056SNickeau 172c3437056SNickeau public static function createFromPageObject(Page $page): DatabasePageRow 173c3437056SNickeau { 174c3437056SNickeau 175c3437056SNickeau $databasePage = new DatabasePageRow(); 176c3437056SNickeau $row = $databasePage->getDatabaseRowFromPage($page); 177c3437056SNickeau if ($row !== null) { 178c3437056SNickeau $databasePage->setRow($row); 179c3437056SNickeau } 180c3437056SNickeau return $databasePage; 181c3437056SNickeau } 182c3437056SNickeau 183c3437056SNickeau public static function createFromPageIdAbbr(string $pageIdAbbr): DatabasePageRow 184c3437056SNickeau { 185c3437056SNickeau $databasePage = new DatabasePageRow(); 186c3437056SNickeau $row = $databasePage->getDatabaseRowFromAttribute(PageId::PAGE_ID_ABBR_ATTRIBUTE, $pageIdAbbr); 187c3437056SNickeau if ($row != null) { 188c3437056SNickeau $databasePage->setRow($row); 189c3437056SNickeau } 190c3437056SNickeau return $databasePage; 191c3437056SNickeau 192c3437056SNickeau } 193c3437056SNickeau 194c3437056SNickeau /** 195c3437056SNickeau * @param $canonical 196c3437056SNickeau * @return DatabasePageRow 197c3437056SNickeau */ 198c3437056SNickeau public static function createFromCanonical($canonical): DatabasePageRow 199c3437056SNickeau { 200c3437056SNickeau 201c3437056SNickeau DokuPath::addRootSeparatorIfNotPresent($canonical); 202c3437056SNickeau $databasePage = new DatabasePageRow(); 203c3437056SNickeau $row = $databasePage->getDatabaseRowFromAttribute(Canonical::PROPERTY_NAME, $canonical); 204c3437056SNickeau if ($row != null) { 205c3437056SNickeau $databasePage->setRow($row); 206c3437056SNickeau } 207c3437056SNickeau return $databasePage; 208c3437056SNickeau 209c3437056SNickeau 210c3437056SNickeau } 211c3437056SNickeau 212c3437056SNickeau public static function createFromAlias($alias): DatabasePageRow 213c3437056SNickeau { 214c3437056SNickeau 215c3437056SNickeau DokuPath::addRootSeparatorIfNotPresent($alias); 216c3437056SNickeau $databasePage = new DatabasePageRow(); 217c3437056SNickeau $row = $databasePage->getDatabaseRowFromAlias($alias); 218c3437056SNickeau if ($row != null) { 219c3437056SNickeau $databasePage->setRow($row); 220*58317768Sgerardnico $page = $databasePage->getPage(); 221*58317768Sgerardnico if ($page !== null) { 222*58317768Sgerardnico // page may be null in production 223*58317768Sgerardnico // PHP Fatal error: Uncaught Error: Call to a member function setBuildAliasPath() on null in 224*58317768Sgerardnico // /opt/www/bytle/farmer.bytle.net/lib/plugins/combo/ComboStrap/DatabasePageRow.php:220 225*58317768Sgerardnico $page->setBuildAliasPath($alias); 226*58317768Sgerardnico } 227c3437056SNickeau } 228c3437056SNickeau return $databasePage; 229c3437056SNickeau 230c3437056SNickeau } 231c3437056SNickeau 232c3437056SNickeau public static function createFromDokuWikiId($id): DatabasePageRow 233c3437056SNickeau { 234c3437056SNickeau $databasePage = new DatabasePageRow(); 235c3437056SNickeau $row = $databasePage->getDatabaseRowFromDokuWikiId($id); 236c3437056SNickeau if ($row !== null) { 237c3437056SNickeau $databasePage->setRow($row); 238c3437056SNickeau } 239c3437056SNickeau return $databasePage; 240c3437056SNickeau } 241c3437056SNickeau 242c3437056SNickeau public function getPageId() 243c3437056SNickeau { 244c3437056SNickeau return $this->getFromRow(PageId::PROPERTY_NAME); 245c3437056SNickeau } 246c3437056SNickeau 247c3437056SNickeau 248c3437056SNickeau public 249c3437056SNickeau function shouldReplicate(): bool 250c3437056SNickeau { 251c3437056SNickeau 252c3437056SNickeau /** 253c3437056SNickeau * When the file does not exist 254c3437056SNickeau */ 255c3437056SNickeau $exist = FileSystems::exists($this->page->getAnalyticsDocument()->getCachePath()); 256c3437056SNickeau if (!$exist) { 257c3437056SNickeau return true; 258c3437056SNickeau } 259c3437056SNickeau 260c3437056SNickeau /** 261c3437056SNickeau * When the file exists 262c3437056SNickeau */ 263c3437056SNickeau $modifiedTime = FileSystems::getModifiedTime($this->page->getAnalyticsDocument()->getCachePath()); 264c3437056SNickeau $dateReplication = $this->getReplicationDate(); 265c3437056SNickeau if ($modifiedTime > $dateReplication) { 266c3437056SNickeau return true; 267c3437056SNickeau } 268c3437056SNickeau 269c3437056SNickeau /** 270c3437056SNickeau * When the database version file is higher 271c3437056SNickeau */ 272c3437056SNickeau $version = LocalPath::createFromPath(__DIR__ . "/../db/latest.version"); 273c3437056SNickeau $versionModifiedTime = FileSystems::getModifiedTime($version); 274c3437056SNickeau if ($versionModifiedTime > $dateReplication) { 275c3437056SNickeau return true; 276c3437056SNickeau } 277c3437056SNickeau 278c3437056SNickeau /** 279c3437056SNickeau * When the class date time is higher 280c3437056SNickeau */ 281c3437056SNickeau $code = LocalPath::createFromPath(__DIR__ . "/DatabasePageRow.php"); 282c3437056SNickeau $codeModified = FileSystems::getModifiedTime($code); 283c3437056SNickeau if ($codeModified > $dateReplication) { 284c3437056SNickeau return true; 285c3437056SNickeau } 286c3437056SNickeau 287c3437056SNickeau return false; 288c3437056SNickeau 289c3437056SNickeau } 290c3437056SNickeau 291c3437056SNickeau public 292c3437056SNickeau function delete() 293c3437056SNickeau { 294c3437056SNickeau 295c3437056SNickeau $request = Sqlite::createOrGetSqlite() 296c3437056SNickeau ->createRequest() 297c3437056SNickeau ->setQueryParametrized('delete from pages where id = ?', [$this->page->getDokuwikiId()]); 298c3437056SNickeau try { 299c3437056SNickeau $request->execute(); 300c3437056SNickeau } catch (ExceptionCombo $e) { 301c3437056SNickeau LogUtility::msg("Something went wrong when deleting the page ({$this->page}) from the database"); 302c3437056SNickeau } finally { 303c3437056SNickeau $request->close(); 304c3437056SNickeau } 305c3437056SNickeau $this->buildInitObjectFields(); 306c3437056SNickeau 307c3437056SNickeau } 308c3437056SNickeau 309c3437056SNickeau /** 310c3437056SNickeau * @return Json|null the analytics array or null if not in db 311c3437056SNickeau */ 312c3437056SNickeau public 313c3437056SNickeau function getAnalyticsData(): ?Json 314c3437056SNickeau { 315c3437056SNickeau 316c3437056SNickeau $jsonString = $this->getFromRow(self::ANALYTICS_ATTRIBUTE); 317c3437056SNickeau if ($jsonString === null) { 318c3437056SNickeau return null; 319c3437056SNickeau } 320c3437056SNickeau try { 321c3437056SNickeau return Json::createFromString($jsonString); 322c3437056SNickeau } catch (ExceptionCombo $e) { 323c3437056SNickeau LogUtility::msg("Error while building back the analytics JSON object. {$e->getMessage()}"); 324c3437056SNickeau return null; 325c3437056SNickeau } 326c3437056SNickeau 327c3437056SNickeau } 328c3437056SNickeau 329c3437056SNickeau /** 330c3437056SNickeau * Return the database row 331c3437056SNickeau * 332c3437056SNickeau * 333c3437056SNickeau */ 334c3437056SNickeau private 335c3437056SNickeau function getDatabaseRowFromPage(Page $page): ?array 336c3437056SNickeau { 337c3437056SNickeau 338c3437056SNickeau $this->setPage($page); 339c3437056SNickeau 340c3437056SNickeau if ($this->sqlite === null) return null; 341c3437056SNickeau 342c3437056SNickeau // Do we have a page attached to this page id 343c3437056SNickeau $pageId = $page->getPageId(); 344c3437056SNickeau if ($pageId !== null) { 345c3437056SNickeau $row = $this->getDatabaseRowFromPageId($pageId); 346c3437056SNickeau if ($row !== null) { 347c3437056SNickeau return $row; 348c3437056SNickeau } 349c3437056SNickeau } 350c3437056SNickeau 351c3437056SNickeau // Do we have a page attached to the canonical 352c3437056SNickeau $canonical = $page->getCanonical(); 353c3437056SNickeau if ($canonical != null) { 354c3437056SNickeau $row = $this->getDatabaseRowFromCanonical($canonical); 355c3437056SNickeau if ($row !== null) { 356c3437056SNickeau return $row; 357c3437056SNickeau } 358c3437056SNickeau } 359c3437056SNickeau 360c3437056SNickeau // Do we have a page attached to the path 361c3437056SNickeau $path = $page->getPath(); 362c3437056SNickeau $row = $this->getDatabaseRowFromPath($path); 363c3437056SNickeau if ($row !== null) { // the path may no yet updated in the db 364c3437056SNickeau return $row; 365c3437056SNickeau } 366c3437056SNickeau 367c3437056SNickeau /** 368c3437056SNickeau * Do we have a page attached to this ID 369c3437056SNickeau */ 370c3437056SNickeau $id = $page->getPath()->getDokuwikiId(); 371c3437056SNickeau return $this->getDatabaseRowFromDokuWikiId($id); 372c3437056SNickeau 373c3437056SNickeau 374c3437056SNickeau } 375c3437056SNickeau 376c3437056SNickeau 377c3437056SNickeau public function getReplicationDate(): ?\DateTime 378c3437056SNickeau { 379c3437056SNickeau $dateString = $this->getFromRow(\ReplicationDate::getPersistentName()); 380c3437056SNickeau if ($dateString === null) { 381c3437056SNickeau return null; 382c3437056SNickeau } 383c3437056SNickeau try { 384c3437056SNickeau return Iso8601Date::createFromString($dateString)->getDateTime(); 385c3437056SNickeau } catch (ExceptionCombo $e) { 386c3437056SNickeau LogUtility::msg("Error while reading the replication date in the database. {$e->getMessage()}"); 387c3437056SNickeau return null; 388c3437056SNickeau } 389c3437056SNickeau 390c3437056SNickeau } 391c3437056SNickeau 392c3437056SNickeau /** 393c3437056SNickeau * @return bool 394c3437056SNickeau * @throws ExceptionCombo 395c3437056SNickeau */ 396c3437056SNickeau public function replicatePage(): bool 397c3437056SNickeau { 398c3437056SNickeau 399c3437056SNickeau if (!$this->page->exists()) { 400c3437056SNickeau throw new ExceptionCombo("You can't replicate the page ($this->page) because it does not exists."); 401c3437056SNickeau } 402c3437056SNickeau 403c3437056SNickeau /** 404c3437056SNickeau * Replication Date 405c3437056SNickeau */ 406c3437056SNickeau $replicationDate = \ReplicationDate::createFromPage($this->page) 407c3437056SNickeau ->setWriteStore(MetadataDbStore::class) 408c3437056SNickeau ->setValue(new \DateTime()); 409c3437056SNickeau 410c3437056SNickeau /** 411c3437056SNickeau * Convenient variable 412c3437056SNickeau */ 413c3437056SNickeau $page = $this->page; 414c3437056SNickeau 415c3437056SNickeau 416c3437056SNickeau /** 417c3437056SNickeau * Same data as {@link Page::getMetadataForRendering()} 418c3437056SNickeau */ 419c3437056SNickeau $record = $this->getMetaRecord(); 420c3437056SNickeau $record['IS_HOME'] = ($page->isHomePage() === true ? 1 : 0); 421c3437056SNickeau $record[$replicationDate::getPersistentName()] = $replicationDate->toStoreValue(); 422c3437056SNickeau 423c3437056SNickeau return $this->upsertAttributes($record); 424c3437056SNickeau 425c3437056SNickeau } 426c3437056SNickeau 427c3437056SNickeau 428c3437056SNickeau /** 429c3437056SNickeau * @return bool when an update as occurred 430c3437056SNickeau * 431c3437056SNickeau * Attribute that are scalar / modifiable in the database 432c3437056SNickeau * (not aliases or json data for instance) 433c3437056SNickeau */ 434c3437056SNickeau public function replicateMetaAttributes(): bool 435c3437056SNickeau { 436c3437056SNickeau 437c3437056SNickeau return $this->upsertAttributes($this->getMetaRecord()); 438c3437056SNickeau 439c3437056SNickeau } 440c3437056SNickeau 441c3437056SNickeau public function upsertAttributes(array $attributes): bool 442c3437056SNickeau { 443c3437056SNickeau 444c3437056SNickeau if ($this->sqlite === null) { 445c3437056SNickeau return false; 446c3437056SNickeau } 447c3437056SNickeau 448c3437056SNickeau if (empty($attributes)) { 449c3437056SNickeau LogUtility::msg("The page database attribute passed should not be empty"); 450c3437056SNickeau return false; 451c3437056SNickeau } 452c3437056SNickeau 453c3437056SNickeau $values = []; 454c3437056SNickeau $columnClauses = []; 455c3437056SNickeau foreach ($attributes as $key => $value) { 456c3437056SNickeau if (is_array($value)) { 457c3437056SNickeau throw new ExceptionComboRuntime("The attribute ($key) has value that is an array (" . implode(", ", $value) . ")"); 458c3437056SNickeau } 459c3437056SNickeau $columnClauses[] = "$key = ?"; 460c3437056SNickeau $values[$key] = $value; 461c3437056SNickeau } 462c3437056SNickeau 463c3437056SNickeau /** 464c3437056SNickeau * Primary key has moved during the time 465c3437056SNickeau * It should be the UUID but not for older version 466c3437056SNickeau * 467c3437056SNickeau * If the primary key is null, no record was found 468c3437056SNickeau */ 469c3437056SNickeau $rowId = $this->getRowId(); 470c3437056SNickeau if ($rowId !== null) { 471c3437056SNickeau /** 472c3437056SNickeau * We just add the primary key 473c3437056SNickeau * otherwise as this is a associative 474c3437056SNickeau * array, we will miss a value for the update statement 475c3437056SNickeau */ 476c3437056SNickeau $values[] = $rowId; 477c3437056SNickeau 478c3437056SNickeau $updateStatement = "update PAGES SET " . implode(", ", $columnClauses) . " where ROWID = ?"; 479c3437056SNickeau $request = $this->sqlite 480c3437056SNickeau ->createRequest() 481c3437056SNickeau ->setQueryParametrized($updateStatement, $values); 482c3437056SNickeau $countChanges = 0; 483c3437056SNickeau try { 484c3437056SNickeau $countChanges = $request 485c3437056SNickeau ->execute() 486c3437056SNickeau ->getChangeCount(); 487c3437056SNickeau } catch (ExceptionCombo $e) { 488c3437056SNickeau LogUtility::msg("There was a problem during the page attribute updates. : {$e->getMessage()}"); 489c3437056SNickeau return false; 490c3437056SNickeau } finally { 491c3437056SNickeau $request->close(); 492c3437056SNickeau } 493c3437056SNickeau if ($countChanges !== 1) { 494c3437056SNickeau LogUtility::msg("The database replication has not updated exactly 1 record but ($countChanges) record", LogUtility::LVL_MSG_ERROR, \action_plugin_combo_fulldatabasereplication::CANONICAL); 495c3437056SNickeau } 496c3437056SNickeau 497c3437056SNickeau } else { 498c3437056SNickeau 499c3437056SNickeau /** 500c3437056SNickeau * Creation 501c3437056SNickeau */ 502c3437056SNickeau if ($this->page === null) { 503c3437056SNickeau LogUtility::msg("The page should be defined to create a page row"); 504c3437056SNickeau return false; 505c3437056SNickeau } 506c3437056SNickeau $values[DokuwikiId::DOKUWIKI_ID_ATTRIBUTE] = $this->page->getPath()->getDokuwikiId(); 507c3437056SNickeau $values[PagePath::PROPERTY_NAME] = $this->page->getPath()->toAbsolutePath()->toString(); 508c3437056SNickeau /** 509c3437056SNickeau * Default implements the auto-canonical feature 510c3437056SNickeau */ 511c3437056SNickeau $values[Canonical::PROPERTY_NAME] = $this->page->getCanonicalOrDefault(); 512c3437056SNickeau 513c3437056SNickeau /** 514c3437056SNickeau * Analytics 515c3437056SNickeau */ 516c3437056SNickeau if (!isset($values[self::ANALYTICS_ATTRIBUTE])) { 517c3437056SNickeau // otherwise we get an empty string 518c3437056SNickeau // and a json function will not work 519c3437056SNickeau $values[self::ANALYTICS_ATTRIBUTE] = Json::createEmpty()->toPrettyJsonString(); 520c3437056SNickeau } 521c3437056SNickeau 522c3437056SNickeau /** 523c3437056SNickeau * Page Id / Abbr are mandatory for url redirection 524c3437056SNickeau */ 525c3437056SNickeau $this->addPageIdAttributeIfNeeded($values); 526c3437056SNickeau 527c3437056SNickeau $request = $this->sqlite 528c3437056SNickeau ->createRequest() 529c3437056SNickeau ->setTableRow('PAGES', $values); 530c3437056SNickeau try { 531c3437056SNickeau /** 532c3437056SNickeau * rowid is used in {@link DatabasePageRow::exists()} 533c3437056SNickeau * to check if the page exists in the database 534c3437056SNickeau * We update it 535c3437056SNickeau */ 536c3437056SNickeau $this->row[self::ROWID] = $request 537c3437056SNickeau ->execute() 538c3437056SNickeau ->getInsertId(); 539c3437056SNickeau $this->row = array_merge($values, $this->row); 540c3437056SNickeau } catch (ExceptionCombo $e) { 541c3437056SNickeau LogUtility::msg("There was a problem during the updateAttributes insert. : {$e->getMessage()}"); 542c3437056SNickeau return false; 543c3437056SNickeau } finally { 544c3437056SNickeau $request->close(); 545c3437056SNickeau } 546c3437056SNickeau 547c3437056SNickeau } 548c3437056SNickeau return true; 549c3437056SNickeau 550c3437056SNickeau } 551c3437056SNickeau 552c3437056SNickeau public 553c3437056SNickeau function getDescription() 554c3437056SNickeau { 555c3437056SNickeau return $this->getFromRow(PageDescription::DESCRIPTION_PROPERTY); 556c3437056SNickeau } 557c3437056SNickeau 558c3437056SNickeau 559c3437056SNickeau public 560c3437056SNickeau function getPageName() 561c3437056SNickeau { 562c3437056SNickeau return $this->getFromRow(ResourceName::PROPERTY_NAME); 563c3437056SNickeau } 564c3437056SNickeau 565c3437056SNickeau public 566c3437056SNickeau function exists(): bool 567c3437056SNickeau { 568c3437056SNickeau return $this->getFromRow(self::ROWID) !== null; 569c3437056SNickeau } 570c3437056SNickeau 571c3437056SNickeau /** 572c3437056SNickeau * Called when a page is moved 573c3437056SNickeau * @param $targetId 574c3437056SNickeau */ 575c3437056SNickeau public 576c3437056SNickeau function updatePathAndDokuwikiId($targetId) 577c3437056SNickeau { 578c3437056SNickeau if (!$this->exists()) { 579c3437056SNickeau LogUtility::msg("The `database` page ($this) does not exist and cannot be moved to ($targetId)", LogUtility::LVL_MSG_ERROR); 580c3437056SNickeau } 581c3437056SNickeau 582c3437056SNickeau $path = $targetId; 583c3437056SNickeau DokuPath::addRootSeparatorIfNotPresent($path); 584c3437056SNickeau $attributes = [ 585c3437056SNickeau DokuwikiId::DOKUWIKI_ID_ATTRIBUTE => $targetId, 586c3437056SNickeau PagePath::PROPERTY_NAME => $path 587c3437056SNickeau ]; 588c3437056SNickeau 589c3437056SNickeau $this->upsertAttributes($attributes); 590c3437056SNickeau 591c3437056SNickeau } 592c3437056SNickeau 593c3437056SNickeau public 594c3437056SNickeau function __toString() 595c3437056SNickeau { 596c3437056SNickeau return $this->page->__toString(); 597c3437056SNickeau } 598c3437056SNickeau 599c3437056SNickeau 600c3437056SNickeau /** 601c3437056SNickeau * Redirect are now added during a move 602c3437056SNickeau * Not when a duplicate is found. 603c3437056SNickeau * With the advent of the page id, it should never occurs anymore 604c3437056SNickeau * @param Page $page 605c3437056SNickeau * @deprecated 2012-10-28 606c3437056SNickeau */ 607c3437056SNickeau private 608c3437056SNickeau function deleteIfExistsAndAddRedirectAlias(Page $page): void 609c3437056SNickeau { 610c3437056SNickeau 611c3437056SNickeau if ($this->page != null) { 612c3437056SNickeau $page->getDatabasePage()->deleteIfExist(); 613c3437056SNickeau $this->addRedirectAliasWhileBuildingRow($page); 614c3437056SNickeau } 615c3437056SNickeau 616c3437056SNickeau } 617c3437056SNickeau 618c3437056SNickeau public 619c3437056SNickeau function getCanonical() 620c3437056SNickeau { 621c3437056SNickeau return $this->getFromRow(Canonical::PROPERTY_NAME); 622c3437056SNickeau } 623c3437056SNickeau 624c3437056SNickeau /** 625c3437056SNickeau * Set the field to their values 626c3437056SNickeau * @param $row 627c3437056SNickeau */ 628c3437056SNickeau public 629c3437056SNickeau function setRow($row) 630c3437056SNickeau { 631c3437056SNickeau if ($row === null) { 632c3437056SNickeau LogUtility::msg("A row should not be null"); 633c3437056SNickeau return; 634c3437056SNickeau } 635c3437056SNickeau if (!is_array($row)) { 636c3437056SNickeau LogUtility::msg("A row should be an array"); 637c3437056SNickeau return; 638c3437056SNickeau } 639c3437056SNickeau 640c3437056SNickeau /** 641c3437056SNickeau * All get function lookup the row 642c3437056SNickeau */ 643c3437056SNickeau $this->row = $row; 644c3437056SNickeau 645c3437056SNickeau 646c3437056SNickeau } 647c3437056SNickeau 648c3437056SNickeau private 649c3437056SNickeau function buildInitObjectFields() 650c3437056SNickeau { 651c3437056SNickeau $this->row = null; 652c3437056SNickeau 653c3437056SNickeau } 654c3437056SNickeau 655c3437056SNickeau public 656c3437056SNickeau function rebuild(): DatabasePageRow 657c3437056SNickeau { 658c3437056SNickeau 659c3437056SNickeau if ($this->page != null) { 660c3437056SNickeau $this->page->rebuild(); 661c3437056SNickeau $row = $this->getDatabaseRowFromPage($this->page); 662c3437056SNickeau if ($row !== null) { 663c3437056SNickeau $this->setRow($row); 664c3437056SNickeau } 665c3437056SNickeau } 666c3437056SNickeau return $this; 667c3437056SNickeau 668c3437056SNickeau } 669c3437056SNickeau 670c3437056SNickeau /** 671c3437056SNickeau * @return array - an array of the fix page metadata (ie not derived) 672c3437056SNickeau * Therefore quick to insert/update 673c3437056SNickeau * 674c3437056SNickeau */ 675c3437056SNickeau private 676c3437056SNickeau function getMetaRecord(): array 677c3437056SNickeau { 678c3437056SNickeau $sourceStore = MetadataDokuWikiStore::getOrCreateFromResource($this->page); 679c3437056SNickeau $targetStore = MetadataDbStore::getOrCreateFromResource($this->page); 680c3437056SNickeau 681c3437056SNickeau $record = array( 682c3437056SNickeau Canonical::PROPERTY_NAME, 683c3437056SNickeau PagePath::PROPERTY_NAME, 684c3437056SNickeau ResourceName::PROPERTY_NAME, 685c3437056SNickeau PageTitle::TITLE, 686c3437056SNickeau PageH1::PROPERTY_NAME, 687c3437056SNickeau PageDescription::PROPERTY_NAME, 688c3437056SNickeau PageCreationDate::PROPERTY_NAME, 689c3437056SNickeau ModificationDate::PROPERTY_NAME, 690c3437056SNickeau PagePublicationDate::PROPERTY_NAME, 691c3437056SNickeau StartDate::PROPERTY_NAME, 692c3437056SNickeau EndDate::PROPERTY_NAME, 693c3437056SNickeau Region::PROPERTY_NAME, 694c3437056SNickeau Lang::PROPERTY_NAME, 695c3437056SNickeau PageType::PROPERTY_NAME, 696c3437056SNickeau DokuwikiId::DOKUWIKI_ID_ATTRIBUTE, 697c3437056SNickeau ); 698c3437056SNickeau $metaRecord = []; 699c3437056SNickeau foreach ($record as $name) { 700c3437056SNickeau $metadata = Metadata::getForName($name); 701c3437056SNickeau if ($metadata === null) { 702c3437056SNickeau throw new ExceptionComboRuntime("The metadata ($name) is unknown"); 703c3437056SNickeau } 704c3437056SNickeau $metaRecord[$name] = $metadata 705c3437056SNickeau ->setResource($this->page) 706c3437056SNickeau ->setReadStore($sourceStore) 707c3437056SNickeau ->buildFromReadStore() 708c3437056SNickeau ->setWriteStore($targetStore) 709c3437056SNickeau ->toStoreValueOrDefault(); // used by the template, the value is or default 710c3437056SNickeau } 711c3437056SNickeau 712c3437056SNickeau if ($this->page->getPageId() !== null) { 713c3437056SNickeau $this->addPageIdMeta($metaRecord); 714c3437056SNickeau } 715c3437056SNickeau return $metaRecord; 716c3437056SNickeau } 717c3437056SNickeau 718c3437056SNickeau public 719c3437056SNickeau function deleteIfExist(): DatabasePageRow 720c3437056SNickeau { 721c3437056SNickeau if ($this->exists()) { 722c3437056SNickeau $this->delete(); 723c3437056SNickeau } 724c3437056SNickeau return $this; 725c3437056SNickeau } 726c3437056SNickeau 727c3437056SNickeau public 728c3437056SNickeau function getRowId() 729c3437056SNickeau { 730c3437056SNickeau return $this->getFromRow(self::ROWID); 731c3437056SNickeau } 732c3437056SNickeau 733c3437056SNickeau private 734c3437056SNickeau function getDatabaseRowFromPageId(string $pageId) 735c3437056SNickeau { 736c3437056SNickeau 737c3437056SNickeau if ($this->sqlite === null) { 738c3437056SNickeau return null; 739c3437056SNickeau } 740c3437056SNickeau 741c3437056SNickeau $pageIdAttribute = PageId::PROPERTY_NAME; 742c3437056SNickeau $query = $this->getParametrizedLookupQuery($pageIdAttribute); 743c3437056SNickeau $request = Sqlite::createOrGetSqlite() 744c3437056SNickeau ->createRequest() 745c3437056SNickeau ->setQueryParametrized($query, [$pageId]); 746c3437056SNickeau $rows = []; 747c3437056SNickeau try { 748c3437056SNickeau $rows = $request 749c3437056SNickeau ->execute() 750c3437056SNickeau ->getRows(); 751c3437056SNickeau } catch (ExceptionCombo $e) { 752c3437056SNickeau LogUtility::msg($e->getMessage(), LogUtility::LVL_MSG_ERROR, $e->getCanonical()); 753c3437056SNickeau return null; 754c3437056SNickeau } finally { 755c3437056SNickeau $request->close(); 756c3437056SNickeau } 757c3437056SNickeau 758c3437056SNickeau switch (sizeof($rows)) { 759c3437056SNickeau case 0: 760c3437056SNickeau return null; 761c3437056SNickeau case 1: 762c3437056SNickeau $id = $rows[0][DokuwikiId::DOKUWIKI_ID_ATTRIBUTE]; 763c3437056SNickeau /** 764c3437056SNickeau * Page Id Collision detection 765c3437056SNickeau */ 766c3437056SNickeau if ($this->page != null && $id !== $this->page->getDokuwikiId()) { 767c3437056SNickeau $duplicatePage = Page::createPageFromId($id); 768c3437056SNickeau if (!$duplicatePage->exists()) { 769c3437056SNickeau // Move 770c3437056SNickeau LogUtility::msg("The non-existing duplicate page ($id) has been added as redirect alias for the page ($this->page)", LogUtility::LVL_MSG_INFO); 771c3437056SNickeau $this->addRedirectAliasWhileBuildingRow($duplicatePage); 772c3437056SNickeau } else { 773c3437056SNickeau // This can happens if two page were created not on the same website 774c3437056SNickeau // of if the sqlite database was deleted and rebuilt. 775c3437056SNickeau // The chance is really, really low 776c3437056SNickeau $errorMessage = "The page ($this->page) and the page ($id) have the same page id ($pageId)"; 777c3437056SNickeau LogUtility::msg($errorMessage, LogUtility::LVL_MSG_ERROR, self::CANONICAL); 778c3437056SNickeau // What to do ? 779c3437056SNickeau // The database does not allow two page id with the same value 780c3437056SNickeau // If it happens, ugh, ugh, ..., a replication process between website may be. 781c3437056SNickeau return null; 782c3437056SNickeau } 783c3437056SNickeau } 784c3437056SNickeau return $rows[0]; 785c3437056SNickeau default: 786c3437056SNickeau $existingPages = implode(", ", $rows); 787c3437056SNickeau LogUtility::msg("The pages ($existingPages) have all the same page id ($pageId)", LogUtility::LVL_MSG_ERROR); 788c3437056SNickeau return null; 789c3437056SNickeau } 790c3437056SNickeau 791c3437056SNickeau } 792c3437056SNickeau 793c3437056SNickeau 794c3437056SNickeau private 795c3437056SNickeau function getParametrizedLookupQuery(string $pageIdAttribute): string 796c3437056SNickeau { 797c3437056SNickeau $select = Sqlite::createSelectFromTableAndColumns("pages", self::PAGE_BUILD_ATTRIBUTES); 798c3437056SNickeau return "$select where $pageIdAttribute = ?"; 799c3437056SNickeau } 800c3437056SNickeau 801c3437056SNickeau 802c3437056SNickeau public function setPage(Page $page) 803c3437056SNickeau { 804c3437056SNickeau $this->page = $page; 805c3437056SNickeau return $this; 806c3437056SNickeau } 807c3437056SNickeau 808c3437056SNickeau private 809c3437056SNickeau function getDatabaseRowFromCanonical($canonical) 810c3437056SNickeau { 811c3437056SNickeau $query = $this->getParametrizedLookupQuery(Canonical::PROPERTY_NAME); 812c3437056SNickeau $request = $this->sqlite 813c3437056SNickeau ->createRequest() 814c3437056SNickeau ->setQueryParametrized($query, [$canonical]); 815c3437056SNickeau $rows = []; 816c3437056SNickeau try { 817c3437056SNickeau $rows = $request 818c3437056SNickeau ->execute() 819c3437056SNickeau ->getRows(); 820c3437056SNickeau } catch (ExceptionCombo $e) { 821c3437056SNickeau LogUtility::msg("An exception has occurred with the page search from CANONICAL. " . $e->getMessage()); 822c3437056SNickeau return null; 823c3437056SNickeau } finally { 824c3437056SNickeau $request->close(); 825c3437056SNickeau } 826c3437056SNickeau 827c3437056SNickeau switch (sizeof($rows)) { 828c3437056SNickeau case 0: 829c3437056SNickeau return null; 830c3437056SNickeau case 1: 831c3437056SNickeau $id = $rows[0][DokuwikiId::DOKUWIKI_ID_ATTRIBUTE]; 832c3437056SNickeau if ($this->page !== null && $id !== $this->page->getDokuwikiId()) { 833c3437056SNickeau $duplicatePage = Page::createPageFromId($id); 834c3437056SNickeau if (!$duplicatePage->exists()) { 835c3437056SNickeau $this->addRedirectAliasWhileBuildingRow($duplicatePage); 836c3437056SNickeau LogUtility::msg("The non-existing duplicate page ($id) has been added as redirect alias for the page ($this->page)", LogUtility::LVL_MSG_INFO); 837c3437056SNickeau } else { 838c3437056SNickeau LogUtility::msg("The page ($this->page) and the page ($id) have the same canonical ($canonical)", LogUtility::LVL_MSG_ERROR); 839c3437056SNickeau } 840c3437056SNickeau } 841c3437056SNickeau return $rows[0]; 842c3437056SNickeau default: 843c3437056SNickeau $existingPages = []; 844c3437056SNickeau foreach ($rows as $row) { 845c3437056SNickeau $id = $row[DokuwikiId::DOKUWIKI_ID_ATTRIBUTE]; 846c3437056SNickeau $duplicatePage = Page::createPageFromId($id); 847c3437056SNickeau if (!$duplicatePage->exists()) { 848c3437056SNickeau 849c3437056SNickeau $this->deleteIfExistsAndAddRedirectAlias($duplicatePage); 850c3437056SNickeau 851c3437056SNickeau } else { 852c3437056SNickeau 853c3437056SNickeau /** 854c3437056SNickeau * Check if the error may come from the auto-canonical 855c3437056SNickeau * (Never ever save generated data) 856c3437056SNickeau */ 857c3437056SNickeau $canonicalLastNamesCount = PluginUtility::getConfValue(Canonical::CONF_CANONICAL_LAST_NAMES_COUNT, 0); 858c3437056SNickeau if ($canonicalLastNamesCount > 0) { 859c3437056SNickeau $this->page->unsetMetadata(Canonical::PROPERTY_NAME); 860c3437056SNickeau $duplicatePage->unsetMetadata(Canonical::PROPERTY_NAME); 861c3437056SNickeau } 862c3437056SNickeau 863c3437056SNickeau $existingPages[] = $row; 864c3437056SNickeau } 865c3437056SNickeau } 866c3437056SNickeau if (sizeof($existingPages) === 1) { 867c3437056SNickeau return $existingPages[0]; 868c3437056SNickeau } else { 869c3437056SNickeau $existingPages = implode(", ", $existingPages); 870c3437056SNickeau LogUtility::msg("The existing pages ($existingPages) have all the same canonical ($canonical)", LogUtility::LVL_MSG_ERROR); 871c3437056SNickeau return null; 872c3437056SNickeau } 873c3437056SNickeau } 874c3437056SNickeau } 875c3437056SNickeau 876c3437056SNickeau private 877c3437056SNickeau function getDatabaseRowFromPath(string $path): ?array 878c3437056SNickeau { 879c3437056SNickeau DokuPath::addRootSeparatorIfNotPresent($path); 880c3437056SNickeau return $this->getDatabaseRowFromAttribute(PagePath::PROPERTY_NAME, $path); 881c3437056SNickeau } 882c3437056SNickeau 883c3437056SNickeau private 884c3437056SNickeau function getDatabaseRowFromDokuWikiId(string $id): ?array 885c3437056SNickeau { 886c3437056SNickeau return $this->getDatabaseRowFromAttribute(DokuwikiId::DOKUWIKI_ID_ATTRIBUTE, $id); 887c3437056SNickeau } 888c3437056SNickeau 889c3437056SNickeau public 890c3437056SNickeau function getDatabaseRowFromAttribute(string $attribute, string $value) 891c3437056SNickeau { 892c3437056SNickeau $query = $this->getParametrizedLookupQuery($attribute); 893c3437056SNickeau $request = $this->sqlite 894c3437056SNickeau ->createRequest() 895c3437056SNickeau ->setQueryParametrized($query, [$value]); 896c3437056SNickeau $rows = []; 897c3437056SNickeau try { 898c3437056SNickeau $rows = $request 899c3437056SNickeau ->execute() 900c3437056SNickeau ->getRows(); 901c3437056SNickeau } catch (ExceptionCombo $e) { 902c3437056SNickeau LogUtility::msg("An exception has occurred with the page search from a PATH: " . $e->getMessage()); 903c3437056SNickeau return null; 904c3437056SNickeau } finally { 905c3437056SNickeau $request->close(); 906c3437056SNickeau } 907c3437056SNickeau 908c3437056SNickeau switch (sizeof($rows)) { 909c3437056SNickeau case 0: 910c3437056SNickeau return null; 911c3437056SNickeau case 1: 912c3437056SNickeau $value = $rows[0][DokuwikiId::DOKUWIKI_ID_ATTRIBUTE]; 913c3437056SNickeau if ($this->page != null && $value !== $this->page->getDokuwikiId()) { 914c3437056SNickeau $duplicatePage = Page::createPageFromId($value); 915c3437056SNickeau if (!$duplicatePage->exists()) { 916c3437056SNickeau $this->addRedirectAliasWhileBuildingRow($duplicatePage); 917c3437056SNickeau } else { 918c3437056SNickeau LogUtility::msg("The page ($this->page) and the page ($value) have the same $attribute ($value)", LogUtility::LVL_MSG_ERROR); 919c3437056SNickeau } 920c3437056SNickeau } 921c3437056SNickeau return $rows[0]; 922c3437056SNickeau default: 923c3437056SNickeau $existingPages = []; 924c3437056SNickeau foreach ($rows as $row) { 925c3437056SNickeau $value = $row[DokuwikiId::DOKUWIKI_ID_ATTRIBUTE]; 926c3437056SNickeau $duplicatePage = Page::createPageFromId($value); 927c3437056SNickeau if (!$duplicatePage->exists()) { 928c3437056SNickeau 929c3437056SNickeau $this->deleteIfExistsAndAddRedirectAlias($duplicatePage); 930c3437056SNickeau 931c3437056SNickeau } else { 932c3437056SNickeau $existingPages[] = $row; 933c3437056SNickeau } 934c3437056SNickeau } 935c3437056SNickeau if (sizeof($existingPages) === 1) { 936c3437056SNickeau return $existingPages[0]; 937c3437056SNickeau } else { 938c3437056SNickeau $existingPages = implode(", ", $existingPages); 939c3437056SNickeau LogUtility::msg("The existing pages ($existingPages) have all the same $attribute ($value)", LogUtility::LVL_MSG_ERROR); 940c3437056SNickeau return null; 941c3437056SNickeau } 942c3437056SNickeau } 943c3437056SNickeau } 944c3437056SNickeau 945c3437056SNickeau public 946c3437056SNickeau function getPage(): ?Page 947c3437056SNickeau { 948c3437056SNickeau if ( 949c3437056SNickeau $this->page === null 950c3437056SNickeau && $this->row[DokuwikiId::DOKUWIKI_ID_ATTRIBUTE] !== null 951c3437056SNickeau ) { 952c3437056SNickeau $this->page = Page::createPageFromId($this->row[DokuwikiId::DOKUWIKI_ID_ATTRIBUTE]); 953c3437056SNickeau } 954c3437056SNickeau return $this->page; 955c3437056SNickeau } 956c3437056SNickeau 957c3437056SNickeau private 958c3437056SNickeau function getDatabaseRowFromAlias($alias): ?array 959c3437056SNickeau { 960c3437056SNickeau 961c3437056SNickeau $pageIdAttribute = PageId::PROPERTY_NAME; 962c3437056SNickeau $buildFields = self::PAGE_BUILD_ATTRIBUTES; 963c3437056SNickeau $fields = array_reduce($buildFields, function ($carry, $element) { 964c3437056SNickeau if ($carry !== null) { 965c3437056SNickeau return "$carry, p.{$element}"; 966c3437056SNickeau } else { 967c3437056SNickeau return "p.{$element}"; 968c3437056SNickeau } 969c3437056SNickeau }, null); 970c3437056SNickeau $query = "select {$fields} from PAGES p, PAGE_ALIASES pa where p.{$pageIdAttribute} = pa.{$pageIdAttribute} and pa.PATH = ? "; 971c3437056SNickeau $request = $this->sqlite 972c3437056SNickeau ->createRequest() 973c3437056SNickeau ->setQueryParametrized($query, [$alias]); 974c3437056SNickeau $rows = []; 975c3437056SNickeau try { 976c3437056SNickeau $rows = $request 977c3437056SNickeau ->execute() 978c3437056SNickeau ->getRows(); 979c3437056SNickeau } catch (ExceptionCombo $e) { 980c3437056SNickeau LogUtility::msg("An exception has occurred with the alias selection query. {$e->getMessage()}"); 981c3437056SNickeau return null; 982c3437056SNickeau } finally { 983c3437056SNickeau $request->close(); 984c3437056SNickeau } 985c3437056SNickeau switch (sizeof($rows)) { 986c3437056SNickeau case 0: 987c3437056SNickeau return null; 988c3437056SNickeau case 1: 989c3437056SNickeau return $rows[0]; 990c3437056SNickeau default: 991c3437056SNickeau $id = $rows[0]['ID']; 992c3437056SNickeau $pages = implode(",", 993c3437056SNickeau array_map( 994c3437056SNickeau function ($row) { 995c3437056SNickeau return $row['ID']; 996c3437056SNickeau }, 997c3437056SNickeau $rows 998c3437056SNickeau ) 999c3437056SNickeau ); 1000c3437056SNickeau LogUtility::msg("For the alias $alias, there is more than one page defined ($pages), the first one ($id) was used", LogUtility::LVL_MSG_ERROR, Aliases::PROPERTY_NAME); 1001c3437056SNickeau return $rows[0]; 1002c3437056SNickeau } 1003c3437056SNickeau } 1004c3437056SNickeau 1005c3437056SNickeau 1006c3437056SNickeau /** 1007c3437056SNickeau * Utility function 1008c3437056SNickeau * @param Page $pageAlias 1009c3437056SNickeau */ 1010c3437056SNickeau private 1011c3437056SNickeau function addRedirectAliasWhileBuildingRow(Page $pageAlias) 1012c3437056SNickeau { 1013c3437056SNickeau 1014c3437056SNickeau $aliasPath = $pageAlias->getPath()->toString(); 1015c3437056SNickeau try { 1016c3437056SNickeau Aliases::createForPage($this->page) 1017c3437056SNickeau ->addAlias($aliasPath) 1018c3437056SNickeau ->sendToWriteStore(); 1019c3437056SNickeau } catch (ExceptionCombo $e) { 1020c3437056SNickeau // we don't throw while getting 1021c3437056SNickeau LogUtility::msg("Unable to add the alias ($aliasPath) for the page ($this->page)"); 1022c3437056SNickeau } 1023c3437056SNickeau 1024c3437056SNickeau } 1025c3437056SNickeau 1026c3437056SNickeau private 1027c3437056SNickeau function addPageIdAttributeIfNeeded(array &$values) 1028c3437056SNickeau { 1029c3437056SNickeau if (!isset($values[PageId::getPersistentName()])) { 1030c3437056SNickeau $values[PageId::getPersistentName()] = $this->page->getPageId(); 1031c3437056SNickeau } 1032c3437056SNickeau if (!isset($values[PageId::PAGE_ID_ABBR_ATTRIBUTE])) { 1033c3437056SNickeau $values[PageId::PAGE_ID_ABBR_ATTRIBUTE] = $this->page->getPageIdAbbr(); 1034c3437056SNickeau } 1035c3437056SNickeau } 1036c3437056SNickeau 1037c3437056SNickeau public 1038c3437056SNickeau function getFromRow(string $attribute) 1039c3437056SNickeau { 1040c3437056SNickeau if ($this->row === null) { 1041c3437056SNickeau return null; 1042c3437056SNickeau } 1043c3437056SNickeau 1044c3437056SNickeau if (!array_key_exists($attribute, $this->row)) { 1045c3437056SNickeau /** 1046c3437056SNickeau * An attribute should be added to {@link DatabasePageRow::PAGE_BUILD_ATTRIBUTES} 1047c3437056SNickeau * or in the table 1048c3437056SNickeau */ 1049c3437056SNickeau throw new ExceptionComboRuntime("The metadata ($attribute) was not found in the returned database row.", $this->getCanonical()); 1050c3437056SNickeau } 1051c3437056SNickeau 1052c3437056SNickeau $value = $this->row[$attribute]; 1053c3437056SNickeau 1054c3437056SNickeau if ($value !== null) { 1055c3437056SNickeau return $value; 1056c3437056SNickeau } 1057c3437056SNickeau 1058c3437056SNickeau // don't know why but the sqlite plugin returns them uppercase 1059c3437056SNickeau // rowid is returned lowercase from the sqlite plugin 1060c3437056SNickeau $upperAttribute = strtoupper($attribute); 1061c3437056SNickeau return $this->row[$upperAttribute]; 1062c3437056SNickeau 1063c3437056SNickeau } 1064c3437056SNickeau 1065c3437056SNickeau 1066c3437056SNickeau public function replicateAnalytics() 1067c3437056SNickeau { 1068c3437056SNickeau 1069c3437056SNickeau try { 1070c3437056SNickeau $analyticsJson = $this->page->getAnalyticsDocument()->getOrProcessJson(); 1071c3437056SNickeau } catch (ExceptionCombo $e) { 1072c3437056SNickeau LogUtility::msg("Unable to replicate the analytics: " . $e->getMessage()); 1073c3437056SNickeau return; 1074c3437056SNickeau } 1075c3437056SNickeau 1076c3437056SNickeau /** 1077c3437056SNickeau * Replication Date 1078c3437056SNickeau */ 1079c3437056SNickeau $replicationDateMeta = \ReplicationDate::createFromPage($this->page) 1080c3437056SNickeau ->setWriteStore(MetadataDbStore::class) 1081c3437056SNickeau ->setValue(new \DateTime()); 1082c3437056SNickeau 1083c3437056SNickeau /** 1084c3437056SNickeau * Analytics 1085c3437056SNickeau */ 1086c3437056SNickeau $analyticsJsonAsString = $analyticsJson->toPrettyJsonString(); 1087c3437056SNickeau $analyticsJsonAsArray = $analyticsJson->toArray(); 1088c3437056SNickeau 1089c3437056SNickeau /** 1090c3437056SNickeau * Record 1091c3437056SNickeau */ 1092c3437056SNickeau $record[self::ANALYTICS_ATTRIBUTE] = $analyticsJsonAsString; 1093c3437056SNickeau $record['IS_LOW_QUALITY'] = ($this->page->isLowQualityPage() === true ? 1 : 0); 1094c3437056SNickeau $record['WORD_COUNT'] = $analyticsJsonAsArray[AnalyticsDocument::STATISTICS][AnalyticsDocument::WORD_COUNT]; 1095c3437056SNickeau $record[BacklinkCount::getPersistentName()] = $analyticsJsonAsArray[AnalyticsDocument::STATISTICS][BacklinkCount::getPersistentName()]; 1096c3437056SNickeau $record[$replicationDateMeta::getPersistentName()] = $replicationDateMeta->toStoreValue(); 1097c3437056SNickeau $this->upsertAttributes($record); 1098c3437056SNickeau } 1099c3437056SNickeau 1100c3437056SNickeau 1101c3437056SNickeau} 1102