function synchronize($task, $entitiesToSynchronize = null)
 {
     $this->maybeInit($entitiesToSynchronize);
     $options = $this->options;
     if (count($options) > 0) {
         $syncQuery = "INSERT INTO {$this->tableName} (option_name, option_value, autoload) VALUES ";
         foreach ($options as $optionName => $option) {
             $option = $this->urlReplacer->restore($option);
             $option = $this->maybeRestoreReference($option);
             if (!isset($option['autoload'])) {
                 $option['autoload'] = 'yes';
             }
             // default value
             if (!isset($option['option_value'])) {
                 $option['option_value'] = '';
             }
             $syncQuery .= "(\"{$optionName}\", \"" . $this->database->_real_escape($option['option_value']) . "\", \"{$option['autoload']}\"),";
         }
         $syncQuery[strlen($syncQuery) - 1] = " ";
         // strip last comma
         $syncQuery .= " ON DUPLICATE KEY UPDATE option_value = VALUES(option_value), autoload = VALUES(autoload);";
         $this->database->query($syncQuery);
     }
     $ignoredOptionNames = ArrayUtils::column($options, 'option_name');
     $ignoredOptionNames = array_merge($ignoredOptionNames, OptionStorage::$optionsBlacklist);
     $deleteSql = "DELETE FROM {$this->tableName} WHERE option_name NOT IN (\"" . join('", "', $ignoredOptionNames) . "\") OR option_name NOT LIKE '_%'";
     if ($entitiesToSynchronize) {
         $synchronizedOptions = ArrayUtils::column($entitiesToSynchronize, 'vp_id');
         $deleteSql = "DELETE FROM {$this->tableName} WHERE option_name NOT IN (\"" . join('", "', $ignoredOptionNames) . "\") AND option_name IN (\"" . join('", "', $synchronizedOptions) . "\")";
     }
     $this->database->query($deleteSql);
     return array();
 }
 public function restoreAllDefinitionFilesFromHistory()
 {
     FileSystem::removeContent($this->directory);
     $definitionFilesWildcard = WP_PLUGIN_DIR . '/*/.versionpress/actions.yml';
     $modifications = $this->gitRepository->getFileModifications($definitionFilesWildcard);
     $modifications = array_filter($modifications, function ($modification) {
         return $modification['status'] !== 'D';
     });
     $lastModifications = ArrayUtils::unique($modifications, function ($modification) {
         return $modification['path'];
     });
     foreach ($lastModifications as $modification) {
         $fileContent = $this->gitRepository->getFileInRevision($modification['path'], $modification['commit']);
         $plugin = basename(dirname(dirname($modification['path'])));
         $targetFile = $this->getDefinitionFileName($plugin);
         file_put_contents($targetFile, $fileContent);
     }
     $this->saveDefinitionForPlugin('versionpress/versionpress.php');
 }
 private function containsTermChangeInfo($changeInfoList)
 {
     return ArrayUtils::any($changeInfoList, function ($changeInfo) {
         return $changeInfo instanceof TermChangeInfo;
     });
 }
Example #4
0
 private function getIdsForVpIds($referencesToUpdate)
 {
     if (count($referencesToUpdate) === 0) {
         return array(array(0, 0));
     }
     $vpIdTable = $this->dbSchema->getPrefixedTableName('vp_id');
     $vpIds = array_map(function ($vpId) {
         return 'UNHEX("' . $vpId . '")';
     }, $referencesToUpdate);
     $vpIdsRestriction = join(', ', $vpIds);
     $result = $this->database->get_results("SELECT HEX(vp_id), id FROM {$vpIdTable} WHERE vp_id IN ({$vpIdsRestriction})", ARRAY_N);
     $result[] = array(0, 0);
     return array_combine(ArrayUtils::column($result, 0), ArrayUtils::column($result, 1));
 }
Example #5
0
 /**
  * Returns true if there is any entity with reference to the passed one.
  *
  * @param $entityName
  * @param $entityId
  * @return bool
  */
 private function existsSomeEntityWithReferenceTo($entityName, $entityId)
 {
     $entityNames = $this->dbSchemaInfo->getAllEntityNames();
     foreach ($entityNames as $otherEntityName) {
         $otherEntityInfo = $this->dbSchemaInfo->getEntityInfo($otherEntityName);
         $otherEntityReferences = $otherEntityInfo->references;
         $otherEntityMnReferences = $otherEntityInfo->mnReferences;
         $otherEntityValueReferences = $otherEntityInfo->valueReferences;
         $allReferences = array_merge($otherEntityReferences, $otherEntityMnReferences, $otherEntityValueReferences);
         foreach ($allReferences as $reference => $referencedEntity) {
             // if the target is dynamic, check it anyway - just to be sure
             if ($referencedEntity !== $entityName && $referencedEntity[0] !== '@') {
                 continue;
             }
             $otherEntityStorage = $this->storageFactory->getStorage($otherEntityName);
             $possiblyReferencingEntities = $otherEntityStorage->loadAll();
             if (isset($otherEntityReferences[$reference])) {
                 // 1:N reference
                 $vpReference = "vp_{$reference}";
                 foreach ($possiblyReferencingEntities as $possiblyReferencingEntity) {
                     if (isset($possiblyReferencingEntity[$vpReference])) {
                         $referencedVpidsString = $possiblyReferencingEntity[$vpReference];
                         preg_match_all(IdUtil::getRegexMatchingId(), $referencedVpidsString, $matches);
                         if (ArrayUtils::any($matches[0], Comparators::equals($entityId))) {
                             return true;
                         }
                     }
                 }
             } elseif (isset($otherEntityMnReferences[$reference])) {
                 // M:N reference
                 $vpReference = "vp_{$otherEntityName}";
                 foreach ($possiblyReferencingEntities as $possiblyReferencingEntity) {
                     if (isset($possiblyReferencingEntity[$vpReference]) && array_search($entityId, $possiblyReferencingEntity[$vpReference]) !== false) {
                         return true;
                     }
                 }
             } elseif (isset($otherEntityValueReferences[$reference])) {
                 // Value reference
                 list($sourceColumn, $sourceValue, $valueColumn, $pathInStructure) = array_values(ReferenceUtils::getValueReferenceDetails($reference));
                 foreach ($possiblyReferencingEntities as $possiblyReferencingEntity) {
                     if (isset($possiblyReferencingEntity[$sourceColumn]) && ($possiblyReferencingEntity[$sourceColumn] === $sourceValue || ReferenceUtils::valueMatchesWildcard($sourceValue, $possiblyReferencingEntity[$sourceColumn])) && isset($possiblyReferencingEntity[$valueColumn])) {
                         if (is_numeric($possiblyReferencingEntity[$valueColumn]) && intval($possiblyReferencingEntity[$valueColumn]) === 0 || $possiblyReferencingEntity[$valueColumn] === '') {
                             continue;
                         }
                         if ($pathInStructure) {
                             $possiblyReferencingEntity[$valueColumn] = unserialize($possiblyReferencingEntity[$valueColumn]);
                             $paths = ReferenceUtils::getMatchingPaths($possiblyReferencingEntity[$valueColumn], $pathInStructure);
                         } else {
                             $paths = [[]];
                             // root = the value itself
                         }
                         /** @var Cursor[] $cursors */
                         $cursors = array_map(function ($path) use(&$possiblyReferencingEntity, $valueColumn) {
                             return new Cursor($possiblyReferencingEntity[$valueColumn], $path);
                         }, $paths);
                         foreach ($cursors as $cursor) {
                             $vpidsString = $cursor->getValue();
                             preg_match_all(IdUtil::getRegexMatchingId(), $vpidsString, $matches);
                             if (ArrayUtils::any($matches[0], Comparators::equals($entityId))) {
                                 return true;
                             }
                         }
                     }
                 }
             }
         }
     }
     return false;
 }
Example #6
0
 function sort($changeInfoList)
 {
     ArrayUtils::stablesort($changeInfoList, array($this, 'compareChangeInfo'));
     return $changeInfoList;
 }
 /**
  * Creates manual commit. Adds everything to stage.
  *
  * @param WP_REST_Request $request
  * @return WP_REST_Response|WP_Error
  */
 public function commit(WP_REST_Request $request)
 {
     $currentUser = wp_get_current_user();
     if ($currentUser->ID === 0) {
         return new WP_Error('error', 'You don\'t have permission to do this.', ['status' => 403]);
     }
     /** @noinspection PhpUndefinedFieldInspection */
     $authorName = $currentUser->display_name;
     /** @noinspection PhpUndefinedFieldInspection */
     $authorEmail = $currentUser->user_email;
     $this->gitRepository->stageAll();
     $status = $this->gitRepository->getStatus(true);
     if (ArrayUtils::any($status, function ($fileStatus) {
         $vpdbName = basename(VP_VPDB_DIR);
         return Strings::contains($fileStatus[1], $vpdbName);
     })) {
         $this->updateDatabase($status);
     }
     $commitMessage = new CommitMessage($request['commit-message']);
     $changeInfoEnvelope = new ChangeInfoEnvelope([new UntrackedChangeInfo($commitMessage)]);
     $this->gitRepository->commit($changeInfoEnvelope->getCommitMessage(), $authorName, $authorEmail);
     return new WP_REST_Response(true);
 }
 /**
  * If entity type identified by $entityName defines an ID column, creates a mapping between WordPress ID and VPID
  * for all entities (db rows) of such type.
  *
  * @param string $entityName E.g., "post"
  */
 private function createVpidsForEntitiesOfType($entityName)
 {
     if (!$this->dbSchema->getEntityInfo($entityName)->usesGeneratedVpids) {
         return;
     }
     $idColumnName = $this->dbSchema->getEntityInfo($entityName)->idColumnName;
     $tableName = $this->dbSchema->getTableName($entityName);
     $prefixedTableName = $this->dbSchema->getPrefixedTableName($entityName);
     $entities = $this->database->get_results("SELECT * FROM {$prefixedTableName}", ARRAY_A);
     $entities = $this->replaceForeignKeysWithReferencesInAllEntities($entityName, $entities);
     $storage = $this->storageFactory->getStorage($entityName);
     $entities = array_filter($entities, function ($entity) use($storage) {
         return $storage->shouldBeSaved($entity);
     });
     $chunks = array_chunk($entities, 1000);
     foreach ($chunks as $entitiesInChunk) {
         $wordpressIds = array_column($entitiesInChunk, $idColumnName);
         $idPairs = [];
         foreach ($wordpressIds as $id) {
             $id = intval($id);
             if (!isset($this->idCache[$entityName], $this->idCache[$entityName][$id])) {
                 $this->idCache[$entityName][$id] = IdUtil::newId();
             }
             $idPairs[$id] = $this->idCache[$entityName][$id];
         }
         $sqlValues = join(', ', ArrayUtils::map(function ($vpId, $id) use($tableName) {
             return "('{$tableName}', {$id}, UNHEX('{$vpId}'))";
         }, $idPairs));
         $query = "INSERT INTO {$this->database->vp_id} (`table`, id, vp_id) VALUES {$sqlValues}";
         $this->database->query($query);
         $this->checkTimeout();
     }
 }
Example #9
0
 /**
  * If entity type identified by $entityName defines an ID column, creates a mapping between WordPress ID and VPID
  * for all entities (db rows) of such type.
  *
  * @param string $entityName E.g., "post"
  */
 private function createVpidsForEntitiesOfType($entityName)
 {
     if (!$this->dbSchema->getEntityInfo($entityName)->usesGeneratedVpids) {
         return;
     }
     $idColumnName = $this->dbSchema->getEntityInfo($entityName)->idColumnName;
     $tableName = $this->dbSchema->getTableName($entityName);
     $prefixedTableName = $this->dbSchema->getPrefixedTableName($entityName);
     $entities = $this->database->get_results("SELECT * FROM {$prefixedTableName}", ARRAY_A);
     $entities = $this->replaceForeignKeysWithReferencesInAllEntities($entityName, $entities);
     $storage = $this->storageFactory->getStorage($entityName);
     $entities = array_filter($entities, function ($entity) use($storage) {
         return $storage->shouldBeSaved($entity);
     });
     $chunks = array_chunk($entities, 1000);
     $this->idCache[$entityName] = array();
     foreach ($chunks as $entitiesInChunk) {
         $wordpressIds = ArrayUtils::column($entitiesInChunk, $idColumnName);
         $vpIds = array_map(array('VersionPress\\Utils\\IdUtil', 'newId'), $entitiesInChunk);
         $idPairs = array_combine($wordpressIds, $vpIds);
         $this->idCache[$entityName] = $this->idCache[$entityName] + $idPairs;
         // merge arrays with preserving keys
         $sqlValues = join(', ', ArrayUtils::map(function ($vpId, $id) use($tableName) {
             return "('{$tableName}', {$id}, UNHEX('{$vpId}'))";
         }, $idPairs));
         $query = "INSERT INTO {$this->getTableName('vp_id')} (`table`, id, vp_id) VALUES {$sqlValues}";
         $this->database->query($query);
         $this->checkTimeout();
     }
 }
Example #10
0
 /**
  * Creates manual commit. Adds everything to stage.
  *
  * @param WP_REST_Request $request
  * @return WP_REST_Response|\WP_Error
  */
 public function commit(WP_REST_Request $request)
 {
     $currentUser = wp_get_current_user();
     if ($currentUser->ID === 0) {
         return new \WP_Error('error', 'You don\'t have permission to do this.', array('status' => 403));
     }
     /** @noinspection PhpUndefinedFieldInspection */
     $authorName = $currentUser->display_name;
     /** @noinspection PhpUndefinedFieldInspection */
     $authorEmail = $currentUser->user_email;
     $this->gitRepository->stageAll();
     $status = $this->gitRepository->getStatus(true);
     if (ArrayUtils::any($status, function ($fileStatus) {
         return Strings::contains($fileStatus[1], 'vpdb');
     })) {
         $this->updateDatabase($status);
     }
     $this->gitRepository->commit($request['commit-message'], $authorName, $authorEmail);
     return new WP_REST_Response(true);
 }
Example #11
0
 private function loadAllFromFiles($entityFiles)
 {
     $entities = array_map(array($this, 'deserializeEntity'), array_map('file_get_contents', $entityFiles));
     $vpIds = ArrayUtils::column($entities, $this->entityInfo->vpidColumnName);
     return array_combine($vpIds, $entities);
 }
 private function groupBulkActions($changeInfoList)
 {
     $bulkChangeInfoClasses = $this->bulkChangeInfoClasses;
     $groupedChangeInfos = ArrayUtils::mapreduce($changeInfoList, function (TrackedChangeInfo $item, $mapEmit) {
         $key = "{$item->getEntityName()}/{$item->getAction()}";
         $mapEmit($key, $item);
     }, function ($key, $items, $reduceEmit) use($bulkChangeInfoClasses) {
         /** @var TrackedChangeInfo[] $items */
         if (count($items) > 1) {
             $entityName = $items[0]->getEntityName();
             if (isset($bulkChangeInfoClasses[$entityName])) {
                 $reduceEmit(new $bulkChangeInfoClasses[$entityName]($items));
             } else {
                 $reduceEmit($items);
             }
         } else {
             $reduceEmit($items[0]);
         }
     });
     $changeInfos = array();
     foreach ($groupedChangeInfos as $changeInfoGroup) {
         if (is_array($changeInfoGroup)) {
             foreach ($changeInfoGroup as $changeInfo) {
                 $changeInfos[] = $changeInfo;
             }
         } else {
             $changeInfos[] = $changeInfoGroup;
         }
     }
     return $changeInfos;
 }
 private function groupBulkActions($changeInfoList)
 {
     $groupedChangeInfos = ArrayUtils::mapreduce($changeInfoList, function (ChangeInfo $item, $mapEmit) {
         if ($item instanceof TrackedChangeInfo) {
             $key = "{$item->getScope()}/{$item->getAction()}";
             $mapEmit($key, $item);
         } else {
             $mapEmit(spl_object_hash($item), $item);
         }
     }, function ($key, $items, $reduceEmit) {
         /** @var TrackedChangeInfo[] $items */
         if (count($items) > 1) {
             $reduceEmit(new BulkChangeInfo($items));
         } else {
             $reduceEmit($items[0]);
         }
     });
     $changeInfos = [];
     foreach ($groupedChangeInfos as $changeInfoGroup) {
         if (is_array($changeInfoGroup)) {
             foreach ($changeInfoGroup as $changeInfo) {
                 $changeInfos[] = $changeInfo;
             }
         } else {
             $changeInfos[] = $changeInfoGroup;
         }
     }
     return $changeInfos;
 }
 private function containsTermChangeInfo($changeInfoList)
 {
     return ArrayUtils::any($changeInfoList, function ($changeInfo) {
         return ChangeInfoUtils::changeInfoRepresentsEntity($changeInfo, 'term');
     });
 }
Example #15
0
 private static function getRelatedHooks($tag, $hookType)
 {
     $relatedHooks = isset(self::$hooks[$hookType][$tag]) ? self::$hooks[$hookType][$tag] : [];
     ArrayUtils::stablesort($relatedHooks, function ($hook1, $hook2) {
         return $hook1['priority'] - $hook2['priority'];
     });
     return $relatedHooks;
 }
Example #16
0
 /**
  * @param $entityName
  */
 private static function assertEntitiesEqualDatabase($entityName)
 {
     $storage = self::$storageFactory->getStorage($entityName);
     $entityInfo = self::$schemaInfo->getEntityInfo($entityName);
     $allDbEntities = self::selectAll(self::$schemaInfo->getPrefixedTableName($entityName));
     $idMap = self::getVpIdMap();
     $allDbEntities = self::identifyEntities($entityName, $allDbEntities, $idMap);
     $allDbEntities = self::replaceForeignKeys($entityName, $allDbEntities, $idMap);
     $dbEntities = array_filter($allDbEntities, [$storage, 'shouldBeSaved']);
     $urlReplacer = new AbsoluteUrlReplacer(self::$testConfig->testSite->url);
     $storageEntities = array_map(function ($entity) use($urlReplacer) {
         return $urlReplacer->restore($entity);
     }, $storage->loadAll());
     $countOfentitiesInDb = count($dbEntities);
     $countOfentitiesInStorage = count($storageEntities);
     if ($countOfentitiesInDb !== $countOfentitiesInStorage) {
         if ($countOfentitiesInStorage > $countOfentitiesInDb) {
             $problematicEntities = self::findMissingEntities($entityName, $storageEntities, $dbEntities);
         } else {
             $problematicEntities = self::findExceedingEntities($entityName, $storageEntities, $dbEntities);
         }
         throw new \PHPUnit_Framework_AssertionFailedError("Different count of synchronized entities ({$entityName}): DB = {$countOfentitiesInDb}, " . "storage = {$countOfentitiesInStorage}\nProblematic entities: " . join(", ", $problematicEntities));
     }
     foreach ($dbEntities as $dbEntity) {
         $id = $dbEntity[$entityInfo->vpidColumnName];
         $storageEntity = $storageEntities[$id];
         $dbEntity = self::$shortcodesReplacer->replaceShortcodesInEntity($entityName, $dbEntity);
         foreach ($dbEntity as $column => $value) {
             if ($entityInfo->idColumnName === $column || isset($entityInfo->getIgnoredColumns()[$column])) {
                 continue;
             }
             if (!isset($storageEntity[$column])) {
                 throw new \PHPUnit_Framework_AssertionFailedError("{$entityName}[{$column}] with value = {$value}, ID = {$id} not found in storage");
             }
             if (is_string($storageEntity[$column])) {
                 $storageEntity[$column] = str_replace("\r\n", "\n", $storageEntity[$column]);
             }
             if (is_string($value)) {
                 $value = str_replace("\r\n", "\n", $value);
             }
             if ($storageEntity[$column] != $value) {
                 throw new \PHPUnit_Framework_AssertionFailedError("Different values ({$entityName}[{$column}]: {$id}): DB = {$value}, storage = {$storageEntity[$column]}");
             }
         }
     }
     $missingReferences = [];
     $exceedingReferences = [];
     foreach ($entityInfo->mnReferences as $reference => $targetEntity) {
         if ($entityInfo->isVirtualReference($reference)) {
             continue;
         }
         $referenceDetails = ReferenceUtils::getMnReferenceDetails(self::$schemaInfo, $entityName, $reference);
         $sourceColumn = $referenceDetails['source-column'];
         $targetColumn = $referenceDetails['target-column'];
         $junctionTable = $referenceDetails['junction-table'];
         $prefixedJunctionTable = self::$schemaInfo->getPrefixedTableName($junctionTable);
         $prefixedVpIdTable = self::$schemaInfo->getPrefixedTableName('vp_id');
         $sourceTable = self::$schemaInfo->getTableName($referenceDetails['source-entity']);
         $targetTable = self::$schemaInfo->getTableName($referenceDetails['target-entity']);
         $junctionTableContent = self::fetchAll("SELECT HEX(s_vp_id.vp_id), HEX(t_vp_id.vp_id) FROM {$prefixedJunctionTable} j\n                 JOIN {$prefixedVpIdTable} s_vp_id ON j.{$sourceColumn} = s_vp_id.id AND s_vp_id.`table`='{$sourceTable}'\n                 JOIN {$prefixedVpIdTable} t_vp_id ON j.{$targetColumn} = t_vp_id.id AND t_vp_id.`table` = '{$targetTable}'", MYSQLI_NUM);
         $checkedReferences = [];
         $missingReferences[$junctionTable] = [];
         foreach ($storageEntities as $storageEntity) {
             if (!isset($storageEntity["vp_{$targetEntity}"])) {
                 continue;
             }
             foreach ($storageEntity["vp_{$targetEntity}"] as $referenceVpId) {
                 if (!ArrayUtils::any($junctionTableContent, function ($junctionRow) use($storageEntity, $referenceVpId) {
                     return $junctionRow[0] === $storageEntity['vp_id'] && $junctionRow[1] === $referenceVpId;
                 })) {
                     $missingReferences[$junctionTable][] = [$sourceColumn => $storageEntity['vp_id'], $targetColumn => $referenceVpId];
                 }
                 $checkedReferences[] = [$storageEntity['vp_id'], $referenceVpId];
             }
         }
         $exceedingReferences[$junctionTable] = array_map(function ($pair) use($sourceColumn, $targetColumn) {
             return [$sourceColumn => $pair[0], $targetColumn => $pair[1]];
         }, array_filter($junctionTableContent, function ($pair) use($checkedReferences) {
             foreach ($checkedReferences as $reference) {
                 if ($reference[0] === $pair[0] && $reference[1] === $pair[1]) {
                     return false;
                 }
             }
             return true;
         }));
     }
     self::reportResultOfMnReferenceCheck($missingReferences, "Missing");
     self::reportResultOfMnReferenceCheck($exceedingReferences, "Exceeding");
 }