Added backend support for Comments.

- Single Vote per comment checks and balances added. 
 - Fixed up RsGxsImage stuff. (TODO move to proper REF counted Image).
 - Added GxsTokenQueue ticks were required.
 - Made GenExchange publicToken() fns  public, so that comments can use them.
 - Added OwnVote details to Comments 



git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@6224 b45a01b8-16f6-495d-af2f-9b41ad6348cc
This commit is contained in:
drbob 2013-03-15 20:46:49 +00:00
parent de9ec4776e
commit 1f6d6a4fc2
8 changed files with 432 additions and 9 deletions

View File

@ -362,6 +362,7 @@ protected:
return ok;
}
public:
/*!
* Assigns a token value to passed integer
* The status of the token can still be queried from request status feature
@ -388,6 +389,7 @@ protected:
*/
bool disposeOfPublicToken(const uint32_t &token);
protected:
/*!
* This gives access to the data store which hold msgs and groups
* for the service

View File

@ -48,6 +48,7 @@ class RsGxsImage
RsGxsImage();
~RsGxsImage();
RsGxsImage(const RsGxsImage& a); // TEMP use copy constructor and duplicate memory.
RsGxsImage &operator=(const RsGxsImage &a); // Need this as well?
//NB: Must make sure that we always use methods - to be consistent about malloc/free for this data.
static uint8_t *allocate(uint32_t size);
@ -64,6 +65,7 @@ static void release(void *data);
};
#define GXS_VOTE_NONE 0x0000
#define GXS_VOTE_DOWN 0x0001
#define GXS_VOTE_UP 0x0002
@ -87,6 +89,8 @@ class RsGxsComment
uint32_t mDownVotes;
double mScore;
uint32_t mOwnVote;
// This is filled in if detailed Comment Data is called.
std::list<RsGxsVote> mVotes;
};
@ -108,6 +112,7 @@ virtual bool createComment(uint32_t &token, RsGxsComment &comment) = 0;
virtual bool createVote(uint32_t &token, RsGxsVote &vote) = 0;
virtual bool acknowledgeComment(const uint32_t& token, std::pair<RsGxsGroupId, RsGxsMessageId>& msgId) = 0;
virtual bool acknowledgeVote(const uint32_t& token, std::pair<RsGxsGroupId, RsGxsMessageId>& msgId) = 0;
};

View File

@ -138,6 +138,10 @@ void p3GxsChannels::service_tick()
{
dummy_tick();
RsTickEvent::tick_events();
GxsTokenQueue::checkRequests();
mCommentService->comment_tick();
return;
}
@ -426,7 +430,16 @@ void p3GxsChannels::request_SpecificUnprocessedPosts(std::list<std::pair<RsGxsGr
uint32_t token = 0;
RsGenExchange::getTokenService()->requestGroupInfo(token, ansType, opts);
/* organise Ids how they want them */
GxsMsgReq msgIds;
std::list<std::pair<RsGxsGroupId, RsGxsMessageId> >::iterator it;
for(it = ids.begin(); it != ids.end(); it++)
{
std::vector<RsGxsMessageId> &vect_msgIds = msgIds[it->first];
vect_msgIds.push_back(it->second);
}
RsGenExchange::getTokenService()->requestMsgInfo(token, ansType, opts, msgIds);
GxsTokenQueue::queueRequest(token, GXSCHANNELS_UNPROCESSED_SPECIFIC);
}
@ -452,9 +465,9 @@ void p3GxsChannels::request_GroupUnprocessedPosts(const std::list<RsGxsGroupId>
void p3GxsChannels::load_SpecificUnprocessedPosts(const uint32_t &token)
{
std::vector<RsGxsChannelPost> posts;
if (!getRelatedPosts(token, posts))
if (!getPostData(token, posts))
{
std::cerr << "p3GxsChannels::load_GroupUnprocessedPosts ERROR";
std::cerr << "p3GxsChannels::load_SpecificUnprocessedPosts ERROR";
return;
}
@ -864,7 +877,7 @@ void p3GxsChannels::dummy_tick()
{
/* get the msg Id, and generate next snapshot */
RsGxsGrpMsgIdPair msgId;
if (!acknowledgeTokenMsg(mGenToken, msgId))
if (!acknowledgeVote(mGenToken, msgId))
{
std::cerr << " ERROR ";
std::cerr << std::endl;

View File

@ -118,6 +118,16 @@ virtual bool acknowledgeComment(const uint32_t& token, std::pair<RsGxsGroupId, R
}
virtual bool acknowledgeVote(const uint32_t& token, std::pair<RsGxsGroupId, RsGxsMessageId>& msgId)
{
if (mCommentService->acknowledgeVote(token, msgId))
{
return true;
}
return acknowledgeMsg(token, msgId);
}
// Overloaded from RsGxsIface.
virtual void subscribeToGroup(const RsGxsGroupId &groupId, bool subscribe);

View File

@ -26,6 +26,7 @@
#include "retroshare/rsgxscommon.h"
#include "services/p3gxscommon.h"
#include "serialiser/rsgxscommentitems.h"
#include "util/rsstring.h"
#include <stdio.h>
#include <stdlib.h>
@ -37,6 +38,7 @@ RsGxsComment::RsGxsComment()
mUpVotes = 0;
mDownVotes = 0;
mScore = 0;
mOwnVote = 0;
}
/********************************************************************************/
@ -67,6 +69,12 @@ RsGxsImage::~RsGxsImage()
}
RsGxsImage &RsGxsImage::operator=(const RsGxsImage &a)
{
copy(a.mData, a.mSize);
}
void RsGxsImage::take(uint8_t *data, uint32_t size)
{
std::cerr << "RsGxsImage(" << this << ")::take(" << (void *) data << "," << size << ")";
@ -112,7 +120,7 @@ void RsGxsImage::clear()
// Frees.
if (mData)
{
release(mData);
release(mData);
}
mData = NULL;
mSize = 0;
@ -138,17 +146,49 @@ RsGxsVote::RsGxsVote()
mVoteType = 0;
}
/********************************************************************************/
#define RSGXSCOMMENT_MAX_SERVICE_STRING 16
bool SSGxsComment::load(const std::string &input)
{
//char line[RSGXSCOMMENT_MAX_SERVICE_STRING];
int val;
mVoteValue = 0;
if (1 == sscanf(input.c_str(), "V:%d", &val))
{
mVoteValue = val;
}
return true;
}
std::string SSGxsComment::save() const
{
std::string output;
rs_sprintf(output, "V:%d", mVoteValue);
return output;
}
/********************************************************************************/
/******************* Startup / Tick ******************************************/
/********************************************************************************/
#define GXSCOMMENTS_VOTE_CHECK 0x0002
#define GXSCOMMENTS_VOTE_DONE 0x0003
p3GxsCommentService::p3GxsCommentService(RsGenExchange *exchange, uint16_t service_type)
: mExchange(exchange), mServiceType(service_type)
: GxsTokenQueue(exchange), mExchange(exchange), mServiceType(service_type)
{
return;
}
void p3GxsCommentService::comment_tick()
{
GxsTokenQueue::checkRequests();
}
bool p3GxsCommentService::getGxsCommentData(const uint32_t &token, std::vector<RsGxsComment> &comments)
{
std::cerr << "p3GxsCommentService::getGxsCommentData()";
@ -214,6 +254,11 @@ bool p3GxsCommentService::getGxsCommentData(const uint32_t &token, std::vector<R
}
}
cit->mScore = calculateBestScore(cit->mUpVotes, cit->mDownVotes);
/* convert serviceString -> mHaveVoted */
SSGxsComment ss;
ss.load(cit->mMeta.mServiceString);
cit->mOwnVote = ss.mVoteValue;
}
std::cerr << "p3GxsCommentService::getGxsCommentData() Found " << comments.size() << " Comments";
@ -235,6 +280,8 @@ bool p3GxsCommentService::getGxsCommentData(const uint32_t &token, std::vector<R
}
bool p3GxsCommentService::getGxsRelatedComments(const uint32_t &token, std::vector<RsGxsComment> &comments)
{
std::cerr << "p3GxsCommentService::getGxsRelatedComments()";
@ -297,6 +344,11 @@ bool p3GxsCommentService::getGxsRelatedComments(const uint32_t &token, std::vect
}
}
cit->mScore = calculateBestScore(cit->mUpVotes, cit->mDownVotes);
/* convert serviceString -> mHaveVoted */
SSGxsComment ss;
ss.load(cit->mMeta.mServiceString);
cit->mOwnVote = ss.mVoteValue;
}
std::cerr << "p3GxsCommentService::getGxsRelatedComments() Found " << comments.size() << " Comments";
@ -352,9 +404,272 @@ bool p3GxsCommentService::createGxsComment(uint32_t &token, RsGxsComment &msg)
}
bool p3GxsCommentService::createGxsVote(uint32_t &token, RsGxsVote &msg)
bool p3GxsCommentService::createGxsVote(uint32_t &token, RsGxsVote &vote)
{
std::cerr << "p3GxsChannels::createGxsVote() GroupId: " << msg.mMeta.mGroupId;
// NOTE Because we cannot do this operation immediately, we create a token,
// and monitor acknowledgeTokenMsg ... to return correct answer.
std::cerr << "p3GxsChannels::createGxsVote() GroupId: " << vote.mMeta.mGroupId;
std::cerr << std::endl;
/* vote must be associated with another item */
if (vote.mMeta.mThreadId.empty())
{
std::cerr << "p3GxsChannels::createGxsVote() ERROR Missing Required ThreadId";
std::cerr << std::endl;
return false;
}
if (vote.mMeta.mParentId.empty())
{
std::cerr << "p3GxsChannels::createGxsVote() ERROR Missing Required ParentId";
std::cerr << std::endl;
return false;
}
if (vote.mMeta.mGroupId.empty())
{
std::cerr << "p3GxsChannels::createGxsVote() ERROR Missing Required GroupId";
std::cerr << std::endl;
return false;
}
if (vote.mMeta.mAuthorId.empty())
{
std::cerr << "p3GxsChannels::createGxsVote() ERROR Missing Required AuthorId";
std::cerr << std::endl;
return false;
}
/* now queue */
RsGxsGrpMsgIdPair parentId(vote.mMeta.mGroupId, vote.mMeta.mParentId);
std::map<RsGxsGrpMsgIdPair, VoteHolder>::iterator it;
it = mPendingVotes.find(parentId);
if (it != mPendingVotes.end())
{
std::cerr << "p3GxsChannels::createGxsVote() ERROR Already a pending vote!";
std::cerr << std::endl;
return false;
}
token = mExchange->generatePublicToken();
mPendingVotes[parentId] = VoteHolder(vote, token);
// request parent, and queue for response.
RsTokReqOptions opts;
opts.mReqType = GXS_REQUEST_TYPE_MSG_META;
GxsMsgReq msgIds;
std::vector<RsGxsMessageId> &vect_msgIds = msgIds[parentId.first];
vect_msgIds.push_back(parentId.second);
uint32_t int_token;
mExchange->getTokenService()->requestMsgInfo(int_token, RS_TOKREQ_ANSTYPE_SUMMARY, opts, msgIds);
GxsTokenQueue::queueRequest(int_token, GXSCOMMENTS_VOTE_CHECK);
return true;
}
void p3GxsCommentService::load_PendingVoteParent(const uint32_t &token)
{
std::cerr << "p3GxsCommentService::load_PendingVoteParent()";
std::cerr << std::endl;
GxsMsgMetaMap msginfo;
if (!mExchange->getMsgMeta(token, msginfo))
{
std::cerr << "p3GxsCommentService::load_PendingVoteParent() ERROR Fetching Data";
std::cerr << std::endl;
std::cerr << "p3GxsCommentService::load_PendingVoteParent() Warning - this means LOST QUEUE ENTRY";
std::cerr << std::endl;
std::cerr << "p3GxsCommentService::load_PendingVoteParent() Need to track tokens - if this happens";
std::cerr << std::endl;
return;
}
GxsMsgMetaMap::iterator it;
for(it = msginfo.begin(); it != msginfo.end(); it++)
{
std::vector<RsMsgMetaData>::iterator mit;
for(mit = it->second.begin(); mit != it->second.end(); mit++)
{
/* find the matching Pending Vote */
RsMsgMetaData &meta = *mit;
std::cerr << "p3GxsCommentService::load_PendingVoteParent() recv (groupId: " << meta.mGroupId;
std::cerr << ", msgId: " << meta.mMsgId << ")";
std::cerr << std::endl;
RsGxsGrpMsgIdPair parentId(meta.mGroupId, meta.mMsgId);
std::map<RsGxsGrpMsgIdPair, VoteHolder>::iterator pit;
pit = mPendingVotes.find(parentId);
if (pit == mPendingVotes.end())
{
std::cerr << "p3GxsCommentService::load_PendingVoteParent() ERROR Finding Pending Vote";
std::cerr << std::endl;
continue;
}
/* check Vote serviceString */
SSGxsComment ss;
ss.load(meta.mServiceString);
RsGxsVote vote = pit->second.mVote;
if (ss.mVoteValue)
{
std::cerr << "p3GxsCommentService::load_PendingVoteParent() ERROR Already Voted";
std::cerr << std::endl;
std::cerr << "mGroupId: " << meta.mGroupId;
std::cerr << std::endl;
std::cerr << "mMsgId: " << meta.mMsgId;
std::cerr << std::endl;
pit->second.mStatus = VoteHolder::ERROR;
uint32_t status = RsTokenService::GXS_REQUEST_V2_STATUS_FAILED;
mExchange->updatePublicRequestStatus(pit->second.mReqToken, status);
continue;
}
std::cerr << "p3GxsCommentService::load_PendingVoteParent() submitting Vote";
std::cerr << std::endl;
ss.mVoteValue = vote.mVoteType;
std::string servString = ss.save();
uint32_t ss_token;
mExchange->setMsgServiceString(ss_token, parentId, servString);
uint32_t vote_token;
castVote(vote_token, vote);
GxsTokenQueue::queueRequest(vote_token, GXSCOMMENTS_VOTE_DONE);
pit->second.mVoteToken = vote_token;
pit->second.mStatus = VoteHolder::SUBMITTED;
}
}
}
void p3GxsCommentService::completeInternalVote(uint32_t &token)
{
std::cerr << "p3GxsChannels::completeInternalVote() token: " << token;
std::cerr << std::endl;
std::map<RsGxsGrpMsgIdPair, VoteHolder>::iterator it;
for (it = mPendingVotes.begin(); it != mPendingVotes.end(); it++)
{
if (it->second.mVoteToken == token)
{
uint32_t status = mExchange->getTokenService()->requestStatus(token);
mExchange->updatePublicRequestStatus(it->second.mReqToken, status);
std::cerr << "p3GxsChannels::completeInternalVote() Matched to PendingVote. status: " << status;
std::cerr << std::endl;
it->second.mStatus = VoteHolder::READY;
return;
}
}
std::cerr << "p3GxsChannels::completeInternalVote() ERROR Failed to match PendingVote";
std::cerr << std::endl;
return;
}
bool p3GxsCommentService::acknowledgeVote(const uint32_t& token, RsGxsGrpMsgIdPair& msgId)
{
std::cerr << "p3GxsChannels::acknowledgeVote() token: " << token;
std::cerr << std::endl;
std::map<RsGxsGrpMsgIdPair, VoteHolder>::iterator it;
for (it = mPendingVotes.begin(); it != mPendingVotes.end(); it++)
{
if (it->second.mReqToken == token)
{
std::cerr << "p3GxsChannels::acknowledgeVote() Matched to PendingVote";
std::cerr << std::endl;
bool ans = false;
if (it->second.mStatus == VoteHolder::READY)
{
std::cerr << "p3GxsChannels::acknowledgeVote() PendingVote = READY";
std::cerr << std::endl;
// Finally finish this Vote off.
ans = mExchange->acknowledgeTokenMsg(it->second.mVoteToken, msgId);
}
else if (it->second.mStatus == VoteHolder::ERROR)
{
std::cerr << "p3GxsChannels::acknowledgeVote() PendingVote = ERROR ???";
std::cerr << std::endl;
}
else
{
std::cerr << "p3GxsChannels::acknowledgeVote() PendingVote = OTHER STATUS";
std::cerr << std::endl;
}
std::cerr << "p3GxsChannels::acknowledgeVote() cleanup token & PendingVote";
std::cerr << std::endl;
mExchange->disposeOfPublicToken(it->second.mReqToken);
mPendingVotes.erase(it);
return ans;
}
}
std::cerr << "p3GxsChannels::acknowledgeVote() Failed to match PendingVote";
std::cerr << std::endl;
return false;
}
// Overloaded from GxsTokenQueue for Request callbacks.
void p3GxsCommentService::handleResponse(uint32_t token, uint32_t req_type)
{
std::cerr << "p3GxsCommentService::handleResponse(" << token << "," << req_type << ")";
std::cerr << std::endl;
// stuff.
switch(req_type)
{
case GXSCOMMENTS_VOTE_CHECK:
load_PendingVoteParent(token);
break;
case GXSCOMMENTS_VOTE_DONE:
completeInternalVote(token);
break;
default:
/* error */
std::cerr << "p3GxsCommentService::handleResponse() Unknown Request Type: " << req_type;
std::cerr << std::endl;
break;
}
}
bool p3GxsCommentService::castVote(uint32_t &token, RsGxsVote &msg)
{
std::cerr << "p3GxsChannels::castVote() GroupId: " << msg.mMeta.mGroupId;
std::cerr << std::endl;
RsGxsVoteItem* msgItem = new RsGxsVoteItem(mServiceType);
@ -366,6 +681,11 @@ bool p3GxsCommentService::createGxsVote(uint32_t &token, RsGxsVote &msg)
}
/********************************************************************************************/
/********************************************************************************************/

View File

@ -27,6 +27,7 @@
#include "retroshare/rsgxscommon.h"
#include "gxs/rsgenexchange.h"
#include "gxs/gxstokenqueue.h"
#include <stdio.h>
@ -35,18 +36,62 @@
* provides the implementation for any services requiring Comments.
*/
class p3GxsCommentService
class VoteHolder
{
public:
static const uint32_t ERROR = 0;
static const uint32_t QUEUED = 1;
static const uint32_t SUBMITTED = 2;
static const uint32_t READY = 3;
VoteHolder() :mVoteToken(0), mReqToken(0), mStatus(ERROR) { return; }
VoteHolder(const RsGxsVote &vote, uint32_t reqToken)
:mVote(vote), mVoteToken(0), mReqToken(reqToken), mStatus(QUEUED) { return; }
RsGxsVote mVote;
uint32_t mVoteToken;
uint32_t mReqToken;
uint32_t mStatus;
};
// NOTE this ServiceString class must be compatible with other classes
// that use ServiceString... i.e. it should scan to string for {V:%d }
// and remember the rest of the string - so it can maintain other settings.
// TODO.
class SSGxsComment
{
public:
SSGxsComment(): mVoteValue(0) { return; }
bool load(const std::string &input);
std::string save() const;
std::string mPreString;
std::string mPostString;
uint32_t mVoteValue;
};
class p3GxsCommentService: public GxsTokenQueue
{
public:
p3GxsCommentService(RsGenExchange *exchange, uint16_t service_type);
void comment_tick();
bool getGxsCommentData(const uint32_t &token, std::vector<RsGxsComment> &msgs);
bool getGxsRelatedComments(const uint32_t &token, std::vector<RsGxsComment> &msgs);
bool createGxsComment(uint32_t &token, RsGxsComment &msg);
bool createGxsVote(uint32_t &token, RsGxsVote &msg);
// Special Acknowledge.
bool acknowledgeVote(const uint32_t& token, RsGxsGrpMsgIdPair& msgId);
static double calculateBestScore(int upVotes, int downVotes);
@ -54,10 +99,25 @@ static double calculateBestScore(int upVotes, int downVotes);
void setGxsMessageReadStatus(uint32_t& token, const RsGxsGrpMsgIdPair& msgId, bool read);
#endif
protected:
// Overloaded from GxsTokenQueue for Request callbacks.
virtual void handleResponse(uint32_t token, uint32_t req_type);
private:
void load_PendingVoteParent(const uint32_t &token);
void completeInternalVote(uint32_t &token);
bool castVote(uint32_t &token, RsGxsVote &msg);
RsGenExchange *mExchange;
uint16_t mServiceType;
/* pending queue of Votes */
std::map<RsGxsGrpMsgIdPair, VoteHolder> mPendingVotes;
};

View File

@ -108,6 +108,9 @@ void p3Posted::service_tick()
{
dummy_tick();
RsTickEvent::tick_events();
mCommentService->comment_tick();
return;
}

View File

@ -112,6 +112,16 @@ virtual bool acknowledgeComment(const uint32_t& token, std::pair<RsGxsGroupId, R
return acknowledgeMsg(token, msgId);
}
virtual bool acknowledgeVote(const uint32_t& token, std::pair<RsGxsGroupId, RsGxsMessageId>& msgId)
{
if (mCommentService->acknowledgeVote(token, msgId))
{
return true;
}
return acknowledgeMsg(token, msgId);
}
private:
static uint32_t postedAuthenPolicy();