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