'. ''. ''. ''. // will be set by javascript for next update ''. // will be set by javascript for next update ''. ''. ''; // 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.= '
'.i18n::translate('GEDCOM File:').'
'.i18n::translate('Batch Update').':'; if ($this->PLUGIN) { $html.='
'.$this->PLUGIN->getDescription().''; } $html.='

'. ''; } else { $html.=''; } } } $html.='
'. self::createSubmitButton(i18n::translate('< Previous'), $this->prev_xref). self::createSubmitButton(i18n::translate('Next >'), $this->next_xref). '

'.$object->getFullName().''. '

'. '
'.implode('
',$this->PLUGIN->getActionButtons($this->curr_xref, $this->record)).'
'. '
'. $this->PLUGIN->getActionPreview($this->curr_xref, $this->record); '
'.i18n::translate('Nothing found.').'
'; 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 ); } } ?>