You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

342 lines
11 KiB

<?php
/**
* Try to get changes from an Elasticsearch index and perform each wiki page change event
*
* $Id$
*/
namespace Tiki\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
define('TIKI_CONSOLE', 1);
if (isset($_SERVER['REQUEST_METHOD'])) {
die('Only available through command-line.');
}
require_once('tiki-setup.php');
/**
* Add a singleton command "rescue" using the Symfony concole component just for this script
*
* Class ESRescueCommand
* @package Tiki\Command
*/
class ESRescueCommand extends Command
{
private $elasticUri;
private $indexName;
protected function configure()
{
$this
->setName('rescue')
->setDescription("Rescues wiki changes from the activity stream in an Elasticsearch index\nExample usage: php doc/devtools/rescue_wiki_changes_from_elastic.php rescue -u http://elastic.example.com:9200/ -i mytiki_main")
->addOption(
'elasticuri',
'u',
InputOption::VALUE_OPTIONAL,
'Elasticsearch URI',
'http://localhost:9200/'
)
->addOption(
'indexname',
'i',
InputOption::VALUE_OPTIONAL,
'Index Name',
'tiki_main'
)
->addOption(
'startdate',
's',
InputOption::VALUE_OPTIONAL,
'Start Date',
'2016-11-04'
)
->addOption(
'enddate',
'e',
InputOption::VALUE_OPTIONAL,
'End Date',
'2017-04-05'
)
->addOption(
'confirm',
'c',
InputOption::VALUE_NONE,
'Confirm (add -c or --confirm to perform the actual changes)'
);
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->elasticUri = $input->getOption('elasticuri');
$this->elasticUri = strrpos($this->elasticUri, '/') !== (strlen($this->elasticUri) - 1) ? $this->elasticUri . '/' : $this->elasticUri;
$this->indexName = $input->getOption('indexname');
$startDate = $input->getOption('startdate');
$endDate = $input->getOption('enddate');
$results = $this->getWikiPageEditEvents($startDate, $endDate);
$output->writeln(tr('<comment>Found %0 wiki edits</comment>', count($results)));
$confirm = $input->getOption('confirm');
if (! $confirm) {
$output->writeln(tr('<info>Dry run mode: add --confirm to run for real</info>', count($results)));
}
$this->makeThePageUpdates($results, $output, $confirm);
}
/**
* @param array $edits
* @param OutputInterface $output
*/
private function makeThePageUpdates(array $edits, OutputInterface $output, $confirm = false)
{
$tikilib = \TikiLib::lib('tiki');
$histlib = \TikiLib::lib('hist');
$now = date('c');
$progress = new ProgressBar($output, count($edits));
if ($confirm) {
$progress->start();
}
$transaction = $tikilib->begin();
foreach ($edits as $edit) {
$page = $edit['page'];
if (! $tikilib->page_exists($page)) {
$info = $this->getWikiPage($page);
if ($confirm) {
$tikilib->create_page(
$page,
0,
$edit['data'],
strtotime($info['creation_date']),
'Page created by rescue script ' . $now,
$edit['user'],
'0.0.0.0',
$info['description'],
$info['language'],
null,
null,
null,
'',
0,
strtotime($info['creation_date'])
);
} else {
$output->writeln(tr('<comment>Page: "%0" (version %1) CREATED</comment>', $page, $edit['version']));
}
if ($edit['version'] > 1 && $confirm) { // missing history
$tiki_history = $tikilib->table('tiki_history');
$old_version = ! empty($edit['old_version']) ? $edit['old_version'] : $edit['version'] - 1;
$tiki_history->update([
'version' => $old_version,
], [
'pageName' => $page,
]);
$tiki_history->insert([
'pageName' => $page,
'version' => $edit['version'],
'version_minor' => 0,
'lastModif' => strtotime($edit['modification_date']),
'user' => $edit['user'],
'ip' => '0.0.0.0',
'comment' => 'Version created by rescue script ' . $now,
'data' => $edit['old_data'],
'description' => $info['description'],
'is_html' => 0, // a guess
]);
}
} else {
$info = $tikilib->get_page_info($page);
if (! $histlib->get_version($page, $edit['version'])) {
if ($confirm) {
$tikilib->update_page(
$page,
$edit['data'],
'Edit restored by rescue script ' . $now,
$edit['user'],
'0.0.0.0',
null,
0,
'',
null,
null,
strtotime($edit['modification_date'])
);
} else {
$output->writeln(tr('<info>Page: "%0" (version %1) UPDATED</info>', $page, $edit['version']));
}
} else {
// version alrteady exists, do what?
}
}
if ($confirm) {
$progress->advance();
}
}
if ($confirm) {
$progress->finish();
}
$output->writeln('');
$output->writeln(tr('Committing transaction'));
$transaction->commit();
$output->writeln('');
$output->writeln(tr('Done'));
}
/**
* Get all activity stream events for wiki pages
*
* @param $startDate
* @param $endDate
* @return array|bool
* @internal param $unifiedsearchlib
* @internal param $this
*/
private function getWikiPageEditEvents($startDate, $endDate)
{
$unifiedsearchlib = \TikiLib::lib('unifiedsearch');
$connection = new \Search_Elastic_Connection($this->elasticUri);
$esIndex = new \Search_Elastic_Index($connection, $this->indexName);
$query = new \Search_Query();
$unifiedsearchlib->initQueryBase($query);
//$query = $unifiedsearchlib->buildQuery($filter, $query); // annoying, can't use type in this as that converts it to object_type, meh
$query->filterIdentifier('wiki page', 'type');
$query->filterType('activity');
if ($startDate && $endDate) {
$query->filterRange($startDate, $endDate);
}
$query->setOrder('modification_date_nasc');
$query->setRange(0, 1000);
// build query for es
$builder = new \Search_Elastic_OrderBuilder();
$orderPart = $builder->build($query->getSortOrder());
$builder = new \Search_Elastic_QueryBuilder($esIndex);
$queryPart = $builder->build($query->getExpr());
$fullQuery = array_merge(
$queryPart,
$orderPart,
[
'from' => 0,
'size' => 1000,
]
);
$data = json_encode($fullQuery);
$full = "{$this->elasticUri}{$this->indexName}/_search";
//$full = "{$elasticUri}{$indexName}/_validate/query?explain=true";
$client = \TikiLib::lib('tiki')->get_http_client($full);
$client->setRawBody($data);
$client->setMethod(\Laminas\Http\Request::METHOD_GET);
$client->setHeaders(['Content-Type: application/json']);
try {
$response = $client->send();
$body = $response->getBody();
$decoded = json_decode($body);
$hits = $decoded->hits;
$results = [];
foreach ($hits->hits as $hit) {
$results[] = [
'modification_date' => $hit->_source->modification_date,
'page' => $hit->_source->object,
'user' => $hit->_source->user,
'version' => $hit->_source->version,
'old_version' => $hit->_source->old_version,
'data' => $hit->_source->data,
'old_data' => $hit->_source->old_data,
];
}
return $results;
} catch (\Exception $e) {
echo $e->getMessage();
return null;
}
}
/**
* Gets a single wiki page definitioopn from the index (for missing pages)
*
* @param string $page page name
* @return array|bool
* @internal param $unifiedsearchlib
* @internal param $this
*/
private function getWikiPage($page)
{
$unifiedsearchlib = \TikiLib::lib('unifiedsearch');
$connection = new \Search_Elastic_Connection($this->elasticUri);
$esIndex = new \Search_Elastic_Index($connection, $this->indexName);
$query = new \Search_Query();
$unifiedsearchlib->initQueryBase($query);
//$query = $unifiedsearchlib->buildQuery($filter, $query); // annoying, can't use type in this as that converts it to object_type, meh
$query->filterIdentifier($page, 'object_id');
$query->filterType('wiki page');
// build query for es
$builder = new \Search_Elastic_QueryBuilder($esIndex);
$queryPart = $builder->build($query->getExpr());
$data = json_encode($queryPart);
$full = "{$this->elasticUri}{$this->indexName}/_search";
//$full = "{$elasticUri}{$indexName}/_validate/query?explain=true";
$client = \TikiLib::lib('tiki')->get_http_client($full);
$client->setRawBody($data);
$client->setMethod(\Laminas\Http\Request::METHOD_GET);
$client->setHeaders(['Content-Type: application/json']);
$response = $client->send();
$body = $response->getBody();
$decoded = json_decode($body, true);
if ($decoded && ! empty($decoded['hits']['hits'][0]['_source'])) {
return $decoded['hits']['hits'][0]['_source'];
} else {
return false;
}
}
}
// create the application and new console
$console = new Application();
$console->add(new ESRescueCommand());
$console->setDefaultCommand('rescue');
// run the command
$console->run();