void ChannelFillThread::run() { #ifdef CHAN_DEBUG std::cerr << "ChannelFillThread::run()" << std::endl; #endif std::list<ChannelMsgSummary> msgs; std::list<ChannelMsgSummary>::iterator it; rsChannels->getChannelMsgList(channelId, msgs); msgs.sort(sortChannelMsgSummary); int count = msgs.size(); int pos = 0; for (it = msgs.begin(); it != msgs.end(); it++) { if (stopped) { break; } emit addMsg(QString::fromStdString(channelId), QString::fromStdString(it->msgId), ++pos, count); } #ifdef CHAN_DEBUG std::cerr << "ChannelFillThread::run() stopped: " << (wasStopped() ? "yes" : "no") << std::endl; #endif }
void GxsForumsFillThread::run() { RsTokenService *service = rsGxsForums->getTokenService(); uint32_t msg_token; uint32_t grp_token; emit status(tr("Waiting")); { /* get all messages of the forum */ RsTokReqOptions opts; opts.mReqType = GXS_REQUEST_TYPE_MSG_DATA; std::list<RsGxsGroupId> grpIds; grpIds.push_back(mForumId); #ifdef DEBUG_FORUMS std::cerr << "GxsForumsFillThread::run() forum id " << mForumId << std::endl; #endif service->requestMsgInfo(msg_token, RS_TOKREQ_ANSTYPE_DATA, opts, grpIds); /* wait for the answer */ uint32_t requestStatus = RsTokenService::PENDING; while (!wasStopped()) { requestStatus = service->requestStatus(msg_token); if (requestStatus == RsTokenService::FAILED || requestStatus == RsTokenService::COMPLETE) { break; } msleep(200); } if (requestStatus == RsTokenService::FAILED) return; } // also get the forum meta data. { RsTokReqOptions opts; opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; std::list<RsGxsGroupId> grpIds; grpIds.push_back(mForumId); #ifdef DEBUG_FORUMS std::cerr << "GxsForumsFillThread::run() forum id " << mForumId << std::endl; #endif service->requestGroupInfo(grp_token, RS_TOKREQ_ANSTYPE_DATA, opts, grpIds); /* wait for the answer */ uint32_t requestStatus = RsTokenService::PENDING; while (!wasStopped()) { requestStatus = service->requestStatus(grp_token); if (requestStatus == RsTokenService::FAILED || requestStatus == RsTokenService::COMPLETE) { break; } msleep(200); } if (requestStatus == RsTokenService::FAILED) return; } if (wasStopped()) { #ifdef DEBUG_FORUMS std::cerr << "GxsForumsFillThread::run() thread stopped, cancel request" << std::endl; #endif /* cancel request */ service->cancelRequest(msg_token); service->cancelRequest(grp_token); return; } emit status(tr("Retrieving")); std::vector<RsGxsForumGroup> forum_groups; if (!rsGxsForums->getGroupData(grp_token, forum_groups) || forum_groups.size() != 1) return; RsGxsForumGroup forum_group = *forum_groups.begin(); //#ifdef DEBUG_FORUMS std::cerr << "Retrieved group data: " << std::endl; std::cerr << " Group ID: " << forum_group.mMeta.mGroupId << std::endl; std::cerr << " Admin lst: " << forum_group.mAdminList.ids.size() << " elements." << std::endl; for(auto it(forum_group.mAdminList.ids.begin());it!=forum_group.mAdminList.ids.end();++it) std::cerr << " " << *it << std::endl; std::cerr << " Pinned Post: " << forum_group.mPinnedPosts.ids.size() << " messages." << std::endl; for(auto it(forum_group.mPinnedPosts.ids.begin());it!=forum_group.mPinnedPosts.ids.end();++it) std::cerr << " " << *it << std::endl; //#endif /* get messages */ std::map<RsGxsMessageId,RsGxsForumMsg> msgs; { // This forces to delete msgs_array after the conversion to std::map. std::vector<RsGxsForumMsg> msgs_array; if (!rsGxsForums->getMsgData(msg_token, msgs_array)) return; // now put everything into a map in order to make search log(n) for(uint32_t i=0;i<msgs_array.size();++i) { #ifdef DEBUG_FORUMS std::cerr << "Adding message " << msgs_array[i].mMeta.mMsgId << " with parent " << msgs_array[i].mMeta.mParentId << " to message map" << std::endl; #endif msgs[msgs_array[i].mMeta.mMsgId] = msgs_array[i] ; } } emit status(tr("Loading")); int count = msgs.size(); int pos = 0; int steps = count / PROGRESSBAR_MAX; int step = 0; // ThreadList contains the list of parent threads. The algorithm below iterates through all messages // and tries to establish parenthood relationships between them, given that we only know the // immediate parent of a message and now its children. Some messages have a missing parent and for them // a fake top level parent is generated. // In order to be efficient, we first create a structure that lists the children of every mesage ID in the list. // Then the hierarchy of message is build by attaching the kids to every message until all of them have been processed. // The messages with missing parents will be the last ones remaining in the list. std::list<std::pair< RsGxsMessageId, QTreeWidgetItem* > > threadStack; std::map<RsGxsMessageId,std::list<RsGxsMessageId> > kids_array ; std::set<RsGxsMessageId> missing_parents; // First of all, remove all older versions of posts. This is done by first adding all posts into a hierarchy structure // and then removing all posts which have a new versions available. The older versions are kept appart. #ifdef DEBUG_FORUMS std::cerr << "GxsForumsFillThread::run() Collecting post versions" << std::endl; #endif mPostVersions.clear(); std::list<RsGxsMessageId> msg_stack ; for ( std::map<RsGxsMessageId,RsGxsForumMsg>::iterator msgIt = msgs.begin(); msgIt != msgs.end();++msgIt) if(!msgIt->second.mMeta.mOrigMsgId.isNull() && msgIt->second.mMeta.mOrigMsgId != msgIt->second.mMeta.mMsgId) { #ifdef DEBUG_FORUMS std::cerr << " Post " << msgIt->second.mMeta.mMsgId << " is a new version of " << msgIt->second.mMeta.mOrigMsgId << std::endl; #endif std::map<RsGxsMessageId,RsGxsForumMsg>::iterator msgIt2 = msgs.find(msgIt->second.mMeta.mOrigMsgId); // Ensuring that the post exists allows to only collect the existing data. if(msgIt2 == msgs.end()) continue ; // Make sure that the author is the same than the original message, or is a moderator. This should always happen when messages are constructed using // the UI but nothing can prevent a nasty user to craft a new version of a message with his own signature. if(msgIt2->second.mMeta.mAuthorId != msgIt->second.mMeta.mAuthorId) { if( !IS_FORUM_MSG_MODERATION(msgIt->second.mMeta.mMsgFlags) ) // if authors are different the moderation flag needs to be set on the editing msg continue ; if( forum_group.mAdminList.ids.find(msgIt->second.mMeta.mAuthorId)==forum_group.mAdminList.ids.end()) // if author is not a moderator, continue continue ; } // always add the post a self version if(mPostVersions[msgIt->second.mMeta.mOrigMsgId].empty()) mPostVersions[msgIt->second.mMeta.mOrigMsgId].push_back(QPair<time_t,RsGxsMessageId>(msgIt2->second.mMeta.mPublishTs,msgIt2->second.mMeta.mMsgId)) ; mPostVersions[msgIt->second.mMeta.mOrigMsgId].push_back(QPair<time_t,RsGxsMessageId>(msgIt->second.mMeta.mPublishTs,msgIt->second.mMeta.mMsgId)) ; } // The following code assembles all new versions of a given post into the same array, indexed by the oldest version of the post. for(QMap<RsGxsMessageId,QVector<QPair<time_t,RsGxsMessageId> > >::iterator it(mPostVersions.begin());it!=mPostVersions.end();++it) { QVector<QPair<time_t,RsGxsMessageId> >& v(*it) ; for(int32_t i=0;i<v.size();++i) if(v[i].second != it.key()) { RsGxsMessageId sub_msg_id = v[i].second ; QMap<RsGxsMessageId,QVector<QPair<time_t,RsGxsMessageId> > >::iterator it2 = mPostVersions.find(sub_msg_id); if(it2 != mPostVersions.end()) { for(int32_t j=0;j<(*it2).size();++j) if((*it2)[j].second != sub_msg_id) // dont copy it, since it is already present at slot i v.append((*it2)[j]) ; mPostVersions.erase(it2) ; // it2 is never equal to it } } } // Now remove from msg ids, all posts except the most recent one. And make the mPostVersion be indexed by the most recent version of the post, // which corresponds to the item in the tree widget. #ifdef DEBUG_FORUMS std::cerr << "Final post versions: " << std::endl; #endif QMap<RsGxsMessageId,QVector<QPair<time_t,RsGxsMessageId> > > mTmp; std::map<RsGxsMessageId,RsGxsMessageId> most_recent_versions ; for(QMap<RsGxsMessageId,QVector<QPair<time_t,RsGxsMessageId> > >::iterator it(mPostVersions.begin());it!=mPostVersions.end();++it) { #ifdef DEBUG_FORUMS std::cerr << "Original post: " << it.key() << std::endl; #endif // Finally, sort the posts from newer to older qSort((*it).begin(),(*it).end(),decreasing_time_comp) ; #ifdef DEBUG_FORUMS std::cerr << " most recent version " << (*it)[0].first << " " << (*it)[0].second << std::endl; #endif for(int32_t i=1;i<(*it).size();++i) { msgs.erase((*it)[i].second) ; #ifdef DEBUG_FORUMS std::cerr << " older version " << (*it)[i].first << " " << (*it)[i].second << std::endl; #endif } mTmp[(*it)[0].second] = *it ; // index the versions map by the ID of the most recent post. // Now make sure that message parents are consistent. Indeed, an old post may have the old version of a post as parent. So we need to change that parent // to the newest version. So we create a map of which is the most recent version of each message, so that parent messages can be searched in it. for(int i=1;i<(*it).size();++i) most_recent_versions[(*it)[i].second] = (*it)[0].second ; } mPostVersions = mTmp ; // The next step is to find the top level thread messages. These are defined as the messages without // any parent message ID. // this trick is needed because while we remove messages, the parents a given msg may already have been removed // and wrongly understand as a missing parent. std::map<RsGxsMessageId,RsGxsForumMsg> kept_msgs; for ( std::map<RsGxsMessageId,RsGxsForumMsg>::iterator msgIt = msgs.begin(); msgIt != msgs.end();++msgIt) { if(mFlatView || msgIt->second.mMeta.mParentId.isNull()) { /* add all threads */ if (wasStopped()) return; const RsGxsForumMsg& msg = msgIt->second; #ifdef DEBUG_FORUMS std::cerr << "GxsForumsFillThread::run() Adding TopLevel Thread: mId: " << msg.mMeta.mMsgId << std::endl; #endif QTreeWidgetItem *item = mParent->convertMsgToThreadWidget(msg, mUseChildTS, mFilterColumn,NULL); if (!mFlatView) threadStack.push_back(std::make_pair(msg.mMeta.mMsgId,item)) ; calculateExpand(msg, item); mItems.append(item); if (++step >= steps) { step = 0; emit progress(++pos, PROGRESSBAR_MAX); } } else { #ifdef DEBUG_FORUMS std::cerr << "GxsForumsFillThread::run() Storing kid " << msgIt->first << " of message " << msgIt->second.mMeta.mParentId << std::endl; #endif // The same missing parent may appear multiple times, so we first store them into a unique container. RsGxsMessageId parent_msg = msgIt->second.mMeta.mParentId; if(msgs.find(parent_msg) == msgs.end()) { // also check that the message is not versionned std::map<RsGxsMessageId,RsGxsMessageId>::const_iterator mrit = most_recent_versions.find(parent_msg) ; if(mrit != most_recent_versions.end()) parent_msg = mrit->second ; else missing_parents.insert(parent_msg); } kids_array[parent_msg].push_back(msgIt->first) ; kept_msgs.insert(*msgIt) ; } } msgs = kept_msgs; // Also create a list of posts by time, when they are new versions of existing posts. Only the last one will have an item created. // Add a fake toplevel item for the parent IDs that we dont actually have. for(std::set<RsGxsMessageId>::const_iterator it(missing_parents.begin());it!=missing_parents.end();++it) { // add dummy parent item QTreeWidgetItem *parent = mParent->generateMissingItem(*it); mItems.append( parent ); threadStack.push_back(std::make_pair(*it,parent)) ; } #ifdef DEBUG_FORUMS std::cerr << "GxsForumsFillThread::run() Processing stack:" << std::endl; #endif // Now use a stack to go down the hierarchy while (!threadStack.empty()) { std::pair<RsGxsMessageId, QTreeWidgetItem*> threadPair = threadStack.front(); threadStack.pop_front(); std::map<RsGxsMessageId, std::list<RsGxsMessageId> >::iterator it = kids_array.find(threadPair.first) ; #ifdef DEBUG_FORUMS std::cerr << "GxsForumsFillThread::run() Node: " << threadPair.first << std::endl; #endif if(it == kids_array.end()) continue ; if (wasStopped()) return; for(std::list<RsGxsMessageId>::const_iterator it2(it->second.begin());it2!=it->second.end();++it2) { // We iterate through the top level thread items, and look for which message has the current item as parent. // When found, the item is put in the thread list itself, as a potential new parent. std::map<RsGxsMessageId,RsGxsForumMsg>::iterator mit = msgs.find(*it2) ; if(mit == msgs.end()) { std::cerr << "GxsForumsFillThread::run() Cannot find submessage " << *it2 << " !!!" << std::endl; continue ; } const RsGxsForumMsg& msg(mit->second) ; #ifdef DEBUG_FORUMS std::cerr << "GxsForumsFillThread::run() adding sub_item " << msg.mMeta.mMsgId << std::endl; #endif QTreeWidgetItem *item = mParent->convertMsgToThreadWidget(msg, mUseChildTS, mFilterColumn, threadPair.second); calculateExpand(msg, item); /* add item to process list */ threadStack.push_back(std::make_pair(msg.mMeta.mMsgId, item)); if (++step >= steps) { step = 0; emit progress(++pos, PROGRESSBAR_MAX); } msgs.erase(mit); } #ifdef DEBUG_FORUMS std::cerr << "GxsForumsFillThread::run() Erasing entry " << it->first << " from kids tab." << std::endl; #endif kids_array.erase(it) ; // This is not strictly needed, but it improves performance by reducing the search space. } #ifdef DEBUG_FORUMS std::cerr << "Kids array now has " << kids_array.size() << " elements" << std::endl; for(std::map<RsGxsMessageId,std::list<RsGxsMessageId> >::const_iterator it(kids_array.begin());it!=kids_array.end();++it) { std::cerr << "Node " << it->first << std::endl; for(std::list<RsGxsMessageId>::const_iterator it2(it->second.begin());it2!=it->second.end();++it2) std::cerr << " " << *it2 << std::endl; } std::cerr << "GxsForumsFillThread::run() stopped: " << (wasStopped() ? "yes" : "no") << std::endl; #endif }