Файл: qa-include/qa-app-recalc.php
Строк: 824
<?php
/*
Question2Answer (c) Gideon Greenspan
http://www.question2answer.org/
File: qa-include/qa-app-recalc.php
Version: See define()s at top of qa-include/qa-base.php
Description: Managing database recalculations (clean-up operations) and status messages
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
More about this license: http://www.question2answer.org/license.php
*/
/*
A full list of redundant (non-normal) information in the database that can be recalculated:
Recalculated in doreindexcontent:
================================
^titlewords (all): index of words in titles of posts
^contentwords (all): index of words in content of posts
^tagwords (all): index of words in tags of posts (a tag can contain multiple words)
^posttags (all): index tags of posts
^words (all): list of words used for indexes
^options (title=cache_qcount|cache_acount|cache_ccount|cache_tagcount|cache_unaqcount): total Qs, As, Cs, tags, unanswered Qs
Recalculated in dorecountposts:
==============================
^posts (upvotes, downvotes, netvotes, hotness, acount, amaxvotes, flagcount): number of votes, hotness, answers, answer votes, flags
Recalculated in dorecalcpoints:
===============================
^userpoints (all except bonus): points calculation for all users
^options (title=cache_userpointscount):
Recalculated in dorecalccategories:
===================================
^posts (categoryid): assign to answers and comments based on their antecedent question
^posts (catidpath1, catidpath2, catidpath3): hierarchical path to category ids (requires QA_CATEGORY_DEPTH=4)
^categories (qcount): number of (visible) questions in each category
^categories (backpath): full (backwards) path of slugs to that category
Recalculated in dorebuildupdates:
=================================
^sharedevents (all): per-entity event streams (see big comment in qa-db-favorites.php)
^userevents (all): per-subscriber event streams
[but these are not entirely redundant since they can contain historical information no longer in ^posts]
*/
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
header('Location: ../');
exit;
}
require_once QA_INCLUDE_DIR.'qa-db-recalc.php';
require_once QA_INCLUDE_DIR.'qa-db-post-create.php';
require_once QA_INCLUDE_DIR.'qa-db-points.php';
require_once QA_INCLUDE_DIR.'qa-db-selects.php';
require_once QA_INCLUDE_DIR.'qa-db-admin.php';
require_once QA_INCLUDE_DIR.'qa-app-options.php';
require_once QA_INCLUDE_DIR.'qa-app-post-create.php';
require_once QA_INCLUDE_DIR.'qa-app-post-update.php';
function qa_recalc_perform_step(&$state)
/*
Advance the recalculation operation represented by $state by a single step.
$state can also be the name of a recalculation operation on its own.
*/
{
$continue=false;
@list($operation, $length, $next, $done)=explode(',', $state);
switch ($operation) {
case 'doreindexcontent':
qa_recalc_transition($state, 'doreindexcontent_pagereindex');
break;
case 'doreindexcontent_pagereindex':
$pages=qa_db_pages_get_for_reindexing($next, 10);
if (count($pages)) {
require_once QA_INCLUDE_DIR.'qa-app-format.php';
$lastpageid=max(array_keys($pages));
foreach ($pages as $pageid => $page)
if (!($page['flags'] & QA_PAGE_FLAGS_EXTERNAL)) {
$searchmodules=qa_load_modules_with('search', 'unindex_page');
foreach ($searchmodules as $searchmodule)
$searchmodule->unindex_page($pageid);
$searchmodules=qa_load_modules_with('search', 'index_page');
if (count($searchmodules)) {
$indextext=qa_viewer_text($page['content'], 'html');
foreach ($searchmodules as $searchmodule)
$searchmodule->index_page($pageid, $page['tags'], $page['heading'], $page['content'], 'html', $indextext);
}
}
$next=1+$lastpageid;
$done+=count($pages);
$continue=true;
} else
qa_recalc_transition($state, 'doreindexcontent_postcount');
break;
case 'doreindexcontent_postcount':
qa_db_qcount_update();
qa_db_acount_update();
qa_db_ccount_update();
qa_recalc_transition($state, 'doreindexcontent_postreindex');
break;
case 'doreindexcontent_postreindex':
$posts=qa_db_posts_get_for_reindexing($next, 10);
if (count($posts)) {
require_once QA_INCLUDE_DIR.'qa-app-format.php';
$lastpostid=max(array_keys($posts));
qa_db_prepare_for_reindexing($next, $lastpostid);
qa_suspend_update_counts();
foreach ($posts as $postid => $post) {
qa_post_unindex($postid);
qa_post_index($postid, $post['type'], $post['questionid'], $post['parentid'], $post['title'], $post['content'],
$post['format'], qa_viewer_text($post['content'], $post['format']), $post['tags'], $post['categoryid']);
}
$next=1+$lastpostid;
$done+=count($posts);
$continue=true;
} else {
qa_db_truncate_indexes($next);
qa_recalc_transition($state, 'doreindexposts_wordcount');
}
break;
case 'doreindexposts_wordcount':
$wordids=qa_db_words_prepare_for_recounting($next, 1000);
if (count($wordids)) {
$lastwordid=max($wordids);
qa_db_words_recount($next, $lastwordid);
$next=1+$lastwordid;
$done+=count($wordids);
$continue=true;
} else {
qa_db_tagcount_update(); // this is quick so just do it here
qa_recalc_transition($state, 'doreindexposts_complete');
}
break;
case 'dorecountposts':
qa_recalc_transition($state, 'dorecountposts_postcount');
break;
case 'dorecountposts_postcount':
qa_db_qcount_update();
qa_db_acount_update();
qa_db_ccount_update();
qa_db_unaqcount_update();
qa_db_unselqcount_update();
qa_recalc_transition($state, 'dorecountposts_votecount');
break;
case 'dorecountposts_votecount':
$postids=qa_db_posts_get_for_recounting($next, 1000);
if (count($postids)) {
$lastpostid=max($postids);
qa_db_posts_votes_recount($next, $lastpostid);
$next=1+$lastpostid;
$done+=count($postids);
$continue=true;
} else
qa_recalc_transition($state, 'dorecountposts_acount');
break;
case 'dorecountposts_acount':
$postids=qa_db_posts_get_for_recounting($next, 1000);
if (count($postids)) {
$lastpostid=max($postids);
qa_db_posts_answers_recount($next, $lastpostid);
$next=1+$lastpostid;
$done+=count($postids);
$continue=true;
} else {
qa_db_unupaqcount_update();
qa_recalc_transition($state, 'dorecountposts_complete');
}
break;
case 'dorecalcpoints':
qa_recalc_transition($state, 'dorecalcpoints_usercount');
break;
case 'dorecalcpoints_usercount':
qa_db_userpointscount_update(); // for progress update - not necessarily accurate
qa_recalc_transition($state, 'dorecalcpoints_recalc');
break;
case 'dorecalcpoints_recalc':
$userids=qa_db_users_get_for_recalc_points($next, 10);
if (count($userids)) {
$lastuserid=max($userids);
qa_db_users_recalc_points($next, $lastuserid);
$next=1+$lastuserid;
$done+=count($userids);
$continue=true;
} else {
qa_db_truncate_userpoints($next);
qa_db_userpointscount_update(); // quick so just do it here
qa_recalc_transition($state, 'dorecalcpoints_complete');
}
break;
case 'dorefillevents':
qa_recalc_transition($state, 'dorefillevents_qcount');
break;
case 'dorefillevents_qcount':
qa_db_qcount_update();
qa_recalc_transition($state, 'dorefillevents_refill');
break;
case 'dorefillevents_refill':
$questionids=qa_db_qs_get_for_event_refilling($next, 1);
if (count($questionids)) {
require_once QA_INCLUDE_DIR.'qa-app-events.php';
require_once QA_INCLUDE_DIR.'qa-app-updates.php';
require_once QA_INCLUDE_DIR.'qa-util-sort.php';
$lastquestionid=max($questionids);
foreach ($questionids as $questionid) {
// Retrieve all posts relating to this question
list($question, $childposts, $achildposts)=qa_db_select_with_pending(
qa_db_full_post_selectspec(null, $questionid),
qa_db_full_child_posts_selectspec(null, $questionid),
qa_db_full_a_child_posts_selectspec(null, $questionid)
);
// Merge all posts while preserving keys as postids
$posts=array($questionid => $question);
foreach ($childposts as $postid => $post)
$posts[$postid]=$post;
foreach ($achildposts as $postid => $post)
$posts[$postid]=$post;
// Creation and editing of each post
foreach ($posts as $postid => $post) {
$followonq=($post['basetype']=='Q') && ($postid!=$questionid);
qa_create_event_for_q_user($questionid, $postid, $followonq ? QA_UPDATE_FOLLOWS : null, $post['userid'], @$posts[$post['parentid']]['userid'], $post['created']);
if (isset($post['updated']) && !$followonq)
qa_create_event_for_q_user($questionid, $postid, $post['updatetype'], $post['lastuserid'], $post['userid'], $post['updated']);
}
// Tags and categories of question
qa_create_event_for_tags($question['tags'], $questionid, null, $question['userid'], $question['created']);
qa_create_event_for_category($question['categoryid'], $questionid, null, $question['userid'], $question['created']);
// Collect comment threads
$parentidcomments=array();
foreach ($posts as $postid => $post)
if ($post['basetype']=='C')
$parentidcomments[$post['parentid']][$postid]=$post;
// For each comment thread, notify all previous comment authors of each comment in the thread (could get slow)
foreach ($parentidcomments as $parentid => $comments) {
$keyuserids=array();
qa_sort_by($comments, 'created');
foreach ($comments as $comment) {
foreach ($keyuserids as $keyuserid => $dummy)
if ( ($keyuserid != $comment['userid']) && ($keyuserid != @$posts[$parentid]['userid']) )
qa_db_event_create_not_entity($keyuserid, $questionid, $comment['postid'], null, $comment['userid'], $comment['created']);
if (isset($comment['userid']))
$keyuserids[$comment['userid']]=true;
}
}
}
$next=1+$lastquestionid;
$done+=count($questionids);
$continue=true;
} else
qa_recalc_transition($state, 'dorefillevents_complete');
break;
case 'dorecalccategories':
qa_recalc_transition($state, 'dorecalccategories_postcount');
break;
case 'dorecalccategories_postcount':
qa_db_acount_update();
qa_db_ccount_update();
qa_recalc_transition($state, 'dorecalccategories_postupdate');
break;
case 'dorecalccategories_postupdate':
$postids=qa_db_posts_get_for_recategorizing($next, 100);
if (count($postids)) {
$lastpostid=max($postids);
qa_db_posts_recalc_categoryid($next, $lastpostid);
qa_db_posts_calc_category_path($next, $lastpostid);
$next=1+$lastpostid;
$done+=count($postids);
$continue=true;
} else {
qa_recalc_transition($state, 'dorecalccategories_recount');
}
break;
case 'dorecalccategories_recount':
$categoryids=qa_db_categories_get_for_recalcs($next, 10);
if (count($categoryids)) {
$lastcategoryid=max($categoryids);
foreach ($categoryids as $categoryid)
qa_db_ifcategory_qcount_update($categoryid);
$next=1+$lastcategoryid;
$done+=count($categoryids);
$continue=true;
} else {
qa_recalc_transition($state, 'dorecalccategories_backpaths');
}
break;
case 'dorecalccategories_backpaths':
$categoryids=qa_db_categories_get_for_recalcs($next, 10);
if (count($categoryids)) {
$lastcategoryid=max($categoryids);
qa_db_categories_recalc_backpaths($next, $lastcategoryid);
$next=1+$lastcategoryid;
$done+=count($categoryids);
$continue=true;
} else {
qa_recalc_transition($state, 'dorecalccategories_complete');
}
break;
case 'dodeletehidden':
qa_recalc_transition($state, 'dodeletehidden_comments');
break;
case 'dodeletehidden_comments':
$posts=qa_db_posts_get_for_deleting('C', $next, 1);
if (count($posts)) {
require_once QA_INCLUDE_DIR.'qa-app-posts.php';
$postid=$posts[0];
qa_post_delete($postid);
$next=1+$postid;
$done++;
$continue=true;
} else
qa_recalc_transition($state, 'dodeletehidden_answers');
break;
case 'dodeletehidden_answers':
$posts=qa_db_posts_get_for_deleting('A', $next, 1);
if (count($posts)) {
require_once QA_INCLUDE_DIR.'qa-app-posts.php';
$postid=$posts[0];
qa_post_delete($postid);
$next=1+$postid;
$done++;
$continue=true;
} else
qa_recalc_transition($state, 'dodeletehidden_questions');
break;
case 'dodeletehidden_questions':
$posts=qa_db_posts_get_for_deleting('Q', $next, 1);
if (count($posts)) {
require_once QA_INCLUDE_DIR.'qa-app-posts.php';
$postid=$posts[0];
qa_post_delete($postid);
$next=1+$postid;
$done++;
$continue=true;
} else
qa_recalc_transition($state, 'dodeletehidden_complete');
break;
default:
$state='';
break;
}
if ($continue)
$state=$operation.','.$length.','.$next.','.$done;
return $continue && ($done<$length);
}
function qa_recalc_transition(&$state, $operation)
/*
Change the $state to represent the beginning of a new $operation
*/
{
$state=$operation.','.qa_recalc_stage_length($operation).',0,0';
}
function qa_recalc_stage_length($operation)
/*
Return how many steps there will be in recalculation $operation
*/
{
switch ($operation) {
case 'doreindexcontent_pagereindex':
$length=qa_db_count_pages();
break;
case 'doreindexcontent_postreindex':
$length=qa_opt('cache_qcount')+qa_opt('cache_acount')+qa_opt('cache_ccount');
break;
case 'doreindexposts_wordcount':
$length=qa_db_count_words();
break;
case 'dorecalcpoints_recalc':
$length=qa_opt('cache_userpointscount');
break;
case 'dorecountposts_votecount':
case 'dorecountposts_acount':
case 'dorecalccategories_postupdate':
$length=qa_db_count_posts();
break;
case 'dorefillevents_refill':
$length=qa_opt('cache_qcount')+qa_db_count_posts('Q_HIDDEN');
break;
case 'dorecalccategories_recount':
case 'dorecalccategories_backpaths':
$length=qa_db_count_categories();
break;
case 'dodeletehidden_comments':
$length=count(qa_db_posts_get_for_deleting('C'));
break;
case 'dodeletehidden_answers':
$length=count(qa_db_posts_get_for_deleting('A'));
break;
case 'dodeletehidden_questions':
$length=count(qa_db_posts_get_for_deleting('Q'));
break;
default:
$length=0;
break;
}
return $length;
}
function qa_recalc_get_message($state)
/*
Return a string which gives a user-viewable version of $state
*/
{
@list($operation, $length, $next, $done)=explode(',', $state);
$done=(int)$done;
$length=(int)$length;
switch ($operation) {
case 'doreindexcontent_postcount':
case 'dorecountposts_postcount':
case 'dorecalccategories_postcount':
case 'dorefillevents_qcount':
$message=qa_lang('admin/recalc_posts_count');
break;
case 'doreindexcontent_pagereindex':
$message=strtr(qa_lang('admin/reindex_pages_reindexed'), array(
'^1' => number_format($done),
'^2' => number_format($length)
));
break;
case 'doreindexcontent_postreindex':
$message=strtr(qa_lang('admin/reindex_posts_reindexed'), array(
'^1' => number_format($done),
'^2' => number_format($length)
));
break;
case 'doreindexposts_wordcount':
$message=strtr(qa_lang('admin/reindex_posts_wordcounted'), array(
'^1' => number_format($done),
'^2' => number_format($length)
));
break;
case 'dorecountposts_votecount':
$message=strtr(qa_lang('admin/recount_posts_votes_recounted'), array(
'^1' => number_format($done),
'^2' => number_format($length)
));
break;
case 'dorecountposts_acount':
$message=strtr(qa_lang('admin/recount_posts_as_recounted'), array(
'^1' => number_format($done),
'^2' => number_format($length)
));
break;
case 'doreindexposts_complete':
$message=qa_lang('admin/reindex_posts_complete');
break;
case 'dorecountposts_complete':
$message=qa_lang('admin/recount_posts_complete');
break;
case 'dorecalcpoints_usercount':
$message=qa_lang('admin/recalc_points_usercount');
break;
case 'dorecalcpoints_recalc':
$message=strtr(qa_lang('admin/recalc_points_recalced'), array(
'^1' => number_format($done),
'^2' => number_format($length)
));
break;
case 'dorecalcpoints_complete':
$message=qa_lang('admin/recalc_points_complete');
break;
case 'dorefillevents_refill':
$message=strtr(qa_lang('admin/refill_events_refilled'), array(
'^1' => number_format($done),
'^2' => number_format($length)
));
break;
case 'dorefillevents_complete':
$message=qa_lang('admin/refill_events_complete');
break;
case 'dorecalccategories_postupdate':
$message=strtr(qa_lang('admin/recalc_categories_updated'), array(
'^1' => number_format($done),
'^2' => number_format($length)
));
break;
case 'dorecalccategories_recount':
$message=strtr(qa_lang('admin/recalc_categories_recounting'), array(
'^1' => number_format($done),
'^2' => number_format($length)
));
break;
case 'dorecalccategories_backpaths':
$message=strtr(qa_lang('admin/recalc_categories_backpaths'), array(
'^1' => number_format($done),
'^2' => number_format($length)
));
break;
case 'dorecalccategories_complete':
$message=qa_lang('admin/recalc_categories_complete');
break;
case 'dodeletehidden_comments':
$message=strtr(qa_lang('admin/hidden_comments_deleted'), array(
'^1' => number_format($done),
'^2' => number_format($length)
));
break;
case 'dodeletehidden_answers':
$message=strtr(qa_lang('admin/hidden_answers_deleted'), array(
'^1' => number_format($done),
'^2' => number_format($length)
));
break;
case 'dodeletehidden_questions':
$message=strtr(qa_lang('admin/hidden_questions_deleted'), array(
'^1' => number_format($done),
'^2' => number_format($length)
));
break;
case 'dodeletehidden_complete':
$message=qa_lang('admin/delete_hidden_complete');
break;
default:
$message='';
break;
}
return $message;
}
/*
Omit PHP closing tag to help avoid accidental output
*/