'.
''.
''.
''. // will be set by javascript for next update
''. // will be set by javascript for next update
'
'.
'
'.i18n::translate('GEDCOM File:').'
'.
'
'.i18n::translate('Batch Update').':
';
if ($this->PLUGIN) {
$html.=' '.$this->PLUGIN->getDescription().'';
}
$html.='
';
// If a plugin is selected, display the details
if ($this->PLUGIN) {
$html.=$this->PLUGIN->getOptionsForm();
if (substr($this->action, -4)=='_all') {
// Reset - otherwise we might "undo all changes", which refreshes the
// page, which makes them all again!
$html.='';
} else {
if ($this->curr_xref) {
// Create an object, so we can get the latest version of the name.
$object=GedcomRecord::getInstance($this->curr_xref);
$object->setGedcomRecord($this->record);
$html.=
'
';
return $html.mod_print_footer();
}
// Constructor - initialise variables and validate user-input
function __construct() {
$this->plugins=self::getPluginList(); // List of available plugins
$this->plugin =safe_GET('plugin', array_keys($this->plugins)); // User parameters
$this->xref =safe_GET('xref', WT_REGEX_XREF);
$this->action =safe_GET('action');
$this->data =safe_GET('data');
// Don't do any processing until a plugin is chosen.
if ($this->plugin) {
$this->PLUGIN=new $this->plugin;
$this->PLUGIN->getOptions();
$this->getAllXrefs();
switch ($this->action) {
case '':
break;
case 'update':
$record=self::getLatestRecord($this->xref, $this->all_xrefs[$this->xref]);
if ($this->PLUGIN->doesRecordNeedUpdate($this->xref, $record)) {
$newrecord=$this->PLUGIN->updateRecord($this->xref, $record);
if ($newrecord!=$record) {
if ($newrecord) {
replace_gedrec($this->xref, WT_GED_ID, $newrecord, $this->PLUGIN->chan);
} else {
delete_gedrec($this->xref, WT_GED_ID);
}
}
}
$this->xref=$this->findNextXref($this->xref);
break;
case 'update_all':
foreach ($this->all_xrefs as $xref=>$type) {
$record=self::getLatestRecord($xref, $type);
if ($this->PLUGIN->doesRecordNeedUpdate($xref, $record)) {
$newrecord=$this->PLUGIN->updateRecord($xref, $record);
if ($newrecord!=$record) {
if ($newrecord) {
replace_gedrec($xref, WT_GED_ID, $newrecord, $this->PLUGIN->chan);
} else {
delete_gedrec($xref, WT_GED_ID);
}
}
}
}
$this->xref='';
return;
case 'delete':
$record=self::getLatestRecord($this->xref, $this->all_xrefs[$this->xref]);
if ($this->PLUGIN->doesRecordNeedUpdate($this->xref, $record)) {
delete_gedrec($this->xref, WT_GED_ID);
}
$this->xref=$this->findNextXref($this->xref);
break;
case 'delete_all':
foreach ($this->all_xrefs as $xref=>$type) {
$record=self::getLatestRecord($xref, $type);
if ($this->PLUGIN->doesRecordNeedUpdate($xref, $record)) {
delete_gedrec($xref, WT_GED_ID);
}
}
$xref->xref='';
return;
default:
// Anything else will be handled by the plugin
$this->PLUGIN->performAction($this->xref, $this->record, $this->action, $this->data);
break;
}
// Make sure that our requested record really does need updating.
// It may have been updated in another session, or may not have
// been specified at all.
if (array_key_exists($this->xref, $this->all_xrefs) &&
$this->PLUGIN->doesRecordNeedUpdate($this->xref, self::getLatestRecord($this->xref, $this->all_xrefs[$this->xref]))) {
$this->curr_xref=$this->xref;
}
// The requested record doesn't need updating - find one that does
if (!$this->curr_xref) {
$this->curr_xref=$this->findNextXref($this->xref);
}
if (!$this->curr_xref) {
$this->curr_xref=$this->findPrevXref($this->xref);
}
// If we've found a record to update, get details and look for the next/prev
if ($this->curr_xref) {
$this->record=self::getLatestRecord($this->curr_xref, $this->all_xrefs[$this->curr_xref]);
$this->prev_xref=$this->findPrevXref($this->curr_xref);
$this->next_xref=$this->findNextXref($this->curr_xref);
}
}
}
// Find the next record that needs to be updated
function findNextXref($xref) {
foreach (array_keys($this->all_xrefs) as $key) {
if ($key>$xref) {
$record=self::getLatestRecord($key, $this->all_xrefs[$key]);
if ($this->PLUGIN->doesRecordNeedUpdate($key, $record)) {
return $key;
}
}
}
return null;
}
// Find the previous record that needs to be updated
function findPrevXref($xref) {
foreach (array_reverse(array_keys($this->all_xrefs)) as $key) {
if ($key<$xref) {
$record=self::getLatestRecord($key, $this->all_xrefs[$key]);
if ($this->PLUGIN->doesRecordNeedUpdate($key, $record)) {
return $key;
}
}
}
return null;
}
function getAllXrefs() {
global $TBLPREFIX;
$sql=array();
$vars=array();
foreach ($this->PLUGIN->getRecordTypesToUpdate() as $type) {
switch ($type) {
case 'INDI':
$sql[]="SELECT i_id, 'INDI' FROM {$TBLPREFIX}individuals WHERE i_file=?";
$vars[]=WT_GED_ID;
break;
case 'FAM':
$sql[]="SELECT f_id, 'FAM' FROM {$TBLPREFIX}families WHERE f_file=?";
$vars[]=WT_GED_ID;
break;
case 'SOUR':
$sql[]="SELECT s_id, 'SOUR' FROM {$TBLPREFIX}sources WHERE s_file=?";
$vars[]=WT_GED_ID;
break;
case 'OBJE':
$sql[]="SELECT m_media, 'OBJE' FROM {$TBLPREFIX}media WHERE m_gedfile=?";
$vars[]=WT_GED_ID;
break;
default:
$sql[]="SELECT o_id, ? FROM {$TBLPREFIX}other WHERE o_type=? AND o_file=?";
$vars[]=$type;
$vars[]=$type;
$vars[]=WT_GED_ID;
break;
}
}
$this->all_xrefs=
WT_DB::prepare(implode(' UNION ', $sql).' ORDER BY 1 ASC')
->execute($vars)
->fetchAssoc();
}
// Scan the plugin directory for a list of plugins
static function getPluginList() {
$array=array();
$dir=dirname(__FILE__).'/plugins/';
$dir_handle=opendir($dir);
while ($file=readdir($dir_handle)) {
if (substr($file, -4)=='.php') {
require dirname(__FILE__).'/plugins/'.$file;
$class=basename($file, '.php').'_bu_plugin';
$array[$class]=new $class;
}
}
closedir($dir_handle);
return $array;
}
// Javascript that gets included on every page
static function getJavascript() {
return
''
;
}
// Create a submit button for our form
static function createSubmitButton($text, $xref, $action='', $data='') {
return
'';
}
// Get the current view of a record, allowing for pending changes
static function getLatestRecord($xref, $type) {
return find_gedcom_record($xref, WT_GED_ID);
}
}
// Each plugin should extend the base_plugin class, and implement these
// two functions:
//
// bool doesRecordNeedUpdate($xref, $gedrec)
// string updateRecord($xref, $gedrec)
//
class base_plugin {
var $chan=false; // User option; update change record
// Default is to operate on INDI records
function getRecordTypesToUpdate() {
return array('INDI');
}
// Default option is just the "don't update CHAN record"
function getOptions() {
$this->chan=safe_GET_bool('chan');
}
// Default option is just the "don't update CHAN record"
function getOptionsForm() {
return
'
'.i18n::translate('Update the CHAN record').':
'.
'
';
}
// Default buttons are update and update_all
function getActionButtons($xref) {
return array(
batch_update::createSubmitButton(i18n::translate('Update'), $xref, 'update'),
batch_update::createSubmitButton(i18n::translate('Update all'), $xref, 'update_all')
);
}
// Default previewer for plugins with no custom preview.
function getActionPreview($xref, $gedrec) {
$old_lines=preg_split('/[\r\n]+/', $gedrec);
$new_lines=preg_split('/[\r\n]+/', $this->updateRecord($xref, $gedrec));
// Find matching lines using longest-common-subsequence algorithm.
$lcs=self::LCS($old_lines, $new_lines, 0, count($old_lines)-1, 0, count($new_lines)-1);
$diff_lines=array();
$last_old=-1;
$last_new=-1;
while ($lcs) {
list($old, $new)=array_shift($lcs);
while ($last_old<$old-1) {
$diff_lines[]=self::decorateDeletedText($old_lines[++$last_old]);
}
while ($last_new<$new-1) {
$diff_lines[]=self::decorateInsertedText($new_lines[++$last_new]);
}
$diff_lines[]=$new_lines[$new];
$last_old=$old;
$last_new=$new;
}
while ($last_old'.self::createEditLinks(implode("\n", $diff_lines)).'';
}
// Longest Common Subsequence.
static function LCS($X, $Y, $x1, $x2, $y1, $y2) {
if ($x2-$x1>=0 && $y2-$y1>=0) {
if ($X[$x1]==$Y[$y1]) {
// Match at start of sequence
$tmp=self::LCS($X, $Y, $x1+1, $x2, $y1+1, $y2);
array_unshift($tmp, array($x1, $y1));
return $tmp;
} elseif ($X[$x2]==$Y[$y2]) {
// Match at end of sequence
$tmp=self::LCS($X, $Y, $x1, $x2-1, $y1, $y2-1);
array_push($tmp, array($x2, $y2));
return $tmp;
} else {
// No match. Look for subsequences
$tmp1=self::LCS($X, $Y, $x1, $x2, $y1, $y2-1);
$tmp2=self::LCS($X, $Y, $x1, $x2-1, $y1, $y2);
return count($tmp1) > count($tmp2) ? $tmp1 : $tmp2;
}
} else {
// One array is empty - end recursion
return array();
}
}
// Default handler for plugin with no custom actions.
function performAction($xref, $gedrec, $action, $data) {
}
// Decorate inserted/deleted text
static function decorateInsertedText($text) {
return ''.$text.'';
}
static function decorateDeletedText($text) {
return ''.$text.'';
}
// Converted gedcom links into editable links
static function createEditLinks($gedrec) {
return preg_replace(
"/@([^#@\n]+)@/m",
'@\\1@',
$gedrec
);
}
}
?>