Newer
Older
// messages in the sub-board.
retObj.opSuccessful = false;
retObj.failureList[subBoardCode] = [];
}
}
msgBase.close();
}
else
{
// Failure to open the sub-board.
// Create an entry in retObj.failureList indexed by the
// sub-board code to indicate failure to delete all messages
// in the sub-board.
retObj.opSuccessful = false;
retObj.failureList[subBoardCode] = [];
14017
14018
14019
14020
14021
14022
14023
14024
14025
14026
14027
14028
14029
14030
14031
14032
14033
}
}
return retObj;
}
// For the DigDistMsgReader class: Returns the number of selected messages
function DigDistMsgReader_NumSelectedMessages()
{
var numSelectedMsgs = 0;
for (var subBoardCode in this.selectedMessages)
numSelectedMsgs += Object.keys(this.selectedMessages[subBoardCode]).length;
return numSelectedMsgs;
}

nightfox
committed
// Allows the user to forward a message to an email address or
// another user. This function is interactive with the user.
//
// Parameters:
// pMsgHeader: The header of the message being forwarded
// pMsgBody: The body text of the message
//
// Return value: A blank string on success or a string containing a
// message upon failure.
function DigDistMsgReader_ForwardMessage(pMsgHdr, pMsgBody)
{
if (typeof(pMsgHdr) != "object")
return "Invalid message header given";
var retStr = "";

Eric Oulashin
committed
console.print("\x01n");

nightfox
committed
console.crlf();
console.print("\x01cUser name/number/email address\x01h:\x01n");

nightfox
committed
console.crlf();
var msgDest = console.getstr(console.screen_columns - 1, K_LINE);
console.print("\x01n");

nightfox
committed
console.crlf();
if (msgDest.length > 0)
{
var tmpMsgbase = new MsgBase("mail");
if (tmpMsgbase.open())
{
// If the given message body is not a string, then get the
// message body from the messagebase.
if (typeof(pMsgBody) != "string")
{
var msgbase = new MsgBase(this.subBoardCode);
if (msgbase.open())
{
pMsgBody = msgbase.get_msg_body(false, pMsgHdr.number, false, false, true, true);
msgbase.close();
}
else
return "Unable to open the sub-board to get the message body";
}

nightfox
committed
14076
14077
14078
14079
14080
14081
14082
14083
14084
14085
14086
14087
14088
14089
14090
14091
14092
14093
14094
14095
14096
// Prepend some lines to the message body to describe where
// the message came from originally.
var newMsgBody = "This is a forwarded message from " + system.name + "\n";
newMsgBody += "Forwarded by: " + user.alias;
if (user.alias != user.name)
newMsgBody += " (" + user.name + ")";
newMsgBody += "\n";
if (this.subBoardCode == "mail")
newMsgBody += "From " + user.name + "'s personal email\n";
else
{
newMsgBody += "From sub-board: "
+ msg_area.grp_list[msg_area.sub[this.subBoardCode].grp_index].description
+ ", " + msg_area.sub[this.subBoardCode].description + "\n";
}
newMsgBody += "From: " + pMsgHdr.from + "\n";
newMsgBody += "To: " + pMsgHdr.to + "\n";
newMsgBody += "Subject: " + pMsgHdr.subject + "\n";
newMsgBody += "==================================\n\n";
newMsgBody += pMsgBody;
// Ask whether to edit the message before forwarding it,
14098
14099
14100
14101
14102
14103
14104
14105
14106
14107
14108
14109
14110
14111
14112
14113
14114
14115
14116
14117
14118
14119
14120
14121
14122
14123
14124
14125
14126
14127
14128
14129
14130
14131
// and use console.editfile(filename) to edit it.
if (!console.noyes("Edit the message before sending"))
{
var baseWorkDir = system.node_dir + "DDMsgReader_Temp";
deltree(baseWorkDir + "/");
if (mkdir(baseWorkDir))
{
// TODO: Let the user edit the message, then read it
// and set newMsgBody to it
var tmpMsgFilename = baseWorkDir + "/message.txt";
// Write the current message to the file
var wroteMsgToTmpFile = false;
var outFile = new File(tmpMsgFilename);
if (outFile.open("w"))
{
wroteMsgToTmpFile = outFile.write(newMsgBody, newMsgBody.length);
outFile.close();
}
if (wroteMsgToTmpFile)
{
// Let the user edit the file, and if successful,
// read it in to newMsgBody
if (console.editfile(tmpMsgFilename))
{
var inFile = new File(tmpMsgFilename);
if (inFile.open("r"))
{
newMsgBody = inFile.read(inFile.length);
inFile.close();
}
}
}
else
{
console.print("\x01n\x01cFailed to write message to a file for editing\x01n");
console.crlf();
console.pause();
}
}
else
{
console.print("\x01n\x01cCouldn't create temporary directory\x01n");
console.crlf();
console.pause();
}
}
// End New (editing message)

nightfox
committed
// Create part of a header object which will be used when saving/sending
// the message. The destination ("to" informatoin) will be filled in
// according to the destination type.
var destMsgHdr = { to_net_type: NET_NONE, from: user.name,
replyto: user.name, subject: "Fwd: " + pMsgHdr.subject };
if (user.netmail.length > 0)

Eric Oulashin
committed
{

nightfox
committed
destMsgHdr.replyto_net_addr = user.netmail;

Eric Oulashin
committed
}

nightfox
committed
else

Eric Oulashin
committed
{

nightfox
committed
destMsgHdr.replyto_net_addr = user.email;

Eric Oulashin
committed
}

nightfox
committed
//destMsgHdr.when_written_time =
//destMsgHdr.when_written_zone = system.timezone;
//destMsgHdr.when_written_zone_offset =
var confirmedForwardMsg = true;
// If the destination is in the format anything@anything, then
// accept it as the message destination. It could be an Internet
// address (someone@somewhere.com), FidoNet address (sysop@1:327/4),
// or a QWK address (someone@HOST).
// We could specifically use gEmailRegex and gFTNEmailregex to test
// msgDest, but just using those would be too restrictive.
if (/^.*@.*$/.test(msgDest))

nightfox
committed
{
confirmedForwardMsg = console.yesno("Forward via email to " + msgDest);
if (confirmedForwardMsg)
{
console.print("\x01n\x01cForwarding via email to " + msgDest + "\x01n");

nightfox
committed
console.crlf();
destMsgHdr.to = msgDest;
destMsgHdr.to_net_addr = msgDest;

Eric Oulashin
committed
destMsgHdr.to_net_type = netaddr_type(msgDest);

nightfox
committed
}
}
else
{
// See if what the user entered is a user number/name/alias
var userNum = 0;
if (/^[0-9]+$/.test(msgDest))
{
userNum = +msgDest;
// Determine if the user entered a valid user number
var lastUserNum = (system.lastuser == undefined ? system.stats.total_users : system.lastuser + 1);
if ((userNum < 1) || (userNum >= lastUserNum))
{
userNum = 0;
console.print("\x01h\x01y* Invalid user number (" + msgDest + ")\x01n");

nightfox
committed
14196
14197
14198
14199
14200
14201
14202
14203
14204
14205
14206
14207
14208
14209
14210
14211
14212
14213
14214
14215
14216
14217
console.crlf();
}
}
else // Try to get a user number assuming msgDest is a username/alias
userNum = system.matchuser(msgDest, true);
// If we have a valid user number, then we can forward the message.
if (userNum > 0)
{
var destUser = new User(userNum);
confirmedForwardMsg = console.yesno("Forward to " + destUser.alias + " (user " + destUser.number + ")");
if (confirmedForwardMsg)
{
destMsgHdr.to = destUser.alias;
// If the destination user has an Internet email address,
// ask the user if they want to send to the destination
// user's Internet email address
var sendToNetEmail = false;
if (destUser.netmail.length > 0)
{
sendToNetEmail = !console.noyes("Send to the user's Internet email (" + destUser.netmail + ")");
if (sendToNetEmail)
{
console.print("\x01n\x01cForwarding to " + destUser.netmail + "\x01n");

nightfox
committed
console.crlf();
destMsgHdr.to = destUser.name;
destMsgHdr.to_net_addr = destUser.netmail;
destMsgHdr.to_net_type = NET_INTERNET;
}
}
if (!sendToNetEmail)
{
console.print("\x01n\x01cForwarding to " + destUser.alias + "\x01n");

nightfox
committed
console.crlf();
destMsgHdr.to_ext = destUser.number;

Eric Oulashin
committed
destMsgHdr.to_net_type = NET_NONE;

nightfox
committed
}
}
}
else
{
confirmedForwardMsg = false;
console.print("\x01h\x01y* Unknown destination\x01n");

nightfox
committed
console.crlf();
}
}
var savedMsg = true;
if (confirmedForwardMsg)
savedMsg = tmpMsgbase.save_msg(destMsgHdr, newMsgBody);
else
{
console.print("\x01n\x01cCanceled\x01n");

nightfox
committed
console.crlf();
}
tmpMsgbase.close();
if (!savedMsg)
{
console.print("\x01h\x01y* Failed to send the message!\x01n");

nightfox
committed
console.crlf();
}
// Pause for user input so the user can see the messages written
console.pause();
}
else
retStr = "Failed to open email messagebase";
}
else
{
console.print("\x01n\x01cCanceled\x01n");

nightfox
committed
console.crlf();
console.pause();
}
return retStr;
}

Eric Oulashin
committed
14273
14274
14275
14276
14277
14278
14279
14280
14281
14282
14283
14284
14285
14286
14287
14288
14289
14290
14291
14292
14293
14294
14295
14296
14297
14298
14299
14300
14301
14302
14303
14304
14305
14306
14307
14308
14309
14310
14311
14312
14313
14314
14315
14316
14317
14318
14319
14320
function printMsgHdrInfo(pMsgHdr)
{
if (typeof(pMsgHdr) != "object")
return;
for (var prop in pMsgHdr)
{
if (prop == "to_net_type")
print(prop + ": " + toNetTypeToStr(pMsgHdr[prop]));
else
console.print(prop + ": " + pMsgHdr[prop]);
console.crlf();
}
}
function toNetTypeToStr(toNetType)
{
var toNetTypeStr = "Unknown";
if (typeof(toNetType) == "number")
{
switch (toNetType)
{
case NET_NONE:
toNetTypeStr = "Local";
break;
case NET_UNKNOWN:
toNetTypeStr = "Unknown networked";
break;
case NET_FIDO:
toNetTypeStr = "FidoNet";
break;
case NET_POSTLINK:
toNetTypeStr = "PostLink";
break;
case NET_QWK:
toNetTypeStr = "QWK";
break;
case NET_INTERNET:
toNetTypeStr = "Internet";
break;
default:
toNetTypeStr = "Unknown";
break;
}
}
return toNetTypeStr;
}
// For the DigDistMsgReader class: Lets the user vote on a message
//
// Parameters:
// pMsgHdr: The header of the mesasge being voted on
// pRemoveNLsFromVoteText: Optional boolean - Whether or not to remove newlines
// (and carriage returns) from the voting text from
// text.dat. Defaults to false.
//
// Return value: An object with the following properties:
// BBSHasVoteFunction: Boolean - Whether or not the system has
// the vote_msg function
// savedVote: Boolean - Whether or not the vote was saved
// userQuit: Boolean - Whether or not the user quit and didn't vote
// errorMsg: String - An error message, if something went wrong
// mnemonicsRequiredForErrorMsg: Boolean - Whether or not mnemonics is required to print the error message
// updatedHdr: The updated message header containing vote information.
// If something went wrong, this will be null.
function DigDistMsgReader_VoteOnMessage(pMsgHdr, pRemoveNLsFromVoteText)
var retObj = {
BBSHasVoteFunction: false,
savedVote: false,
userQuit: false,
errorMsg: "",
mnemonicsRequiredForErrorMsg: false,
updatedHdr: null
};

nightfox
committed
// Don't allow voting for personal email
if (this.subBoardCode == "mail")
{
retObj.errorMsg = "Can not vote on personal email";
return retObj;
}
// Check whether the user has the voting restiction
if ((user.security.restrictions & UFLAG_V) == UFLAG_V)
{
// Use the line from text.dat that says the user is not allowed to vote,
// and remove newlines from it.
retObj.errorMsg = "\x01n" + bbs.text(typeof(R_Voting) != "undefined" ? R_Voting : 781).replace("\r\n", "").replace("\n", "").replace("\N", "").replace("\r", "").replace("\R", "").replace("\R\n", "").replace("\r\N", "").replace("\R\N", "");
return retObj;
}

nightfox
committed
var msgbase = new MsgBase(this.subBoardCode);
if (!msgbase.open())
{
return retObj;
}
// If the message vote function is not defined in the running verison of Synchronet,
// then just return.
retObj.BBSHasVoteFunction = (typeof(msgbase.vote_msg) == "function");
if (!retObj.BBSHasVoteFunction)
{
msgbase.close();
return retObj;
var removeNLsFromVoteText = (typeof(pRemoveNLsFromVoteText) === "boolean" ? pRemoveNLsFromVoteText : false)
// See if voting is allowed in the current sub-board
if ((msg_area.sub[this.subBoardCode].settings & SUB_NOVOTING) == SUB_NOVOTING)
{
retObj.errorMsg = bbs.text(typeof(VotingNotAllowed) != "undefined" ? VotingNotAllowed : 779);
if (removeNLsFromVoteText)
retObj.errorMsg = retObj.errorMsg.replace("\r\n", "").replace("\n", "").replace("\N", "").replace("\r", "").replace("\R", "").replace("\R\n", "").replace("\r\N", "").replace("\R\N", "");
retObj.mnemonicsRequiredForErrorMsg = true;
msgbase.close();
return retObj;
}
// If the message is a poll question and has the maximum number of votes
// already or is closed for voting, then don't let the user vote on it.
if ((pMsgHdr.attr & MSG_POLL) == MSG_POLL)
{
var userVotedMaxVotes = false;
var numVotes = (pMsgHdr.hasOwnProperty("votes") ? pMsgHdr.votes : 0);
if (typeof(msgbase.how_user_voted) === "function")
{
var votes = msgbase.how_user_voted(pMsgHdr.number, (msgbase.cfg.settings & SUB_NAME) == SUB_NAME ? user.name : user.alias);
// TODO: I'm not sure if this 'if' section is correct anymore for
// the latest 3.17 build of Synchronet (August 14, 2017)
// Digital Man said:
// In a poll message, the "votes" property specifies the maximum number of
// answers/votes per ballot (0 is the equivalent of 1).
// Max votes testing? :
// userVotedMaxVotes = (votes == pMsgHdr.votes);
14409
14410
14411
14412
14413
14414
14415
14416
14417
14418
14419
14420
14421
14422
14423
14424
14425
14426
14427
14428
14429
14430
if (votes >= 0)
{
if ((votes == 0) || (votes == 1))
userVotedMaxVotes = (votes == 3); // (1 << 0) | (1 << 1);
else
{
userVotedMaxVotes = true;
for (var voteIdx = 0; voteIdx <= numVotes; ++voteIdx)
{
if (votes && (1 << voteIdx) == 0)
{
userVotedMaxVotes = false;
break;
}
}
}
}
}
var pollIsClosed = ((pMsgHdr.auxattr & POLL_CLOSED) == POLL_CLOSED);
if (pollIsClosed)
{
retObj.errorMsg = "This poll is closed";
msgbase.close();
return retObj;
}
else if (userVotedMaxVotes)
{
retObj.errorMsg = bbs.text(typeof(VotedAlready) != "undefined" ? VotedAlready : 780);
if (removeNLsFromVoteText)
retObj.errorMsg = retObj.errorMsg.replace("\r\n", "").replace("\n", "").replace("\N", "").replace("\r", "").replace("\R", "").replace("\R\n", "").replace("\r\N", "").replace("\R\N", "");
retObj.mnemonicsRequiredForErrorMsg = true;
msgbase.close();
return retObj;
}
}
// If the user has voted on this message already, then set an error message and return.
if (this.HasUserVotedOnMsg(pMsgHdr.number))
{
retObj.errorMsg = bbs.text(typeof(VotedAlready) != "undefined" ? VotedAlready : 780);
if (removeNLsFromVoteText)
retObj.errorMsg = retObj.errorMsg.replace("\r\n", "").replace("\n", "").replace("\N", "").replace("\r", "").replace("\R", "").replace("\R\n", "").replace("\r\N", "").replace("\R\N", "");
retObj.mnemonicsRequiredForErrorMsg = true;
msgbase.close();
return retObj;
}
// New MsgBase method: vote_msg(). it takes a message header object
// (like save_msg), except you only need a few properties, in order of
// importarnce:
// attr: you need to have this set to MSG_UPVOTE, MSG_DOWNVOTE, or MSG_VOTE
// thread_back or reply_id: either of these must be set to indicate msg to vote on
// from: name of voter
// from_net_type and from_net_addr: if applicable
// Do some initial setup of the header for the vote message to be
// saved to the messagebase
var voteMsgHdr = {
thread_back: pMsgHdr.number,
reply_id: pMsgHdr.id,
from: (msgbase.cfg.settings & SUB_NAME) == SUB_NAME ? user.name : user.alias
};
if (pMsgHdr.from.hasOwnProperty("from_net_type"))
{
voteMsgHdr.from_net_type = pMsgHdr.from_net_type;
if (pMsgHdr.from_net_type != NET_NONE)
voteMsgHdr.from_net_addr = user.email;
}
// Input vote options from the user differently depending on whether
// the message is a poll or not
if ((pMsgHdr.attr & MSG_POLL) == MSG_POLL)
{
if (pMsgHdr.hasOwnProperty("field_list"))
{
console.clear("\x01n");

nightfox
committed
var selectHdr = bbs.text(typeof(BallotHdr) != "undefined" ? BallotHdr : 791);
printf("\x01n" + selectHdr + "\x01n", pMsgHdr.subject);
var optionFormatStr = "\x01n\x01c\x01h%2d\x01n\x01c: \x01h%s\x01n";
var optionNum = 1;
for (var fieldI = 0; fieldI < pMsgHdr.field_list.length; ++fieldI)
{
if (pMsgHdr.field_list[fieldI].type == SMB_POLL_ANSWER)
{
printf(optionFormatStr, optionNum++, pMsgHdr.field_list[fieldI].data);
console.crlf();
}
}
console.crlf();
// Get & process the selection from the user
var userInputNum = 0;
if (pMsgHdr.votes > 1)
{
// Support multiple answers from the user
console.print("\x01n\x01gYour vote numbers, separated by commas, up to \x01h" + pMsgHdr.votes + "\x01n\x01g (Blank/Q=Quit):");
console.crlf();
console.print("\x01c\x01h");
var userInput = consoleGetStrWithValidKeys("0123456789,Q", null, false);
14507
14508
14509
14510
14511
14512
14513
14514
14515
14516
14517
14518
14519
14520
14521
14522
14523
14524
14525
14526
14527
14528
14529
14530
14531
14532
14533
14534
14535
14536
14537
14538
14539
14540
14541
14542
14543
14544
14545
14546
if ((userInput.length > 0) && (userInput.toUpperCase() != "Q"))
{
var userAnswers = userInput.split(",");
if (userAnswers.length > 0)
{
// Generate confirmation text and an array of numbers
// representing the user's choices, up to the number
// of responses allowed
var confirmText = "Vote ";
var voteNumbers = [];
for (var i = 0; (i < userAnswers.length) && (i < pMsgHdr.votes); ++i)
{
// Trim any whitespace from the user's response
userAnswers[i] = trimSpaces(userAnswers[i], true, true, true);
if (/^[0-9]+$/.test(userAnswers[i]))
{
voteNumbers.push(+userAnswers[i]);
confirmText += userAnswers[i] + ",";
}
}
// If the confirmation text has a trailing comma, remove it
if (/,$/.test(confirmText))
confirmText = confirmText.substr(0, confirmText.length-1);
// Confirm from the user and submit their vote if they say yes
if (voteNumbers.length > 0)
{
if (console.yesno(confirmText))
{
userInputNum = 0;
for (var i = 0; i < voteNumbers.length; ++i)
userInputNum |= (1 << (voteNumbers[i]-1));
}
else
retObj.userQuit = true;
}
}
}
else
retObj.userQuit = true;
}
else
{
// Get the selection prompt text from text.dat and replace the %u or %d with
// the number 1 (default option)
var selectPromptText = bbs.text(SelectItemWhich);
selectPromptText = selectPromptText.replace(/%[uU]/, 1).replace(/%[dD]/, 1);
console.mnemonics(selectPromptText);
var maxNum = optionNum - 1;
userInputNum = console.getnum(maxNum);
if (userInputNum == -1) // The user chose Q to quit
retObj.userQuit = true;
console.print("\x01n");
}
if (!retObj.userQuit)
{
voteMsgHdr.attr = MSG_VOTE;
voteMsgHdr.votes = userInputNum;
}
}
}
else
// The message is not a poll - Prompt for up/downvote
if ((typeof(MSG_UPVOTE) != "undefined") && (typeof(MSG_DOWNVOTE) != "undefined"))
{
var voteAttr = 0;
// Get text line 783 to prompt for voting
var textDatText = bbs.text(typeof(VoteMsgUpDownOrQuit) != "undefined" ? VoteMsgUpDownOrQuit : 783);
if (removeNLsFromVoteText)
textDatText = textDatText.replace("\r\n", "").replace("\n", "").replace("\N", "").replace("\r", "").replace("\R", "").replace("\R\n", "").replace("\r\N", "").replace("\R\N", "");
console.print("\x01n");
console.mnemonics(textDatText);
console.print("\x01n");
// Using getAllowedKeyWithMode() instead of console.getkeys() so we
// can control the input mode better, so it doesn't output a CRLF
switch (getAllowedKeyWithMode("UDQ" + KEY_UP + KEY_DOWN, K_NOCRLF|K_NOSPIN))
{
case "U":
case KEY_UP:
voteAttr = MSG_UPVOTE;
break;
case "D":
case KEY_DOWN:
voteAttr = MSG_DOWNVOTE;
break;
case "Q":
default:
retObj.userQuit = true;
break;
}
// If the user voted, then save the user's vote in the attr property
// in the header
if (voteAttr != 0)
voteMsgHdr.attr = voteAttr;
}
else
retObj.errorMsg = "MSG_UPVOTE & MSG_DOWNVOTE are not defined";
}
// If the user hasn't quit and there is no error message, then save the vote
// message header
if (!retObj.userQuit && (retObj.errorMsg.length == 0))
{
Eric Oulashin
committed
console.print("\x01n Submitting..");
retObj.savedVote = msgbase.vote_msg(voteMsgHdr);
// If the save was successful, then update
// this.hdrsForCurrentSubBoard with the updated
// message header (for the message that was read)
if (retObj.savedVote)
{
if (this.hdrsForCurrentSubBoardByMsgNum.hasOwnProperty(pMsgHdr.number))
{
var originalMsgIdx = this.hdrsForCurrentSubBoardByMsgNum[pMsgHdr.number];

Eric Oulashin
committed
var tmpHdrs = msgbase.get_all_msg_headers(true);
if (tmpHdrs.hasOwnProperty(pMsgHdr.number))

Eric Oulashin
committed
this.hdrsForCurrentSubBoard[originalMsgIdx] = tmpHdrs[pMsgHdr.number];
// Originally, this script assigned retObj.updatedHdr as follows:
//retObj.updatedHdr = pMsgHdr;
// However, after an update, there were a couple errors that total_votes and upvotes
// were read-only, so it wuldn't assign to them, so now we copy pMsgHdr this way:
retObj.updatedHdr = {};
for (var prop in pMsgHdr)
retObj.updatedHdr[prop] = pMsgHdr[prop];
if (this.hdrsForCurrentSubBoard[originalMsgIdx].hasOwnProperty("total_votes"))
retObj.updatedHdr.total_votes = this.hdrsForCurrentSubBoard[originalMsgIdx].total_votes;
if (this.hdrsForCurrentSubBoard[originalMsgIdx].hasOwnProperty("upvotes"))
retObj.updatedHdr.upvotes = this.hdrsForCurrentSubBoard[originalMsgIdx].upvotes;
if (this.hdrsForCurrentSubBoard[originalMsgIdx].hasOwnProperty("tally"))
retObj.updatedHdr.tally = this.hdrsForCurrentSubBoard[originalMsgIdx].tally;
}
}
}
else
{
// Failed to save the vote
retObj.errorMsg = "Failed to save your vote";
}
msgbase.close();
return retObj;
}
// For the DigDistMsgReader class: Checks to see whether a user has voted on a message.
// The message must belong to the currently-open sub-board.
//
// Parameters:
// pMsgNum: The message number
// pUser: Optional - A user account to check. If omitted, the current logged-in
// user will be used.
function DigDistMsgReader_HasUserVotedOnMsg(pMsgNum, pUser)
{

nightfox
committed
// Don't do this for personal email
if (this.subBoardCode == "mail")
return false;
// Thanks to echicken for explaining how to check this. To check a user's
// vote, use MsgBase.how_user_voted().
/*
The return value will be:
0 - user hasn't voted
1 - upvoted
2 - downvoted
Or, if the message was a poll, it's a bitfield:
if (votes&(1<<2)) {
// User selected answer 2
}
*/
var userHasVotedOnMsg = false;
var msgbase = new MsgBase(this.subBoardCode);
if (msgbase.open())
{
if (typeof(msgbase.how_user_voted) === "function")
{
var votes = 0;
if (typeof(pUser) == "object")
votes = msgbase.how_user_voted(pMsgNum, (msgbase.cfg.settings & SUB_NAME) == SUB_NAME ? pUser.name : pUser.alias);
else
votes = msgbase.how_user_voted(pMsgNum, (msgbase.cfg.settings & SUB_NAME) == SUB_NAME ? user.name : user.alias);
userHasVotedOnMsg = (votes > 0);
}
msgbase.close();
}
return userHasVotedOnMsg;
}

nightfox
committed
// Gets information about the upvotes and downvotes for a message.
// If the user is a sysop, this will also get who voted on the message.
//
// Parameters:
// pMsgHdr: A header of a message that has upvotes & downvotes
//
// Return value: An array of strings containing information about the upvotes,
// downvotes, tally, and (if the user is a sysop) who submitted
// votes on the message.
function DigDistMsgReader_GetUpvoteAndDownvoteInfo(pMsgHdr)
{
// If the message header doesn't have the "total_votes" or "upvotes" properties,
// then there's no vote information, so just return an empty array.
if (!pMsgHdr.hasOwnProperty("total_votes") || !pMsgHdr.hasOwnProperty("upvotes"))
return [];
var msgVoteInfo = getMsgUpDownvotesAndScore(pMsgHdr);

nightfox
committed
var voteInfo = [];
voteInfo.push("Upvotes: " + msgVoteInfo.upvotes);
voteInfo.push("Downvotes: " + msgVoteInfo.downvotes);
voteInfo.push("Score: " + msgVoteInfo.voteScore);

nightfox
committed
if (pMsgHdr.hasOwnProperty("tally"))
voteInfo.push("Tally: " + pMsgHdr.tally);
// If the user is the sysop, then also add the names of people who
// voted on the message.
Eric Oulashin
committed
if (user.is_sysop)

nightfox
committed
{
// Check all the messages in the messagebase after the current one
// to find response messages
var msgbase = new MsgBase(this.subBoardCode);
if (msgbase.open())

nightfox
committed
{

Eric Oulashin
committed
// Pass true to get_all_msg_headers() to tell it to return vote messages
// (the parameter was introduced in Synchronet 3.17+)
var tmpHdrs = msgbase.get_all_msg_headers(true);
for (var tmpProp in tmpHdrs)

nightfox
committed
{

Eric Oulashin
committed
if (tmpHdrs[tmpProp] == null)
continue;
// If this header's thread_back or reply_id matches the poll message
// number, then append the 'user voted' string to the message body.
if ((tmpHdrs[tmpProp].thread_back == pMsgHdr.number) || (tmpHdrs[tmpProp].reply_id == pMsgHdr.id))

nightfox
committed
{

Eric Oulashin
committed
var tmpMessageBody = msgbase.get_msg_body(false, tmpHdrs[tmpProp].number, false, false, true, true);
if ((tmpHdrs[tmpProp].field_list.length == 0) && (tmpMessageBody.length == 0))

nightfox
committed
{

Eric Oulashin
committed
var msgWrittenLocalTime = msgWrittenTimeToLocalBBSTime(tmpHdrs[tmpProp]);
var voteDate = strftime("%a %b %d %Y %H:%M:%S", msgWrittenLocalTime);
voteInfo.push("\x01n\x01c\x01h" + tmpHdrs[tmpProp].from + "\x01n\x01c voted on this message on " + voteDate + "\x01n");

nightfox
committed
}
}
}
msgbase.close();

nightfox
committed
}
}
return voteInfo;
}
// For the DigDistMsgReader class: Gets the body (text) of a message. If it's
// a poll, this method will format the message body with poll results. Otherwise,
// this method will simply get the message body.
//
// Parameters:
// pMsgHeader: The message header
//
// Return value: The poll results, colorized. If the message is not a
// poll message, then an empty string will be returned.
function DigDistMsgReader_GetMsgBody(pMsgHdr)
{
var msgbase = new MsgBase(this.subBoardCode);
if (!msgbase.open())
return "";
14769
14770
14771
14772
14773
14774
14775
14776
14777
14778
14779
14780
14781
14782
14783
14784
14785
var msgBody = "";
if ((typeof(MSG_TYPE_POLL) != "undefined") && (pMsgHdr.type & MSG_TYPE_POLL) == MSG_TYPE_POLL)
{
// A poll is intended to be parsed (and displayed) using on the header data. The
// (optional) comments are stored in the hdr.field_list[] with type values of
// SMB_COMMENT (now defined in sbbsdefs.js) and the available answers are in the
// field_list[] with type values of SMB_POLL_ANSWER.
// The 'comments' and 'answers' are also a part of the message header, so you can
// grab them separately, then format and display them however you want. You can
// find them in the header.field_list array; each element in that array should be
// an object with a 'type' and a 'data' property. Relevant types here are
// SMB_COMMENT and SMB_POLL_ANSWER. (This is what I'm doing on the web, and I
// just ignore the message body for poll messages.)
if (pMsgHdr.hasOwnProperty("field_list"))
{
14786
14787
14788
14789
14790
14791
14792
14793
14794
14795
14796
14797
14798
14799
14800
14801
14802
14803
14804
// Figure out the longest length of the poll choices, with
// a maximum of 22 characters less than the terminal width.
// Use a minimum of 27 characters.
// That length will be used for the formatting strings for
// the poll results.
var voteOptDescLen = 0;
for (var fieldI = 0; fieldI < pMsgHdr.field_list.length; ++fieldI)
{
if (pMsgHdr.field_list[fieldI].type == SMB_POLL_ANSWER)
{
if (pMsgHdr.field_list[fieldI].data.length > voteOptDescLen)
voteOptDescLen = pMsgHdr.field_list[fieldI].data.length;
}
}
if (voteOptDescLen > console.screen_columns - 22)
voteOptDescLen = console.screen_columns - 22;
else if (voteOptDescLen < 27)
voteOptDescLen = 27;
// Format strings for outputting the voting option lines
var unvotedOptionFormatStr = "\x01n\x01c\x01h%2d\x01n\x01c: \x01w\x01h%-" + voteOptDescLen + "s [%-4d %6.2f%]\x01n";
var votedOptionFormatStr = "\x01n\x01c\x01h%2d\x01n\x01c: \x01" + "5\x01w\x01h%-" + voteOptDescLen + "s [%-4d %6.2f%]\x01n";
// Add up the total number of votes so that we can
// calculate vote percentages.
var totalNumVotes = 0;
if (pMsgHdr.hasOwnProperty("tally"))
{
for (var tallyI = 0; tallyI < pMsgHdr.tally.length; ++tallyI)
totalNumVotes += pMsgHdr.tally[tallyI];
}
// Go through field_list and append the voting options and stats to
// msgBody
var pollComment = "";
var optionNum = 1;
var numVotes = 0;
var votePercentage = 0;
var tallyIdx = 0;
for (var fieldI = 0; fieldI < pMsgHdr.field_list.length; ++fieldI)
{
if (pMsgHdr.field_list[fieldI].type == SMB_COMMENT)
pollComment += pMsgHdr.field_list[fieldI].data + "\r\n";
else if (pMsgHdr.field_list[fieldI].type == SMB_POLL_ANSWER)
{
// Figure out the number of votes on this option and the
// vote percentage
if (pMsgHdr.hasOwnProperty("tally"))
{
if (tallyIdx < pMsgHdr.tally.length)
{
numVotes = pMsgHdr.tally[tallyIdx];
votePercentage = (numVotes / totalNumVotes) * 100;
}
}
// Append to the message text
msgBody += format(numVotes == 0 ? unvotedOptionFormatStr : votedOptionFormatStr,
optionNum++, pMsgHdr.field_list[fieldI].data.substr(0, voteOptDescLen),
numVotes, votePercentage);
if (numVotes > 0)
msgBody += " " + CHECK_CHAR;
msgBody += "\r\n";
++tallyIdx;
}
}
if (pollComment.length > 0)
msgBody = pollComment + "\r\n" + msgBody;

nightfox
committed
// If voting is allowed in this sub-board and the current logged-in
// user has not voted on this message, then append some text saying
// how to vote.
var votingAllowed = ((this.subBoardCode != "mail") && (((msg_area.sub[this.subBoardCode].settings & SUB_NOVOTING) == 0)));
if (votingAllowed && !this.HasUserVotedOnMsg(pMsgHdr.number))
msgBody += "\x01n\r\n\x01gTo vote in this poll, press \x01w\x01h" + this.enhReaderKeys.vote + "\x01n\x01g now.\r\n";
// If the current logged-in user created this poll, then show the
// users who have voted on it so far.
var msgFromUpper = pMsgHdr.from.toUpperCase();
if ((msgFromUpper == user.name.toUpperCase()) || (msgFromUpper == user.handle.toUpperCase()))
{
// Check all the messages in the messagebase after the current one
// to find poll response messages

Eric Oulashin
committed
// Get the line from text.dat for writing who voted & when. It
// is a format string and should look something like this:
//"\r\n\x01n\x01hOn %s, in \x01c%s \x01n\x01c%s\r\n\x01h\x01m%s voted in your poll: \x01n\x01h%s\r\n" 787 PollVoteNotice
var userVotedInYourPollText = bbs.text(typeof(PollVoteNotice) != "undefined" ? PollVoteNotice : 787);
// Pass true to get_all_msg_headers() to tell it to return vote messages
// (the parameter was introduced in Synchronet 3.17+)
var tmpHdrs = msgbase.get_all_msg_headers(true);
for (var tmpProp in tmpHdrs)
{

Eric Oulashin
committed
if (tmpHdrs[tmpProp] == null)
continue;
// If this header's thread_back or reply_id matches the poll message
// number, then append the 'user voted' string to the message body.
if ((tmpHdrs[tmpProp].thread_back == pMsgHdr.number) || (tmpHdrs[tmpProp].reply_id == pMsgHdr.id))
{

Eric Oulashin
committed
var msgWrittenLocalTime = msgWrittenTimeToLocalBBSTime(tmpHdrs[tmpProp]);
var voteDate = strftime("%a %b %d %Y %H:%M:%S", msgWrittenLocalTime);
var grpName = "";
var msgbaseCfgName = "";
var msgbase = new MsgBase(this.subBoardCode);
if (msgbase.open())
{

Eric Oulashin
committed
grpName = msgbase.cfg.grp_name;
msgbaseCfgName = msgbase.cfg.name;
msgbase.close();
}

Eric Oulashin
committed
msgBody += format(userVotedInYourPollText, voteDate, grpName, msgbaseCfgName, tmpHdrs[tmpProp].from, pMsgHdr.subject);
}
}
}
}
}
else
{
// If the message is UTF8 and the terminal is not UTF8-capable, then convert
// the text to cp437.
msgBody = msgbase.get_msg_body(false, pMsgHdr.number, false, false, true, true);
if (pMsgHdr.hasOwnProperty("is_utf8") && pMsgHdr.is_utf8)
{
var userConsoleSupportsUTF8 = false;
if (typeof(USER_UTF8) != "undefined")
userConsoleSupportsUTF8 = console.term_supports(USER_UTF8);
if (!userConsoleSupportsUTF8)
msgBody = utf8_cp437(msgBody);
}
// Remove any initial coloring from the message body, which can color the whole message
msgBody = removeInitialColorFromMsgBody(msgBody);
// For HTML-formatted messages, convert HTML entities
if (pMsgHdr.hasOwnProperty("text_subtype") && pMsgHdr.text_subtype.toLowerCase() == "html")
{
msgBody = html2asc(msgBody);
// Remove excessive blank lines after HTML-translation
msgBody = msgBody.replace(/\r\n\r\n\r\n/g, '\r\n\r\n');
}
}
msgbase.close();
// Remove any Synchronet pause codes that might exist in the message
msgBody = msgBody.replace("\x01p", "").replace("\x01P", "");
// If the user is a sysop, this is a moderated message area, and the message
// hasn't been validated, then prepend the message with a message to let the
// sysop now know to validate it.
if (this.subBoardCode != "mail")
{
Eric Oulashin
committed
if (user.is_sysop && msg_area.sub[this.subBoardCode].is_moderated && ((pMsgHdr.attr & MSG_VALIDATED) == 0))
{
var validateNotice = "\x01n\x01h\x01yThis is an unvalidated message in a moderated area. Press "
+ this.enhReaderKeys.validateMsg + " to validate it.\r\n\x01g";
for (var i = 0; i < 79; ++i)
validateNotice += HORIZONTAL_SINGLE;
validateNotice += "\x01n\r\n";
msgBody = validateNotice + msgBody;
}
}
return msgBody;
}
// For the DigDistMsgReader class: Refreshes a message header in one of the
// internal message arrays.
//
// Parameters:
// pMsgNum: The number of the message to replace
function DigDistMsgReader_RefreshMsgHdrInArrays(pMsgNum)
{
var msgbase = new MsgBase(this.subBoardCode);
if (!msgbase.open())
return;
if (this.msgSearchHdrs.hasOwnProperty(this.subBoardCode) &&
(this.msgSearchHdrs[this.subBoardCode].indexed.length > 0))
{
for (var i = 0; i < this.msgSearchHdrs[this.subBoardCode].indexed.length; ++i)
{
if (this.msgSearchHdrs[this.subBoardCode].indexed[i].number == pMsgNum)
{
var newMsgHdr = msgbase.get_msg_header(false, pMsgNum, true);
if (newMsgHdr != null)
this.msgSearchHdrs[this.subBoardCode].indexed[i] = newMsgHdr;
break;
}
}
}
else if (this.hdrsForCurrentSubBoard.length > 0)
{
if (this.hdrsForCurrentSubBoardByMsgNum.hasOwnProperty(pMsgNum))
{

Eric Oulashin
committed
var msgHdrs = msgbase.get_all_msg_headers(true);
if (msgHdrs.hasOwnProperty(pMsgNum))
{
var msgIdx = this.hdrsForCurrentSubBoardByMsgNum[pMsgNum];
this.hdrsForCurrentSubBoard[msgIdx] = msgHdrs[pMsgNum];
}
}
}
msgbase.close();
}
// For the DigDistMessageReader class: Re-calculates the message list widths and
// format strings
//
// Parameters:
// pMsgNumLen: Optional - Length to use for the message number field. If not specified,
// then this will get the number of messages in the sub-board and use that
// length.
function DigDistMsgReader_RecalcMsgListWidthsAndFormatStrs(pMsgNumLen)
{
// Note: Constructing these strings must be done after reading the configuration
// file in order for the configured colors to be used
this.sMsgListHdrFormatStr = "";
this.sMsgInfoFormatStr = "";
this.sMsgInfoToUserFormatStr = "";
this.sMsgInfoFromUserFormatStr = "";