Пример #1
0
 public static function addWhitelistPage($pageTitle)
 {
     $key = PropagationManager::getPageKey($pageTitle);
     if ($key) {
         if (!is_array(PropagationManager::$whitelistTitles)) {
             PropagationManager::$whitelistTitles = array();
         }
         PropagationManager::$whitelistTitles[$key] = 1;
     } else {
         error_log("addWhitelistPage bad title");
     }
 }
Пример #2
0
 public function propagateSINMoveDeleteUndelete($thisTitle, $tag, $oldTitleString, $newTitleString, &$propagatedData, &$text, &$textChanged)
 {
     $result = true;
     $newTitle = $newTitleString ? Title::newFromText($newTitleString, $tag == 'person' ? NS_PERSON : NS_FAMILY) : null;
     foreach ($propagatedData['images'] as $i) {
         $imageTitle = Title::newFromText((string) $i['filename'], NS_IMAGE);
         PropagationManager::addPropagatedAction($thisTitle, 'delimage', $imageTitle);
         if ($newTitle) {
             PropagationManager::addPropagatedAction($newTitle, 'addimage', $imageTitle);
         }
         // don't need to check propagated action before calling updateImage, because propagateSINMoveDeleteUndelete is never part of a loop
         $result = $result && $this->updateImage($thisTitle, $imageTitle, $tag, $oldTitleString, $newTitleString, $propagatedData, $text, $textChanged);
     }
     return $result;
 }
Пример #3
0
 /**
  * Propagate move, delete, or undelete to other articles if necessary
  *
  * @param String $newTitleString null in case of delete; same as this title string in case of undelete
  * @param String $text text of article
  * @param bool $textChanged set to true if we change the text
  * @return bool true if success
  */
 protected function propagateMoveDeleteUndelete($newTitleString, $newNs, &$text, &$textChanged)
 {
     global $wgESINHandler;
     $result = true;
     $newTitle = $newTitleString ? Title::newFromText($newTitleString, NS_FAMILY) : null;
     // if we're undeleting, add additional people from WLH, getting updated person data
     if ($this->titleString == $newTitleString) {
         $wlh = simplexml_load_string($this->getPageTextFromWLH(false));
         // get text for all people
         // TODO is propagateEditData called after Undelete? if so, then we could get updated person attributes there
         $personText = $this->getPersonData($wlh->husband, $this->xml->husband, 'husband') . $this->getPersonData($wlh->wife, $this->xml->wife, 'wife') . $this->getPersonData($wlh->child, $this->xml->child, 'child') . $wgESINHandler->getImageData($wlh->image, $this->xml->image);
         // update text: replace old family information with new
         $text = preg_replace("\$<(husband|wife|child|image) [^>]*>\n\$", '', $text);
         $text = preg_replace('$</family>$', StructuredData::protectRegexReplace($personText . '</family>'), $text, 1);
         $this->xml = StructuredData::getXml($this->tagName, $text);
         $textChanged = true;
     }
     // get data to propagate
     $propagatedData = Family::getPropagatedData($this->xml);
     foreach ($propagatedData['husbands'] as $p) {
         $personTitle = Title::newFromText((string) $p, NS_PERSON);
         PropagationManager::addPropagatedAction($this->title, 'delspouse', $personTitle);
         if ($newTitle) {
             PropagationManager::addPropagatedAction($newTitle, 'addspouse', $personTitle);
         }
         // don't need to check propagated action before calling updatePerson, because propagateMoveDeleteUndelete is never part of a loop
         $result = $result && $this->updatePerson($personTitle, 'husband', 'spouse_of_family', $newTitleString, $text, $textChanged);
     }
     foreach ($propagatedData['wives'] as $p) {
         $personTitle = Title::newFromText((string) $p, NS_PERSON);
         PropagationManager::addPropagatedAction($this->title, 'delspouse', $personTitle);
         if ($newTitle) {
             PropagationManager::addPropagatedAction($newTitle, 'addspouse', $personTitle);
         }
         // don't need to check propagated action before calling updatePerson, because propagateMoveDeleteUndelete is never part of a loop
         $result = $result && $this->updatePerson($personTitle, 'wife', 'spouse_of_family', $newTitleString, $text, $textChanged);
     }
     foreach ($propagatedData['children'] as $p) {
         $personTitle = Title::newFromText((string) $p, NS_PERSON);
         PropagationManager::addPropagatedAction($this->title, 'delchild', $personTitle);
         if ($newTitle) {
             PropagationManager::addPropagatedAction($newTitle, 'addchild', $personTitle);
         }
         // don't need to check propagated action before calling updatePerson, because propagateMoveDeleteUndelete is never part of a loop
         $result = $result && $this->updatePerson($personTitle, 'child', 'child_of_family', $newTitleString, $text, $textChanged);
     }
     if (StructuredData::removeDuplicateLinks('husband|wife|child', $text)) {
         $textChanged = true;
     }
     $result = $result && $wgESINHandler->propagateSINMoveDeleteUndelete($this->title, 'family', $this->titleString, $newTitleString, $propagatedData, $text, $textChanged);
     if (!$result) {
         error_log("ERROR! Family move/delete/undelete not propagated: {$this->titleString} -> " . ($newTitleString ? $newTitleString : "[delete]") . "\n");
     }
     return $result;
 }
Пример #4
0
 public function doMerge()
 {
     global $wgOut, $wgUser;
     $skin =& $wgUser->getSkin();
     if ($this->isGedcom()) {
         $editFlags = EDIT_UPDATE;
         $mergeText = 'updated';
     } else {
         // create a mergelog record
         $mergeScore = $this->getMergeScore();
         $isTrustedUser = CompareForm::isTrustedMerger($wgUser, false);
         $isTrustedMerge = MergeForm::isTrustedMerge($mergeScore, $isTrustedUser);
         $mergeLogId = $this->logMerge($mergeScore, $isTrustedMerge);
         wfDebug("MERGESCORE mergeLogId={$mergeLogId} total score={$mergeScore}\n");
         if (!$isTrustedUser && $mergeScore < self::$LOW_MATCH_THRESHOLD) {
             error_log("WARNING suspect merge: id={$mergeLogId} user={$wgUser->getName()} score={$mergeScore}");
         }
         $editFlags = EDIT_UPDATE | EDIT_FORCE_BOT;
         $mergeText = 'merged';
     }
     // get merging people and families
     // add merging people and families to blacklist so propagation doesn't also try to update them
     $mergingPeople = array();
     $mergingFamilies = array();
     for ($m = 0; $m < count($this->data); $m++) {
         for ($p = 0; $p < count($this->data[$m]); $p++) {
             $titleString = $this->data[$m][$p]['title'];
             if ($this->namespace == 'Family' && $m == 0) {
                 if ($p > 0) {
                     $mergingFamilies[$titleString] = $this->data[$m][0]['title'];
                 }
                 $ns = NS_FAMILY;
             } else {
                 if ($p > 0) {
                     $mergingPeople[$titleString] = $this->data[$m][0]['title'];
                 }
                 $ns = NS_PERSON;
             }
             if (!GedcomUtil::isGedcomTitle($titleString)) {
                 $title = Title::newFromText($titleString, $ns);
                 PropagationManager::addBlacklistPage($title);
             }
         }
     }
     $output = "<H2>Pages {$mergeText} successfully</H2>";
     $output .= $this->getWarnings();
     $output .= '<ul>';
     $outputRows = array();
     $emptyRequest = new FauxRequest(array(), true);
     $mergeCmtSuffix = $this->isGedcom() ? '' : " - [[Special:ReviewMerge/{$mergeLogId}|review/undo]]";
     if ($this->namespace == 'Family' && !$this->isGedcom()) {
         $t = Title::newFromText($this->data[0][0]['title'], NS_FAMILY);
         $mergeCmtFamily = $this->namespace == 'Family' ? " in merge of [[{$t->getPrefixedText()}]]" : '';
     } else {
         $mergeCmtFamily = '';
     }
     // backwards, because you must merge family last, so that propagated person data in family xml is correct
     // and so that mergeCmtFamily can be cleared at the end and mergeSummary and mergeTargetTitle are correct after the for loop
     for ($m = count($this->data) - 1; $m >= 0; $m--) {
         $requestData = array();
         $contents = '';
         $talkContents = '';
         $outputRow = '';
         $mainOutput = '';
         $talkOutput = '';
         $nameCount = $eventCount = $sourceCount = $imageCount = $noteCount = $husbandCount = $wifeCount = $childrenCount = $parentFamilyCount = $spouseFamilyCount = 0;
         $primaryNameFound = $primaryImageFound = $birthFound = $christeningFound = $deathFound = $burialFound = $marriageFound = false;
         if ($this->namespace == 'Family' && $m == 0) {
             $mergeTargetNs = NS_FAMILY;
             $mergeTargetTalkNs = NS_FAMILY_TALK;
             $mergeCmtFamily = '';
         } else {
             $mergeTargetNs = NS_PERSON;
             $mergeTargetTalkNs = NS_PERSON_TALK;
         }
         $mergeTargetTitle = Title::newFromText($this->data[$m][0]['title'], $mergeTargetNs);
         if ($mergeTargetTitle->getNamespace() != $mergeTargetNs) {
             error_log("Merge glitch:{$mergeTargetNs}:{$this->data[$m][0]['title']}:{$mergeTargetTitle->getNamespace()}:");
         }
         $mergeTargetTalkTitle = Title::newFromText($this->data[$m][0]['title'], $mergeTargetTalkNs);
         $mergeSummary = '';
         $talkMergeSummary = '';
         $keepKeys = $this->generateKeepKeys($this->data[$m], $this->add[$m], $this->key[$m]);
         $notesMap = array();
         $noteAdoptions = array();
         $this->generateMapAdoptions('notes', 'N', $mergeTargetNs, $this->data[$m], $this->add[$m], $this->key[$m], $keepKeys, $notesMap, $noteAdoptions);
         $sourcesMap = array();
         $sourceAdoptions = array();
         $this->generateMapAdoptions('sources', 'S', $mergeTargetNs, $this->data[$m], $this->add[$m], $this->key[$m], $keepKeys, $sourcesMap, $sourceAdoptions);
         $imagesMap = array();
         $imageAdoptions = array();
         $this->generateMapAdoptions('images', 'I', $mergeTargetNs, $this->data[$m], $this->add[$m], $this->key[$m], $keepKeys, $imagesMap, $imageAdoptions);
         // get request data for merge target
         for ($p = 0; $p < count($this->data[$m]); $p++) {
             if ($this->isMergeable($this->data[$m][$p])) {
                 $this->addNotesToRequestData($requestData, $keepKeys[$p], $noteCount, $notesMap[$p], $this->data[$m][$p]['notes']);
                 $this->addImagesToRequestData($requestData, $keepKeys[$p], $imageCount, $imagesMap[$p], $primaryImageFound, $this->data[$m][$p]['images']);
                 $this->addSourcesToRequestData($requestData, $keepKeys[$p], $sourceCount, $sourcesMap[$p], $notesMap[$p], $imagesMap[$p], $this->data[$m][$p]['sources'], $noteAdoptions, $imageAdoptions);
                 $this->addEventsToRequestData($requestData, $keepKeys[$p], $eventCount, $mergeTargetNs == NS_PERSON ? Person::$STD_EVENT_TYPES : Family::$STD_EVENT_TYPES, $birthFound, $christeningFound, $deathFound, $burialFound, $marriageFound, $notesMap[$p], $imagesMap[$p], $sourcesMap[$p], $this->data[$m][$p]['events'], $noteAdoptions, $sourceAdoptions, $imageAdoptions);
                 if ($mergeTargetNs == NS_PERSON) {
                     $this->addNamesToRequestData($requestData, $keepKeys[$p], $nameCount, $primaryNameFound, $notesMap[$p], $sourcesMap[$p], $this->data[$m][$p]['names'], $noteAdoptions, $sourceAdoptions);
                     $this->addFamilyToRequestData($requestData, $mergingFamilies, 'child_of_family', $parentFamilyCount, $this->data[$m][$p]['child_of_families']);
                     $this->addFamilyToRequestData($requestData, $mergingFamilies, 'spouse_of_family', $spouseFamilyCount, $this->data[$m][$p]['spouse_of_families']);
                 } else {
                     $this->addFamilyMembersToRequestData($requestData, $mergingPeople, 'husband', $husbandCount, $this->data[$m][$p]['husbands']);
                     $this->addFamilyMembersToRequestData($requestData, $mergingPeople, 'wife', $wifeCount, $this->data[$m][$p]['wives']);
                     $this->addFamilyMembersToRequestData($requestData, $mergingPeople, 'child', $childrenCount, $this->data[$m][$p]['children']);
                 }
                 $pageContents = $this->mapContents($sourcesMap[$p], $imagesMap[$p], $notesMap[$p], $this->data[$m][$p]['contents']);
                 $this->addContents($contents, $keepKeys[$p], $pageContents);
                 if ($p > 0) {
                     if ($mergeSummary) {
                         $mergeSummary .= ', ';
                     }
                     if ($mainOutput) {
                         $mainOutput .= ', ';
                     }
                     if ($this->data[$m][$p]['gedcom']) {
                         $mergeSummary .= 'gedcom';
                         $mainOutput .= htmlspecialchars(($mergeTargetNs == NS_FAMILY ? 'Family:' : 'Person:') . $this->data[$m][$p]['title']);
                     } else {
                         $title = Title::newFromText($this->data[$m][$p]['title'], $mergeTargetNs);
                         $mergeSummary .= "[[" . $title->getPrefixedText() . "]]";
                         $mainOutput .= $skin->makeKnownLinkObj($title, htmlspecialchars($title->getPrefixedText()), 'redirect=no');
                     }
                 }
             }
         }
         // redirect other pages to merge target
         $redir = "#REDIRECT [[" . $mergeTargetTitle->getPrefixedText() . "]]";
         $talkRedir = "#REDIRECT [[" . $mergeTargetTalkTitle->getPrefixedText() . "]]";
         for ($p = 1; $p < count($this->data[$m]); $p++) {
             if (!$this->data[$m][$p]['gedcom']) {
                 $obj = $this->data[$m][$p]['object'];
                 $comment = $this->makeComment($this->userComment, "merge into [[" . $mergeTargetTitle->getPrefixedText() . "]]" . $mergeCmtFamily, $mergeCmtSuffix);
                 $obj->editPage($emptyRequest, $redir, $comment, $editFlags, false);
                 // redir talk page as well
                 if ($this->data[$m][$p]['talkrevid']) {
                     // if talk page exists
                     $talkTitle = Title::newFromText($this->data[$m][$p]['title'], $mergeTargetTalkNs);
                     $article = new Article($talkTitle, 0);
                     if ($article) {
                         $this->addTalkContents($talkContents, $talkTitle, $article->fetchContent());
                         if ($talkMergeSummary) {
                             $talkMergeSummary .= ', ';
                         }
                         if ($talkOutput) {
                             $talkOutput .= ', ';
                         }
                         $talkMergeSummary .= "[[" . $talkTitle->getPrefixedText() . "]]";
                         $talkOutput .= $skin->makeKnownLinkObj($talkTitle, htmlspecialchars($talkTitle->getPrefixedText()), 'redirect=no');
                         $comment = $this->makeComment($this->userComment, "merge into [[" . $mergeTargetTalkTitle->getPrefixedText() . "]]" . $mergeCmtFamily, $mergeCmtSuffix);
                         $article->doEdit($talkRedir, $comment, $editFlags);
                     }
                 }
             }
         }
         // update merge target talk
         if ($talkContents) {
             $article = new Article($mergeTargetTalkTitle, 0);
             if ($article) {
                 $mergeTargetTalkContents = $article->fetchContent();
                 if ($mergeTargetTalkContents) {
                     $mergeTargetTalkContents = rtrim($mergeTargetTalkContents) . "\n\n";
                 }
                 $comment = $this->makeComment($this->userComment, 'merged with ' . $talkMergeSummary . $mergeCmtFamily, $mergeCmtSuffix);
                 $article->doEdit($mergeTargetTalkContents . $talkContents, $comment, $editFlags);
                 if ($this->addWatches) {
                     StructuredData::addWatch($wgUser, $article, true);
                 }
             }
             $outputRow .= '<li>Merged ' . $talkOutput . ' into ' . $skin->makeKnownLinkObj($mergeTargetTalkTitle, htmlspecialchars($mergeTargetTalkTitle->getPrefixedText())) . "</li>";
         }
         $obj = $this->data[$m][0]['object'];
         if ($mergeTargetNs == NS_PERSON) {
             Person::addGenderToRequestData($requestData, $this->data[$m][0]['gender']);
         } else {
             // family
             $obj->isMerging(true);
             // to read propagated data from person pages, not from prev family revision
         }
         // update merge target
         $req = new FauxRequest($requestData, true);
         $comment = $this->makeComment($this->userComment, ($mergeSummary == 'gedcom' ? 'Add data from gedcom' : 'merged with ' . $mergeSummary) . $mergeCmtFamily, $mergeCmtSuffix);
         $obj->editPage($req, $contents, $comment, $editFlags, $this->addWatches);
         $outputRow .= '<li>Merged ' . $mainOutput . ' into ' . $skin->makeKnownLinkObj($mergeTargetTitle, htmlspecialchars($mergeTargetTitle->getPrefixedText())) . "</li>";
         $outputRows[] = $outputRow;
     }
     // add log and recent changes
     if (!$this->isGedcom()) {
         if (!$mergeSummary) {
             $mergeSummary = 'members of other families';
         }
         $mergeComment = 'Merge [[' . $mergeTargetTitle->getPrefixedText() . ']] and ' . $mergeSummary;
         $log = new LogPage('merge', false);
         $t = Title::makeTitle(NS_SPECIAL, "ReviewMerge/{$mergeLogId}");
         $log->addEntry('merge', $t, $mergeComment);
         RecentChange::notifyLog(wfTimestampNow(), $t, $wgUser, $mergeComment, '', 'merge', 'merge', $t->getPrefixedText(), $mergeComment, '', $isTrustedMerge, 0);
     }
     $nonmergedPages = $this->getNonmergedPages();
     $output .= join("\n", array_reverse($outputRows)) . '</ul>' . ($nonmergedPages ? '<p>In addition to the people listed above, the following have also been included in the target family' . ($this->isGedcom() ? '<br/>(GEDCOM people listed will be added when the GEDCOM is imported)' : '') . $nonmergedPages . "</p>\n" : '') . ($this->isGedcom() ? '' : '<p>' . $skin->makeKnownLinkObj(Title::makeTitle(NS_SPECIAL, 'ReviewMerge/' . $mergeLogId), htmlspecialchars("Review/undo merge")) . '<br>' . $skin->makeKnownLinkObj(Title::makeTitle(NS_SPECIAL, 'ShowDuplicates'), htmlspecialchars("Show more duplicates")) . '</p>');
     return $output;
 }
Пример #5
0
 /**
  * Propagate an article rollback
  * Sets xml property and calls the abstract function propagateRollbackData($title)
  * @param Article $article contains text being replaced
  * @return bool true if propagate succeeded
  */
 public function propagateRollback($article)
 {
     global $wgUser, $wrBotUserID;
     $result = true;
     // we do propagate bot edits
     //	   if ($wgUser->getID() != $wrBotUserID) {
     $title = $article->getTitle();
     $oldText =& $article->fetchContent();
     // $article contains the text being replaced; $rollbackRevision contains the new text
     $rollbackRevision = StructuredData::getRevision($title, false, true);
     $text =& $rollbackRevision->getText();
     $this->xml = StructuredData::getXml($this->tagName, $text);
     $textChanged = false;
     $result = $this->propagateEditData($oldText, $text, $textChanged);
     if ($result && $textChanged) {
         PropagationManager::enablePropagation(false);
         $result = $article->doEdit($text, self::PROPAGATE_MESSAGE, PROPAGATE_EDIT_FLAGS);
         PropagationManager::enablePropagation(true);
     }
     //	   }
     return $result;
 }
Пример #6
0
 /**
  * Propagate move, delete, or undelete to other articles if necessary
  *
  * @param String $newTitleString null in case of delete; same as this title string in case of undelete
  * @param String $text text of article
  * @param bool $textChanged set to true if we change the text
  * @return bool true if success
  */
 protected function propagateMoveDeleteUndelete($newTitleString, $newNs, &$text, &$textChanged)
 {
     global $wgESINHandler;
     $result = true;
     $newTitle = $newTitleString ? Title::newFromText($newTitleString, NS_PERSON) : null;
     // if we're undeleting, add additional families from WLH
     if ($this->titleString == $newTitleString) {
         $wlh = simplexml_load_string($this->getPageTextFromWLH(false));
         // get text for all families
         $familyText = $this->getFamilyData($wlh->child_of_family, $this->xml->child_of_family, 'child_of_family') . $this->getFamilyData($wlh->spouse_of_family, $this->xml->spouse_of_family, 'spouse_of_family') . $wgESINHandler->getImageData($wlh->image, $this->xml->image);
         // update text: replace old family information with new
         $text = preg_replace("\$<((child|spouse)_of_family|image) [^>]*>\n\$", '', $text);
         $text = preg_replace('$</person>$', StructuredData::protectRegexReplace($familyText . '</person>'), $text, 1);
         $this->xml = StructuredData::getXml($this->tagName, $text);
         $textChanged = true;
     }
     // get data to propagate
     $propagatedData = Person::getPropagatedData($this->xml);
     foreach ($propagatedData['parentFamilies'] as $f) {
         $familyTitle = Title::newFromText((string) $f, NS_FAMILY);
         PropagationManager::addPropagatedAction($this->title, 'delchild_of_family', $familyTitle);
         if ($newTitle) {
             PropagationManager::addPropagatedAction($newTitle, 'addchild_of_family', $familyTitle);
         }
         // don't need to check propagated action before calling updateFamily, because propagateMoveDeleteUndelete is never part of a loop
         $result = $result && $this->updateFamily($familyTitle, 'child_of_family', 'child', $newTitleString, $propagatedData, $text, $textChanged);
     }
     $spouseTag = $propagatedData['gender'] == 'F' ? 'wife' : 'husband';
     foreach ($propagatedData['spouseFamilies'] as $f) {
         $familyTitle = Title::newFromText((string) $f, NS_FAMILY);
         PropagationManager::addPropagatedAction($this->title, 'delspouse_of_family', $familyTitle);
         if ($newTitle) {
             PropagationManager::addPropagatedAction($newTitle, 'addspouse_of_family', $familyTitle);
         }
         // don't need to check propagated action before calling updateFamily, because propagateMoveDeleteUndelete is never part of a loop
         $result = $result && $this->updateFamily($familyTitle, 'spouse_of_family', $spouseTag, $newTitleString, $propagatedData, $text, $textChanged);
     }
     if (StructuredData::removeDuplicateLinks('child_of_family|spouse_of_family', $text)) {
         $textChanged = true;
     }
     $result = $result && $wgESINHandler->propagateSINMoveDeleteUndelete($this->title, 'person', $this->titleString, $newTitleString, $propagatedData, $text, $textChanged);
     if (!$result) {
         error_log("ERROR! Person move/delete/undelete not propagated: {$this->titleString} -> " . ($newTitleString ? $newTitleString : "[delete]") . "\n");
     }
     return $result;
 }
Пример #7
0
    public function unmerge()
    {
        global $wgUser;
        $nonFamilyPages = array();
        // contains revid, next_revid, title
        $familyPages = array();
        // ditto
        $manualPages = array();
        // ditto
        $unchangedPages = array();
        // ditto
        $dbw =& wfGetDB(DB_MASTER);
        // break into different arrays
        $seenTitles = array();
        foreach ($this->merges as $merge) {
            $fields = explode('|', $merge);
            $role = $fields[0];
            $revidSets = explode('#', $fields[1]);
            foreach ($revidSets as $revidSet) {
                if ($revidSet) {
                    $revids = explode('/', $revidSet);
                    foreach ($revids as $revid) {
                        if ($revid) {
                            // get two following revisions
                            $rows = $dbw->query('SELECT r2.rev_page, r2.rev_id, r2.rev_comment FROM revision AS r1, revision AS r2' . ' WHERE r1.rev_id = ' . $revid . ' AND r1.rev_page = r2.rev_page AND r2.rev_id > ' . $revid . ' ORDER BY r2.rev_id LIMIT 2');
                            $cnt = 0;
                            $cmt = '';
                            $nextRevid = '';
                            $pageId = '';
                            while ($row = $dbw->fetchObject($rows)) {
                                $cnt++;
                                if ($cnt == 1) {
                                    $pageId = $row->rev_page;
                                    $nextRevid = $row->rev_id;
                                    $cmt = $row->rev_comment;
                                }
                            }
                            $dbw->freeResult($rows);
                            if ($cnt == 0) {
                                $revision = Revision::newFromId($revid);
                                if ($revision) {
                                    $title = $revision->getTitle();
                                } else {
                                    // page must have been deleted
                                    $title = null;
                                }
                            } else {
                                $title = Title::newFromId($pageId);
                            }
                            // TODO if title doesn't exist, then unmerged pages could have red links; oh well
                            if ($title && !@$seenTitles[$title->getPrefixedText()]) {
                                $seenTitles[$title->getPrefixedText()] = 1;
                                $entry = array('revid' => $revid, 'next_revid' => $nextRevid, 'title' => $title);
                                if ($cnt == 0 || strpos($cmt, '[[Special:ReviewMerge/' . $this->mergeId . '|') === false) {
                                    $unchangedPages[] = $entry;
                                    // page was not edited in the merge
                                } else {
                                    if ($cnt > 1) {
                                        $manualPages[] = $entry;
                                    } else {
                                        if ($role == 'Family') {
                                            PropagationManager::addBlacklistPage($title);
                                            $familyPages[] = $entry;
                                        } else {
                                            PropagationManager::addBlacklistPage($title);
                                            $nonFamilyPages[] = $entry;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        // update nonFamilyPages
        foreach ($nonFamilyPages as $page) {
            $this->revert($page, false);
        }
        // update familyPages
        foreach ($familyPages as $page) {
            $this->revert($page, true);
        }
        // update mergelog
        $dbw->update('mergelog', array('ml_unmerge_user' => $wgUser->getID(), 'ml_unmerge_timestamp' => $dbw->timestamp(wfTimestampNow())), array('ml_id' => $this->mergeId), 'ReviewForm::unmerge');
        // add log and RC
        $mergeComment = 'Unmerge [[' . $this->mergeTitle->getPrefixedText() . ']]' . ($this->comment ? ' - ' . $this->comment : '');
        $log = new LogPage('merge', false);
        $t = Title::makeTitle(NS_SPECIAL, "ReviewMerge/{$this->mergeId}");
        $log->addEntry('unmerge', $t, $mergeComment);
        RecentChange::notifyLog(wfTimestampNow(), $t, $wgUser, $mergeComment, '', 'merge', 'unmerge', $t->getPrefixedText(), $mergeComment, '', 0, 0);
        // list pages
        $output = '';
        $skin =& $wgUser->getSkin();
        if (count($manualPages) == 0) {
            $output .= "<h2>Unmerge successful</h2>\n";
        } else {
            $output .= "<h2>Unmerge partially completed</h2><p><b><font color=\"red\">The following page(s) must still be unmerged</font></b></p><ul>";
            foreach ($manualPages as $page) {
                $output .= "<li>" . htmlspecialchars($page['title']->getPrefixedText()) . ' <b>' . $skin->makeKnownLinkObj($page['title'], 'changes to undo', 'diff=' . $page['next_revid'] . '&oldid=' . $page['revid']) . '</b> &nbsp;=>&nbsp; <b>' . $skin->makeKnownLinkObj($page['title'], 'edit', 'action=edit') . "</b></li>\n";
                error_log("Unmerge may be needed: {$page['title']->getPrefixedText()} id={$this->mergeId}");
            }
            $output .= <<<END
</ul>
<p>For each page listed above, click on the <b>changes to undo</b> link to see what changes need to be manually undone:
<ol>
<li>For each item that was removed in the merge (listed on the left side of the screen in yellow), you need to add that item back to the page.</li>
<li>For each item that was added in the merge (listed on the right side of the screen in green), you need to remove that item from the page.</li>
</ol></p>
<p>Once you know which items need to be added/removed, click on the <b>edit</b> link to edit the page and add/remove the items.</p>
<p>Additional notes:
<ul>
<li>Some of the items may have been added/removed already.</li>
<li>You can ignore place changes that simply fill in missing place levels.</li>
<li>When reviewing changes to Family pages, you can ignore changes to personal information (birth, death, etc.) associated with the family members: <i>husband</i>, <i>wife</i>, or <i>child</i>.
Just re-add family members that were removed in the merge, and remove ones that were added.</li>
</ul></p>
<p>If you have questions, send an email to <b>solveig@quass.org</b> containing the titles of the pages listed above and the URL appearing at the top of your browser window and we will finish the unmerge for you.</p>
<p>&nbsp;</p>
END;
        }
        if (count($familyPages) + count($nonFamilyPages) + count($unchangedPages) > 0) {
            $output .= "<p><b>The following pages have been successfully unmerged:</b></p><ul>\n";
            foreach ($familyPages as $page) {
                $output .= "<li>" . $skin->makeKnownLinkObj($page['title']) . "</li>\n";
            }
            foreach ($nonFamilyPages as $page) {
                $output .= "<li>" . $skin->makeKnownLinkObj($page['title']) . "</li>\n";
            }
            //			$output .= '</ul>';
            //		}
            //		if (count($unchangedPages) > 0) {
            //			$output .= "<p><b>The following pages were not merged and so did not need to be unmerged:</b></p><ul>\n";
            foreach ($unchangedPages as $page) {
                $output .= "<li>" . $skin->makeKnownLinkObj($page['title']) . "</li>\n";
            }
            $output .= "</ul>";
        }
        return $output;
    }
Пример #8
0
 /**
  * Run a refreshLinks job
  * @return boolean success
  */
 function run()
 {
     global $wgUser, $wgTitle, $wrIsTreeDeletion;
     $status = FTE_SUCCESS;
     $user = $this->params['user'];
     $wgUser = User::newFromName($user);
     $treeId = $this->params['tree_id'];
     if ($treeId == 9662) {
         return false;
     }
     $delPages = $this->params['delete_pages'] == 1;
     $wgTitle = $this->title;
     // FakeTitle (the default) generates errors when accessed, and sometimes I log wgTitle, so set it to something else
     $dbw =& wfGetDB(DB_MASTER);
     $dbw->begin();
     $dbw->ignoreErrors(true);
     if ($delPages) {
         // Delete the page if it is in this tree
         //   and is in one of the 4 deletable namespaces
         //   and nobody else is watching the page
         //   and it is not in another of the users trees
         // Keep this query in sync with SpecialTreeDeletionImpact.php
         $sql = 'SELECT fp_namespace, fp_title FROM familytree_page AS fp1' . ' WHERE fp_tree_id=' . $dbw->addQuotes($treeId) . ' and fp_namespace in (' . NS_IMAGE . ',' . NS_PERSON . ',' . NS_FAMILY . ',' . NS_MYSOURCE . ')' . ' and not exists (SELECT 1 FROM watchlist WHERE wl_namespace = fp_namespace and wl_title = fp_title and wl_user <> fp_user_id)' . ' and not exists (SELECT 1 FROM familytree_page AS fp2 WHERE fp2.fp_namespace = fp1.fp_namespace and fp2.fp_title = fp1.fp_title and fp2.fp_user_id = fp1.fp_user_id and fp2.fp_tree_id <> fp1.fp_tree_id)';
         $rows = $dbw->query($sql);
         $errno = $dbw->lastErrno();
         if ($errno > 0) {
             $status = FTE_DB_ERROR;
         } else {
             if ($rows !== false) {
                 $titles = array();
                 while ($row = $dbw->fetchObject($rows)) {
                     $title = Title::makeTitle($row->fp_namespace, $row->fp_title);
                     $talkTitle = $title->getTalkPage();
                     if ($title->exists()) {
                         $titles[] = $title;
                         PropagationManager::addBlacklistPage($title);
                     }
                     if ($talkTitle->exists()) {
                         $titles[] = $talkTitle;
                     }
                 }
                 $dbw->freeResult($rows);
                 $wrIsTreeDeletion = true;
                 foreach ($titles as $title) {
                     $status = ftDelPage($title, false);
                     if ($status == FTE_NOT_AUTHORIZED) {
                         $this->error = "While deleting {$treeId} tried to delete a page not authorized to delete: " . $title->getPrefixedText() . "\n";
                     }
                     if ($status != FTE_SUCCESS) {
                         break;
                     }
                 }
             }
         }
         // remove remaining pages from watchlist
         if ($status == FTE_SUCCESS) {
             $sql = 'SELECT fp_namespace, fp_title FROM familytree_page AS fp1' . ' WHERE fp_tree_id=' . $dbw->addQuotes($treeId) . ' and not exists (SELECT 1 FROM familytree_page AS fp2 WHERE fp2.fp_namespace = fp1.fp_namespace and fp2.fp_title = fp1.fp_title and fp2.fp_user_id = fp1.fp_user_id and fp2.fp_tree_id <> fp1.fp_tree_id)';
             $rows = $dbw->query($sql);
             $errno = $dbw->lastErrno();
             if ($errno > 0) {
                 $status = FTE_DB_ERROR;
             } else {
                 if ($rows !== false) {
                     while ($row = $dbw->fetchObject($rows)) {
                         $title = Title::makeTitle($row->fp_namespace, $row->fp_title);
                         $wgUser->removeWatch($title);
                     }
                     $dbw->freeResult($rows);
                 }
             }
         }
         // remove familytree_page's
         //   If we delete pages that are unwatched by others but in someone else's tree, then this code won't delete them from the others' trees
         //   We need to ensure that all pages in trees are watched.
         if ($status == FTE_SUCCESS) {
             $dbw->delete('familytree_page', array('fp_tree_id' => $treeId));
             $errno = $dbw->lastErrno();
             if ($errno > 0) {
                 $status = FTE_DB_ERROR;
             }
         }
         if ($status == FTE_SUCCESS) {
             // remove familytree_data's
             $dbw->delete('familytree_data', array('fd_tree_id' => $treeId));
             $errno = $dbw->lastErrno();
             if ($errno > 0) {
                 $status = FTE_DB_ERROR;
             }
             // keep familytree_gedcom in case we want to look at it later
         }
         if ($status == FTE_SUCCESS) {
             $dbw->commit();
             return true;
         } else {
             $dbw->rollback();
             if (!$this->error) {
                 $this->error = "Error deleting tree; status={$status}\n";
             }
             return false;
         }
     }
 }
Пример #9
0
/**
 * Generate family tree page
 *
 * @param unknown_type $args user, name, ns, title
 * @return GE_SUCCESS, GE_INVALID_ARG, GE_NOT_LOGGED_IN, GE_NOT_AUTHORIZED, GE_NOT_FOUND, GE_DUP_KEY, GE_DB_ERROR
 */
function wfGenerateFamilyTreePage($args)
{
    global $wgUser, $wgAjaxCachePolicy, $wrBotUserID, $wrIsGedcomUpload;
    // set cache policy
    $wgAjaxCachePolicy->setPolicy(0);
    $status = GE_SUCCESS;
    $ns = '';
    $text = '';
    $oldText = '';
    $titleString = '';
    $editFlags = 0;
    $wrIsGedcomUpload = true;
    if (!$wgUser->isLoggedIn()) {
        $status = GE_NOT_LOGGED_IN;
    } else {
        if (wfReadOnly() || $wgUser->getID() != $wrBotUserID) {
            $status = GE_NOT_AUTHORIZED;
        } else {
            $xml = simplexml_load_string($args);
            $ns = (int) $xml['namespace'];
            $titleString = (string) $xml['title'];
            PropagationManager::setWhitelist();
            // only pages to propagate to are on the whitelist
            $existingTitles = (string) $xml['existing_titles'];
            if ($existingTitles) {
                $existingTitles = explode('|', $existingTitles);
                foreach ($existingTitles as $existingTitle) {
                    PropagationManager::addWhitelistPage(Title::newFromText($existingTitle));
                }
            }
            $treeId = (int) $xml['tree_id'];
            $uid = (string) $xml['uid'];
            //   	wfDebug("wfGenerateFamilyTreePage ns=$ns title=$titleString treeId=$treeId\n");
            if (!$titleString || !$treeId) {
                //wfDebug("wfGenerate parmerr $treeId:$titleString\n");
                $status = GE_INVALID_ARG;
            }
        }
    }
    if ($status == GE_SUCCESS) {
        $dbr =& wfGetDB(DB_SLAVE);
        $dbr->ignoreErrors(true);
        $userName = $dbr->selectField('familytree', 'ft_user', array('ft_tree_id' => $treeId));
        $errno = $dbr->lastErrno();
        if ($errno > 0) {
            $status = GE_DB_ERROR;
        } else {
            if ($userName === false) {
                $status = GE_NOT_FOUND;
            } else {
                $wgUser = User::newFromName($userName, false);
                // switch the global user
                if (!$wgUser) {
                    $status = GE_NOT_FOUND;
                }
            }
        }
    }
    if ($status == GE_SUCCESS) {
        $title = Title::newFromText($titleString, $ns);
        $text = $xml->content;
        if ($title == null || !$treeId) {
            //wfDebug("wfGenerate error $treeId $ns $titleString\n");
            $status = GE_INVALID_ARG;
        } else {
            $article = new Article($title, 0);
            if (!$article->exists()) {
                $editFlags = EDIT_NEW;
            } else {
                $oldText = $article->getContent();
                $editFlags = EDIT_UPDATE;
            }
            //         else if ($ns == NS_MYSOURCE) {
            //            $existingMysource = true;
            //            $revid = $title->getLatestRevID(GAID_FOR_UPDATE);
            //         }
            //         // TODO during re-upload, we need to notify users of changes if others are watching; should we not suppress RC in this case?
            //         // also, decide whether FamilyTreePropagator should update ftp or not
            //         // (FamilyTreePropagator also processes the tree checkboxes, so we probably don't want it called)
            //         else {
            ////            $editFlags = EDIT_UPDATE;
            //            $status = GE_DUP_KEY;
            //         }
        }
    }
    if ($status == GE_SUCCESS && ($editFlags == EDIT_NEW || $text != $oldText)) {
        $isUpdatable = true;
        if ($editFlags == EDIT_UPDATE) {
            $revision = Revision::newFromId($article->getLatest());
            if ($revision && $revision->getComment() != 'gedcom upload') {
                $isUpdatable = false;
                error_log("Cannot update existing user-edited page: " . $article->getTitle()->getPrefixedText());
            }
        }
        if ($isUpdatable) {
            // NOTE: This doesn't execute the code in FamilyTreePropagator to update familytree_page, so if you edit a page, you'll have to update
            // the familytree_page.fp_latest yourself.  Also, FamilyTreePropagator adds the page to the tree (based upon request checkboxes), but we do this below
            if (!$article->doEdit($text, 'gedcom upload', $editFlags | EDIT_SUPPRESS_RC)) {
                $status = GE_WIKI_ERROR;
            }
            // TODO remove this
            if ($ns == NS_PERSON) {
                $xml = StructuredData::getXml('person', $text);
                $summaryFields = explode('|', Person::getSummary($xml, $title));
                $birthDate = $summaryFields[3];
                $deathDate = $summaryFields[5];
                $birthYear = '';
                $deathYear = '';
                if (preg_match('/\\d\\d\\d\\d/', $birthDate, $matches)) {
                    $birthYear = $matches[0];
                }
                if (preg_match('/\\d\\d\\d\\d/', $deathDate, $matches)) {
                    $deathYear = $matches[0];
                }
                if (($birthYear && $birthYear < 1750 || $deathYear && $deathYear < 1750) && !in_array($wgUser->getName(), explode('|', wfMsg('trustedgedcomuploaders')))) {
                    error_log($title->getPrefixedText() . "\n", 3, '/opt/wr/logs/earlypeople.txt');
                }
            }
        }
    }
    if ($status == GE_SUCCESS) {
        $dbw =& wfGetDB(DB_MASTER);
        $dbw->ignoreErrors(true);
        $dbw->begin();
        //      if ($status == GE_SUCCESS) {
        //         // save the data
        //         $data = $xml->data->asXML();
        //         if ($data) {
        //            $dataVersion = 1;
        //            $status == fgSaveData($dbw, $treeId, $title, $data, true);
        //         }
        //         else {
        //            $dataVersion = 0;
        //         }
        //      }
        // add the page to the tree
        if ($status == GE_SUCCESS) {
            if (!FamilyTreeUtil::addPage($dbw, $wgUser, $treeId, $title, 0, 0, 0, $uid, 0)) {
                $status = GE_DB_ERROR;
            }
        }
        // watch the page
        if ($status == GE_SUCCESS) {
            StructuredData::addWatch($wgUser, $article, true);
        }
        if ($status == GE_SUCCESS) {
            $dbw->commit();
        } else {
            $dbw->rollback();
        }
    }
    // return status
    $titleString = StructuredData::escapeXml($titleString);
    return "<generate status=\"{$status}\" ns=\"{$ns}\" title=\"{$titleString}\"></generate>";
}
Пример #10
0
 /**
  * Propagate move, delete, or undelete to other articles if necessary
  *
  * @param String $newTitleString null in case of delete; same as this title string in case of undelete
  * @param String $text text of article
  * @param bool $textChanged set to true if we change the text
  * @return bool true if success
  */
 protected function propagateMoveDeleteUndelete($newTitleString, $newNs, &$text, &$textChanged)
 {
     $result = true;
     $newTitle = $newTitleString ? Title::newFromText($newTitleString, NS_IMAGE) : null;
     // if we're undeleting, add additional people from WLH, getting updated person data
     if ($this->titleString == $newTitleString) {
         list($people, $families) = SDImage::getPropagatedData($this->xml);
         $this->addWhatLinksHere($people, $families);
         $pageText = $this->getPageElements($people, 'person', NS_PERSON) . $this->getPageElements($families, 'family', NS_FAMILY);
         // update text: replace old family information with new
         $text = preg_replace("\$<(person|family) [^>]*>\n\$", '', $text);
         $text = preg_replace('$</' . $this->tagName . '>$', StructuredData::protectRegexReplace($pageText . '</' . $this->tagName . '>'), $text, 1);
         $this->xml = StructuredData::getXml($this->tagName, $text);
         $textChanged = true;
     }
     // get data to propagate
     list($people, $families) = SDImage::getPropagatedData($this->xml);
     foreach ($people as $p) {
         $linkTitle = Title::newFromText((string) $p, NS_PERSON);
         PropagationManager::addPropagatedAction($this->title, 'dellink', $linkTitle);
         if ($newTitle) {
             PropagationManager::addPropagatedAction($newTitle, 'addlink', $linkTitle);
         }
         // don't need to check propagated action before calling updatePage, because propagateMoveDeleteUndelete is never part of a loop
         $result = $result && $this->updatePage($linkTitle, 'person', $newTitleString, $text, $textChanged);
     }
     foreach ($families as $p) {
         $linkTitle = Title::newFromText((string) $p, NS_FAMILY);
         PropagationManager::addPropagatedAction($this->title, 'dellink', $linkTitle);
         if ($newTitle) {
             PropagationManager::addPropagatedAction($newTitle, 'addlink', $linkTitle);
         }
         // don't need to check propagated action before calling updatePage, because propagateMoveDeleteUndelete is never part of a loop
         $result = $result && $this->updatePage($linkTitle, 'family', $newTitleString, $text, $textChanged);
     }
     if (!$result) {
         error_log("ERROR! Family move/delete/undelete not propagated: {$this->titleString} -> " . ($newTitleString ? $newTitleString : "[delete]") . "\n");
     }
     return $result;
 }