// $Id: BugEntry.cc,v 1.9.2.1 1999/07/28 04:48:01 gibbsn Exp $
// $Revision: 1.9.2.1 $ $Author: gibbsn $ $Date: 1999/07/28 04:48:01 $
//
//ScryMUD Server Code
//Copyright (C) 1998 Ben Greear
//
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either version 2
//of the License, or (at your option) any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// To contact the Author, Ben Greear: greear@cyberhighway.net, (preferred)
// greearb@agcs.com
// Meaningless test comment added by angelbob: 7/26/99
//
#include "BugEntry.h"
#include "critter.h"
#include "misc2.h"
int BugEntry::_cnt = 0;
int BugCollection::_cnt = 0;
char* BugEntry::state_str[] = {
"open",
"assigned",
"retest",
"closed",
"defer",
"all",
"none",
"high_state",
};
char* BugEntry::html_color[] = {
"<font color=\"#000000\"> ", //open (black)
"<font color=\"#0000cc\"> ", //assigned (blue)
"<font color=\"#cc0000\"> ", //retest (red)
"<font color=\"#00cc00\"> ", //closed (green)
"<font color=\"#000000\"> ", //all (black) //these last 3 not used now.
"<font color=\"#000000\"> ", //none (black)
"<font color=\"#000000\"> " //high_state (black)
};
char* BugCollection::ct_strs[] = {
"BUGS",
"IDEAS"
};
///*************************************************************///
///********************* CommentEntry *****************************///
///*************************************************************///
CommentEntry::CommentEntry(const char* d, const char* rr, const char* r)
: date(d), reporter(rr), report(r) {
html_safe_report = report.sterilizeForHtml();
heg_safe_report = report;
parse_communication(heg_safe_report);
}//constructor
int CommentEntry::read(ifstream& dafile) {
char buf[100];
int sent;
dafile >> sent;
if (sent == -1)
return -1;
date.Termed_Read(dafile);
dafile >> reporter;
dafile.getline(buf, 99);
report.Termed_Read(dafile);
html_safe_report = report.sterilizeForHtml();
heg_safe_report = report;
parse_communication(heg_safe_report);
if (dafile)
return 0;
return -1;
}
int CommentEntry::write(ofstream& dafile) {
dafile << 1 << endl; //sentinal
dafile << date << "\n~\n";
dafile << reporter << endl;
dafile << report << "\n~\n";
if (dafile)
return 0;
return -1;
}//
int CommentEntry::writeSentinal(ofstream& dafile) {
dafile << -1 << endl;
if (dafile)
return 0;
return -1;
}//writeSentinal
int CommentEntry::writeHtml(ofstream& dafile, const char* fnt) {
dafile << "<tr><td nowrap>" << fnt << date << "</font></td> <td>"
<< fnt << reporter << "</font></td> <td>" << fnt
<< html_safe_report << "</font></td></tr>" << endl;
return 0;
}
String CommentEntry::toStringHeg() {
String buf(200);
Sprintf(buf, "<BUG_COMMENT_ENTRY \"%S\" %S>%S</BUG_COMMENT_ENTRY>",
&date, &reporter, &heg_safe_report);
return buf;
}
String CommentEntry::toString() {
String buf(200);
Sprintf(buf, " %S: %S\n\t%S\n", &date, &reporter, &report);
return buf;
}//toString
///*************************************************************///
///********************* BugEntry *****************************///
///*************************************************************///
BugEntry::state BugEntry::getState(const String& fer_state) {
if (fer_state.Strlen() == 0) {
return all;
}
for (int i = 0; i<high_state; i++) {
if (strcmp(fer_state, state_str[i]) == 0) {
return (state)(i);
}
}//for
return none;
}
BugEntry::BugEntry(const BugEntry& src) //copy constructor
: bug_num(src.bug_num), room_num(src.room_num), cur_state(src.cur_state),
create_date(src.create_date), reporter(src.reporter),
assigned_to(src.assigned_to), title(src.title),
html_safe_title(src.html_safe_title),
heg_safe_title(src.heg_safe_title) {
clear_ptr_list(reports);
Cell<CommentEntry*> cll(src.reports);
CommentEntry* ptr;
while ((ptr = cll.next())) {
reports.append(new CommentEntry(*ptr));
}
_cnt++;
}//copy constructor
BugEntry::BugEntry(int bn, const char* _reporter, const char* _date,
const char* _title, int rn)
: bug_num(bn), room_num(rn), cur_state(open),
create_date(_date), reporter(_reporter),
assigned_to("ADMIN"), title(_title) {
html_safe_title = title.sterilizeForHtml();
heg_safe_title = title;
parse_communication(heg_safe_title);
_cnt++;
}//Constructor
int BugEntry::clear() {
bug_num = room_num = 0;
cur_state = open;
flags.Clear();
create_date.Clear();
reporter.Clear();
assigned_to.Clear();
clear_ptr_list(reports);
title.Clear();
html_safe_title.Clear();
heg_safe_title.Clear();
return 0;
}//clear
BugEntry& BugEntry::operator=(const BugEntry& src) {
bug_num = src.bug_num;
room_num = src.bug_num;
cur_state = src.cur_state;
flags = src.flags;
create_date = src.create_date;
reporter = src.reporter;
assigned_to = src.assigned_to;
title = src.title;
html_safe_title = src.html_safe_title;
heg_safe_title = src.heg_safe_title;
clear_ptr_list(reports);
Cell<CommentEntry*> cll(src.reports);
CommentEntry* ptr;
while ((ptr = cll.next())) {
reports.append(new CommentEntry(*ptr));
}
return *this;
}//operatoe=
void BugEntry::appendComment(const CommentEntry& re) {
reports.append(new CommentEntry(re));
}
void BugEntry::appendComment(const char* date, const char* report,
const char* reporter) {
reports.append(new CommentEntry(date, reporter, report));
}
//does checking needed
int BugEntry::changeState(state new_state, int imm_level, const String& name) {
if ((imm_level < 9) && (strcasecmp(name, assigned_to) != 0)) {
return -EPERM;
}
// This is the only valid state transition for closed, so it's not in
// the switch statement below...
if (new_state == open) {
cur_state = open;
return 0;
}
// Don't restrict me cause I'm lazy!!
if (imm_level > 5) {
cur_state = new_state;
return 0;
}
switch (cur_state)
{
case open:
if ((new_state == assigned) || (new_state == defer)) {
cur_state = new_state;
return 0;
}
else {
return -EINVAL;
}
case assigned:
if (new_state == retest) {
cur_state = retest;
return 0;
}
else {
return -EINVAL;
}
case retest:
if (new_state == closed) {
cur_state = closed;
return 0;
}
else {
return -EINVAL;
}
default:
return -EINVAL;
}//switch
}//stateTransition
int BugEntry::read(ifstream& dafile) {
char buf[100];
clear();
dafile >> bug_num;
// Terminator reached.
if (bug_num == -1) {
return -1;
}
int cs;
dafile >> room_num >> cs;
cur_state = (state)(cs);
dafile.getline(buf, 99);
flags.Read(dafile);
dafile.getline(buf, 99);
create_date = buf;
create_date.Strip();
dafile >> reporter;
dafile.getline(buf, 99);
dafile >> assigned_to;
dafile.getline(buf, 99);
title.Termed_Read(dafile);
html_safe_title = title.sterilizeForHtml();
heg_safe_title = title;
parse_communication(heg_safe_title);
CommentEntry re;
while (re.read(dafile) >= 0) {
reports.append(new CommentEntry(re));
}
return bug_num;
}
int BugEntry::write(ofstream& dafile) {
dafile << bug_num << " " << room_num << " " << (int)cur_state
<< " bug_num, room_num, state\n";
flags.Write(dafile);
dafile << create_date << endl;
dafile << reporter << endl;
dafile << assigned_to << endl;
dafile << title << "\n~\n";
Cell<CommentEntry*> cll(reports);
CommentEntry* ptr;
while ((ptr = cll.next())) {
ptr->write(dafile);
}
CommentEntry::writeSentinal(dafile);
if (dafile)
return 0;
return -1;
};
// Write a table row entry...
int BugEntry::writeHtml(ofstream& dafile) {
String buf(200);
if (!reports.isEmpty()) {
dafile << "\n<table width=\"95%\" border=3>
<caption align=top><strong>";
}
else {
dafile << "<center><b>\n";
}
dafile << html_color[cur_state];
Sprintf(buf, "[%i] %P08[%s]%P17 <u>%S</u> %P35By %S %P60Room: %i<br>\n<font size += 1>%S</font>",
bug_num, state_str[cur_state], &create_date, &reporter,
room_num, &html_safe_title);
dafile << buf << "</font>\n";
if (!reports.isEmpty()) {
dafile << "</strong></caption><h4>\n";
Cell<CommentEntry*> cll(reports);
CommentEntry* ptr;
int did_one = FALSE;
while ((ptr = cll.next())) {
did_one = TRUE;
ptr->writeHtml(dafile, html_color[cur_state]);
}
dafile << "</h4></table>\n";
}//if
else {
dafile << "</b></center>\n";
}
if (dafile)
return 0;
return -1;
};
/** This is w/out hegemon markup */
String BugEntry::toString() {
String retval(1000);
if (reports.isEmpty()) {
Sprintf(retval,
"[%i] %P08[%s] %S\n\t Submitted by %S on %S: in room _%i_. Assigned to: %S\n",
bug_num, state_str[cur_state], &title, &reporter, &create_date,
room_num, &assigned_to);
}
else {
Sprintf(retval,
"* [%i] %P08[%s] %S\n\t Submitted by %S on %S: in room _%i_. Assigned to: %S\n",
bug_num, state_str[cur_state], &title, &reporter, &create_date,
room_num, &assigned_to);
}
Cell<CommentEntry*> cll(reports);
CommentEntry* ptr;
while ((ptr = cll.next())) {
retval += ptr->toString();
}
return retval;
}//toString
/** This is w/out hegemon markup */
String BugEntry::toStringBrief() {
String retval(1000);
if (reports.isEmpty()) {
Sprintf(retval, "[%i] %P08[%s] %S\n\t Submitted by %S on %S: in room %i. Assigned to: %S\n",
bug_num, state_str[cur_state], &heg_safe_title, &reporter,
&create_date, room_num, &assigned_to);
}
else {
Sprintf(retval, "** [%i] %P08[%s] %S\n\t Submitted by %S on %S: in room %i. Assigned to: %S\n",
bug_num, state_str[cur_state], &heg_safe_title, &reporter,
&create_date, room_num, &assigned_to);
}
return retval;
}//toStringBrief
/** This is with hegemon markup */
String BugEntry::toStringHeg(const char* col_type) {
String retval(1000);
String buf(100);
Sprintf(retval, "<BUG_TITLE>%S</BUG_TITLE>", &heg_safe_title);
Sprintf(buf, "<BUG_ENTRY %i %s \"%S\" %S %i %S %s>",
bug_num, state_str[cur_state], &create_date, &reporter, room_num,
&assigned_to, col_type);
retval += buf;
Cell<CommentEntry*> cll(reports);
CommentEntry* ptr;
while ((ptr = cll.next())) {
retval += ptr->toStringHeg();
}
retval += "</BUG_ENTRY>";
return retval;
}//toStringHeg
int BugEntry::canComment(int imm_level, const String& name) {
// Only way to comment on a closed one btw.
if (imm_level >= 9) {
return TRUE;
}
if (cur_state == assigned) {
return (strcasecmp(name, assigned_to) == 0);
}
// retest and open can be commented on by anyone.
return TRUE;
}//canComment
int BugEntry::reAssign(const String& new_name, int imm_level,
const String& name) {
if ((imm_level >= 9) || (strcasecmp(assigned_to, name) == 0)) {
if (new_name.Strlen() >= 3) {
assigned_to = new_name;
return 0;
}
else {
return -EINVAL;
}
}
else {
return -EPERM;
}
}//reAssign
///******************************************************************///
///********************* BugCollection *****************************///
///******************************************************************///
const char* BugCollection::getColTypeName() {
return ct_strs[type];
}
int BugCollection::write() {
ofstream dafile(file_name);
if (dafile) {
dafile << "_VERSION_ 1\n";
dafile << next_bug_num << endl;
Cell<BugEntry*> cll(bugs);
BugEntry* ptr;
while ((ptr = cll.next())) {
ptr->write(dafile);
dafile << endl;
}//while
dafile << -1 << " End of Bug Collection" << endl;
return 0;
}
else {
mudlog << "ERROR: could not open BugCollection for writing, file -:"
<< file_name << endl;
return -1;
}
}//write
int BugCollection::writeHtml() {
String tmp_name(file_name);
tmp_name += ".html";
ofstream dafile(tmp_name);
if (dafile) {
dafile << "
<HTML>
<HEAD>
<TITLE> " << coll_name << " </TITLE>
</HEAD>
<BODY BGCOLOR=\"#FFFFFF\">
<CENTER>
<H2>
<a href=http://scry.wanfear.com>ScryMUD "
<< coll_name << "</a></H2></CENTER><P>
This is an automagically generated list of <b>" << coll_name << "</b> entries
from ScryMUD.<P>
<center>
<a href=\"#open\">Open Issues</a>
<a href=\"#assigned\">Assigned Issues</a>
<a href=\"#retest\">Issues to Retest</a>
<a href=\"#closed\">Closed Issues</a>
<a href=\"#defer\">Deferred Issues</a>
</center><P>
";
Cell<BugEntry*> cll(bugs);
BugEntry* ptr;
dafile << "<center>
<a name=\"open\"></a>
<h2><u>Open Issues</u></h2><br>
<table width=\"100%\"border=3>\n";
while ((ptr = cll.next())) {
if (ptr->getState() == BugEntry::open) {
dafile << "<tr><td>\n";
ptr->writeHtml(dafile);
dafile << "</td></tr><P>" << endl;
}
}//while
dafile << "</table><P>
<a name=\"assigned\"></a>
<h2><u>Assigned Issues</u></h2>
<table width=\"100%\"border=3>\n";
bugs.head(cll);
while ((ptr = cll.next())) {
if (ptr->getState() == BugEntry::assigned) {
dafile << "<tr><td>\n";
ptr->writeHtml(dafile);
dafile << "</td></tr><P>" << endl;
}
}//while
dafile << "</table><P>
<a name=\"retest\"></a>
<h2><u>Issues In Retest</u></h2>
<table width=\"100%\"border=3>\n";
bugs.head(cll);
while ((ptr = cll.next())) {
if (ptr->getState() == BugEntry::retest) {
dafile << "<tr><td>\n";
ptr->writeHtml(dafile);
dafile << "</td></tr><P>" << endl;
}
}//while
dafile << "</table><P>
<a name=\"defer\"></a>
<h2><u>Issues that have been Deferred</u></h2>
<table width=\"100%\"border=3>\n";
bugs.head(cll);
while ((ptr = cll.next())) {
if (ptr->getState() == BugEntry::defer) {
dafile << "<tr><td>\n";
ptr->writeHtml(dafile);
dafile << "</td></tr><P>" << endl;
}
}//while
dafile << "</table><P>
<a name=\"closed\"></a>
<h2><u>Closed Issues</u></h2>
<table width=\"100%\"border=3>\n";
bugs.head(cll);
while ((ptr = cll.next())) {
if (ptr->getState() == BugEntry::closed) {
dafile << "<tr><td>\n";
ptr->writeHtml(dafile);
dafile << "</td></tr><P>" << endl;
}
}//while
dafile << "</table></center>\n\n<hr>
<ADDRESS>
Generated by:<A HREF=\"mailto:greear@cyberhighway.net\">ScryMUD Admin</A>
</ADDRESS></body></html>\n";
dafile << flush;
dafile.close();
String cmd(50);
Sprintf(cmd, "./mv_logs.bash %S", &tmp_name);
system(cmd);
return 0;
}
else {
mudlog << "ERROR: could not open BugCollection html file -:"
<< tmp_name << endl;
return -1;
}
}//writeHtml
int BugCollection::read() {
char buf[100];
ifstream dafile(file_name);
int cnt = 0;
int version_num;
String version;
if (dafile) {
dafile >> version >> version_num;
if (strcmp(version, "_VERSION_") != 0) {
mudlog << "ERROR: BugCollection file -:" << file_name
<< ":- is an incompatible version.\n It will be ignored "
<< "and destroyed the first time a change is made to the "
<< "bug list. I'll be able to handle Version Conversion\n"
<< "better next time...\n";
cerr << "ERROR: BugCollection file -:" << file_name
<< ":- is an incompatible version.\n It will be ignored "
<< "and destroyed the first time a change is made to the "
<< "bug list. I'll be able to handle Version Conversion\n"
<< "better next time...\n";
}
dafile >> next_bug_num;
dafile.getline(buf, 99);
BugEntry be;
while (dafile && (be.read(dafile) != -1)) {
cnt++;
bugs.append(new BugEntry(be));
}
return cnt;
}//if
else {
return -1;
}
}//read
BugEntry* BugCollection::getBugEntry(int bug_num) {
Cell<BugEntry*> cll(bugs);
BugEntry* ptr;
while ((ptr = cll.next())) {
if (bug_num == ptr->getBugNum()) {
return ptr;
}
}//while
return NULL;
}
int BugCollection::changeState(int bug, const String& new_state, int imm_level,
const String& name) {
BugEntry* be = getBugEntry(bug);
BugEntry::state s = BugEntry::getState(new_state);
if (be) {
int retval = be->changeState(s, imm_level, name);
if (retval >= 0)
notifyModified();
return retval;
}
return -EINVAL;
}
int BugCollection::reAssign(int bug, const String& new_name, int imm_level,
const String& name) {
BugEntry* be = getBugEntry(bug);
if (be) {
int retval = be->reAssign(new_name, imm_level, name);
if (retval >= 0)
notifyModified();
return retval;
}
return -EINVAL;
}
int BugCollection::canComment(int bug, int imm_level, const String& name) {
BugEntry* be = getBugEntry(bug);
if (be) {
return be->canComment(imm_level, name);
}
return FALSE;
}
int BugCollection::notifyModified() {
this->write();
this->writeHtml();
return 0;
}
int BugCollection::addComment(int bug, const CommentEntry& re, int imm_level,
const String& name) {
BugEntry* be = getBugEntry(bug);
if (be) {
if (be->canComment(imm_level, name)) {
be->appendComment(re);
notifyModified();
return 0;
}
else {
return -EINVAL;
}
}//if
else {
return -EINVAL;
}
}//addComment
int BugCollection::purgeBug(int num) {
BugEntry* be = getBugEntry(num);
if (be) {
bugs.loseData(be);
delete be;
notifyModified();
return 0;
}//if we found it
return -EINVAL;
}
int BugCollection::addBug(BugEntry* be) {
if (be) {
bugs.append(be);
notifyModified();
return 0;
}
return -EINVAL;
}
int BugCollection::addBug(const char* cur_date, int rnum,
const String& reporter, const String& report) {
bugs.append(new BugEntry(next_bug_num, reporter, cur_date, report, rnum));
next_bug_num++;
notifyModified();
return 0;
}
String BugCollection::toString(const String& in_state, int use_heg_markup) {
String buf(10000);
BugEntry::state s = BugEntry::getState(in_state);
Cell<BugEntry*> cll(bugs);
BugEntry* ptr;
while ((ptr = cll.next())) {
if ((s == BugEntry::all) || (s == ptr->getState())) {
if (use_heg_markup) {
buf.Append(ptr->toStringHeg(ct_strs[type]));
}
else {
buf.Append(ptr->toString());
}
buf.Append("\n");
}
}
return buf;
}
String BugCollection::toStringBrief(const String& in_state) {
String buf(10000);
BugEntry::state s = BugEntry::getState(in_state);
Cell<BugEntry*> cll(bugs);
BugEntry* ptr;
while ((ptr = cll.next())) {
if ((s == BugEntry::all) || (s == ptr->getState())) {
buf.Append(ptr->toStringBrief());
}
}
return buf;
}