function qa_update_counts_for_q($postid)
{
    if (isset($postid)) {
        // post might no longer exist
        qa_db_category_path_qcount_update(qa_db_post_get_category_path($postid));
    }
    qa_db_qcount_update();
    qa_db_unaqcount_update();
    qa_db_unselqcount_update();
    qa_db_unupaqcount_update();
}
Exemple #2
0
function qa_question_create($followanswer, $userid, $handle, $cookieid, $title, $content, $format, $text, $tagstring, $notify, $email, $categoryid = null, $extravalue = null, $queued = false)
{
    require_once QA_INCLUDE_DIR . 'qa-db-selects.php';
    $postid = qa_db_post_create($queued ? 'Q_QUEUED' : 'Q', @$followanswer['postid'], $userid, isset($userid) ? null : $cookieid, qa_remote_ip_address(), $title, $content, $format, $tagstring, qa_combine_notify_email($userid, $notify, $email), $categoryid);
    if (isset($extravalue)) {
        require_once QA_INCLUDE_DIR . 'qa-db-metas.php';
        qa_db_postmeta_set($postid, 'qa_q_extra', $extravalue);
    }
    qa_db_posts_calc_category_path($postid);
    qa_db_hotness_update($postid);
    if (!$queued) {
        qa_db_category_path_qcount_update(qa_db_post_get_category_path($postid));
        qa_post_index($postid, 'Q', $postid, @$followanswer['postid'], $title, $content, $format, $text, $tagstring, $categoryid);
        qa_db_points_update_ifuser($userid, 'qposts');
        qa_db_qcount_update();
        qa_db_unaqcount_update();
        qa_db_unselqcount_update();
        qa_db_unupaqcount_update();
    }
    qa_report_event($queued ? 'q_queue' : 'q_post', $userid, $handle, $cookieid, array('postid' => $postid, 'parentid' => @$followanswer['postid'], 'parent' => $followanswer, 'title' => $title, 'content' => $content, 'format' => $format, 'text' => $text, 'tags' => $tagstring, 'categoryid' => $categoryid, 'extra' => $extravalue, 'notify' => $notify, 'email' => $email));
    return $postid;
}
function qa_recalc_perform_step(&$state)
{
    $continue = false;
    @(list($operation, $length, $next, $done) = explode("\t", $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 . '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 . '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_db_uapprovecount_update();
            // needs to be somewhere and this is the most appropriate place
            qa_recalc_transition($state, 'dorecalcpoints_recalc');
            break;
        case 'dorecalcpoints_recalc':
            $recalccount = 10;
            $userids = qa_db_users_get_for_recalc_points($next, $recalccount + 1);
            // get one extra so we know where to start from next
            $gotcount = count($userids);
            $recalccount = min($recalccount, $gotcount);
            // can't recalc more than we got
            if ($recalccount > 0) {
                $lastuserid = $userids[$recalccount - 1];
                qa_db_users_recalc_points($next, $lastuserid);
                $done += $recalccount;
            } else {
                $lastuserid = $next;
            }
            // for truncation
            if ($gotcount > $recalccount) {
                // more left to do
                $next = $userids[$recalccount];
                // start next round at first one not recalculated
                $continue = true;
            } else {
                qa_db_truncate_userpoints($lastuserid);
                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 . 'app/events.php';
                require_once QA_INCLUDE_DIR . 'app/updates.php';
                require_once QA_INCLUDE_DIR . '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;
                        if ($followonq) {
                            $updatetype = QA_UPDATE_FOLLOWS;
                        } elseif ($post['basetype'] == 'C' && @$posts[$post['parentid']]['basetype'] == 'Q') {
                            $updatetype = QA_UPDATE_C_FOR_Q;
                        } elseif ($post['basetype'] == 'C' && @$posts[$post['parentid']]['basetype'] == 'A') {
                            $updatetype = QA_UPDATE_C_FOR_A;
                        } else {
                            $updatetype = null;
                        }
                        qa_create_event_for_q_user($questionid, $postid, $updatetype, $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'], QA_UPDATE_FOLLOWS, $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 . '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 . '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 . 'app/posts.php';
                $postid = $posts[0];
                qa_post_delete($postid);
                $next = 1 + $postid;
                $done++;
                $continue = true;
            } else {
                qa_recalc_transition($state, 'dodeletehidden_complete');
            }
            break;
        case 'doblobstodisk':
            qa_recalc_transition($state, 'doblobstodisk_move');
            break;
        case 'doblobstodisk_move':
            $blob = qa_db_get_next_blob_in_db($next);
            if (isset($blob)) {
                require_once QA_INCLUDE_DIR . 'app/blobs.php';
                require_once QA_INCLUDE_DIR . 'db/blobs.php';
                if (qa_write_blob_file($blob['blobid'], $blob['content'], $blob['format'])) {
                    qa_db_blob_set_content($blob['blobid'], null);
                }
                $next = 1 + $blob['blobid'];
                $done++;
                $continue = true;
            } else {
                qa_recalc_transition($state, 'doblobstodisk_complete');
            }
            break;
        case 'doblobstodb':
            qa_recalc_transition($state, 'doblobstodb_move');
            break;
        case 'doblobstodb_move':
            $blob = qa_db_get_next_blob_on_disk($next);
            if (isset($blob)) {
                require_once QA_INCLUDE_DIR . 'app/blobs.php';
                require_once QA_INCLUDE_DIR . 'db/blobs.php';
                $content = qa_read_blob_file($blob['blobid'], $blob['format']);
                qa_db_blob_set_content($blob['blobid'], $content);
                qa_delete_blob_file($blob['blobid'], $blob['format']);
                $next = 1 + $blob['blobid'];
                $done++;
                $continue = true;
            } else {
                qa_recalc_transition($state, 'doblobstodb_complete');
            }
            break;
        default:
            $state = '';
            break;
    }
    if ($continue) {
        $state = $operation . "\t" . $length . "\t" . $next . "\t" . $done;
    }
    return $continue && $done < $length;
}
function qa_question_delete($oldquestion, $userid, $handle, $cookieid)
{
    require_once QA_INCLUDE_DIR . 'qa-db-votes.php';
    if (!$oldquestion['hidden']) {
        qa_fatal_error('Tried to delete a non-hidden question');
    }
    $useridvotes = qa_db_uservote_post_get($oldquestion['postid']);
    $oldpath = qa_db_post_get_category_path($oldquestion['postid']);
    qa_post_unindex($oldquestion['postid']);
    qa_db_post_delete($oldquestion['postid']);
    // also deletes any related voteds due to cascading
    qa_db_category_path_qcount_update($oldpath);
    qa_db_points_update_ifuser($oldquestion['userid'], array('qposts', 'aselects', 'qvoteds', 'upvoteds', 'downvoteds'));
    foreach ($useridvotes as $voteruserid => $vote) {
        qa_db_points_update_ifuser($voteruserid, $vote > 0 ? 'qupvotes' : 'qdownvotes');
    }
    // could do this in one query like in qa_db_users_recalc_points() but this will do for now - unlikely to be many votes
    qa_db_qcount_update();
    qa_db_unaqcount_update();
    qa_report_event('q_delete', $userid, $handle, $cookieid, array('postid' => $oldquestion['postid']));
}
function qa_question_create($followanswer, $userid, $handle, $cookieid, $title, $content, $format, $text, $tagstring, $notify, $email, $categoryid = null)
{
    require_once QA_INCLUDE_DIR . 'qa-app-options.php';
    require_once QA_INCLUDE_DIR . 'qa-app-emails.php';
    require_once QA_INCLUDE_DIR . 'qa-db-selects.php';
    require_once QA_INCLUDE_DIR . 'mp-db-users.php';
    require_once QA_INCLUDE_DIR . 'mp-app-users.php';
    $postid = qa_db_post_create('Q', @$followanswer['postid'], $userid, isset($userid) ? null : $cookieid, qa_remote_ip_address(), $title, $content, $format, $tagstring, qa_combine_notify_email($userid, $notify, $email), $categoryid);
    qa_db_posts_calc_category_path($postid);
    qa_db_category_path_qcount_update(qa_db_post_get_category_path($postid));
    qa_post_index($postid, 'Q', $postid, $title, $text, $tagstring);
    qa_db_hotness_update($postid);
    qa_db_points_update_ifuser($userid, 'qposts');
    qa_db_qcount_update();
    qa_db_unaqcount_update();
    if (isset($followanswer['notify']) && !qa_post_is_by_user($followanswer, $userid, $cookieid)) {
        require_once QA_INCLUDE_DIR . 'qa-app-emails.php';
        require_once QA_INCLUDE_DIR . 'qa-app-format.php';
        require_once QA_INCLUDE_DIR . 'qa-util-string.php';
        $blockwordspreg = qa_get_block_words_preg();
        $sendtitle = qa_block_words_replace($title, $blockwordspreg);
        $sendtext = qa_viewer_text($followanswer['content'], $followanswer['format'], array('blockwordspreg' => $blockwordspreg));
        qa_send_notification($followanswer['userid'], $followanswer['notify'], @$followanswer['handle'], qa_lang('emails/a_followed_subject'), qa_lang('emails/a_followed_body'), array('^q_handle' => isset($handle) ? $handle : qa_lang('main/anonymous'), '^q_title' => $sendtitle, '^a_content' => $sendtext, '^url' => qa_path(qa_q_request($postid, $sendtitle), null, qa_opt('site_url'))));
    }
    if (qa_opt('notify_admin_q_post')) {
        qa_send_notification(null, qa_opt('feedback_email'), null, qa_lang('emails/q_posted_subject'), qa_lang('emails/q_posted_body'), array('^q_handle' => isset($handle) ? $handle : qa_lang('main/anonymous'), '^q_title' => $title, '^q_content' => $text, '^url' => qa_path(qa_q_request($postid, $title), null, qa_opt('site_url'))));
    }
    // notify all members of the category about the new post
    // find list of members of the category
    $recipients = mp_get_category_userids(mp_get_categoryid());
    // remove the current user from the list of recipients
    unset($recipients[$userid]);
    // get details for current category
    $currentcategory = mp_get_categoryinfo(mp_get_categoryid());
    foreach ($recipients as $recipient) {
        // retrieve the user flags
        $userflags = mp_get_user_flags($recipient['userid']);
        // check user flags to determine whether user should be notified or not
        // of the new answer post
        if (!($userflags & QA_USER_FLAGS_NOTIFY_QUESTIONS)) {
            // for each member, send notification
            qa_send_notification($recipient['userid'], null, null, qa_lang('emails/q_posted_with_category_subject'), qa_lang('emails/q_posted_body'), array('^q_handle' => isset($handle) ? $handle : qa_lang('main/anonymous'), '^q_title' => $title, '^q_content' => $text, '^url' => qa_path(qa_q_request($postid, $title), null, qa_opt('site_url')), '^category_title' => $currentcategory['title']));
        }
    }
    qa_report_event('q_post', $userid, $handle, $cookieid, array('postid' => $postid, 'parentid' => @$followanswer['postid'], 'title' => $title, 'content' => $content, 'format' => $format, 'text' => $text, 'tags' => $tagstring, 'categoryid' => $categoryid, 'notify' => $notify, 'email' => $email));
    return $postid;
}
function qa_recalc_perform_step(&$state)
{
    $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_perform_step(&$state)
{
    $continue = false;
    @(list($operation, $length, $next, $done) = explode(',', $state));
    switch ($operation) {
        case 'doreindexposts':
            qa_recalc_transition($state, 'doreindexposts_postcount');
            break;
        case 'doreindexposts_postcount':
            qa_db_qcount_update();
            qa_db_acount_update();
            qa_db_ccount_update();
            qa_recalc_transition($state, 'doreindexposts_reindex');
            break;
        case 'doreindexposts_reindex':
            $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);
                foreach ($posts as $postid => $post) {
                    qa_post_index($postid, $post['type'], $post['questionid'], $post['title'], qa_viewer_text($post['content'], $post['format']), $post['tags'], true);
                }
                $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_recalc_transition($state, 'dorecountposts_recount');
            break;
        case 'dorecountposts_recount':
            $postids = qa_db_posts_get_for_recounting($next, 1000);
            if (count($postids)) {
                $lastpostid = max($postids);
                qa_db_posts_recount($next, $lastpostid);
                $next = 1 + $lastpostid;
                $done += count($postids);
                $continue = true;
            } else {
                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 '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)) {
                $postid = $posts[0];
                $oldcomment = qa_db_single_select(qa_db_full_post_selectspec(null, $postid));
                $parent = qa_db_single_select(qa_db_full_post_selectspec(null, $oldcomment['parentid']));
                if ($parent['basetype'] == 'Q') {
                    $question = $parent;
                    $answer = null;
                } else {
                    $question = qa_db_single_select(qa_db_full_post_selectspec(null, $parent['parentid']));
                    $answer = $parent;
                }
                qa_comment_delete($oldcomment, $question, $answer, null, null, null);
                $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)) {
                $postid = $posts[0];
                $oldanswer = qa_db_single_select(qa_db_full_post_selectspec(null, $postid));
                $question = qa_db_single_select(qa_db_full_post_selectspec(null, $oldanswer['parentid']));
                qa_answer_delete($oldanswer, $question, null, null, null);
                $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)) {
                $postid = $posts[0];
                $oldquestion = qa_db_single_select(qa_db_full_post_selectspec(null, $postid));
                qa_question_delete($oldquestion, null, null, null);
                $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;
}