summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CGI.pl9
-rw-r--r--bug_form.pl28
-rwxr-xr-xbuglist.cgi16
-rwxr-xr-xchecksetup.pl38
-rw-r--r--defparams.pl5
-rwxr-xr-xprocessmail5
-rwxr-xr-xtemplate/default/attachment/edit.atml192
-rwxr-xr-xtemplate/default/attachment/list.atml58
-rwxr-xr-xtemplate/default/attachment/updated.atml16
-rwxr-xr-xtemplate/default/attachment/viewall.atml78
-rwxr-xr-xtemplate/default/attachstatus/create.atml56
-rwxr-xr-xtemplate/default/attachstatus/edit.atml52
-rwxr-xr-xtemplate/default/attachstatus/list.atml52
-rwxr-xr-xtemplate/default/global/footer6
-rwxr-xr-xtemplate/default/global/header49
15 files changed, 646 insertions, 14 deletions
diff --git a/CGI.pl b/CGI.pl
index 2cf6ffeca..6bcb1ba02 100644
--- a/CGI.pl
+++ b/CGI.pl
@@ -1194,7 +1194,8 @@ sub DumpBugActivity {
$datepart = "and bugs_activity.bug_when >= $starttime";
}
my $query = "
- SELECT IFNULL(fielddefs.name, bugs_activity.fieldid),
+ SELECT IFNULL(fielddefs.description, bugs_activity.fieldid),
+ bugs_activity.attach_id,
bugs_activity.bug_when,
bugs_activity.removed, bugs_activity.added,
profiles.login_name
@@ -1219,7 +1220,9 @@ sub DumpBugActivity {
my @row;
my $incomplete_data = 0;
while (@row = FetchSQLData()) {
- my ($field,$when,$removed,$added,$who) = (@row);
+ my ($field,$attachid,$when,$removed,$added,$who) = (@row);
+ $field =~ s/^Attachment/<a href="attachment.cgi?id=$attachid&action=view">Attachment #$attachid<\/a>/
+ if (Param('useattachmenttracker') && $attachid);
$removed = html_quote($removed);
$added = html_quote($added);
$removed = "&nbsp;" if $removed eq "";
@@ -1289,6 +1292,8 @@ Actions:
}
if (UserInGroup("editcomponents")) {
$html .= ", <a href=editproducts.cgi>components</a>";
+ $html .= ", <a href=editattachstatuses.cgi><NOBR>attachment statuses</NOBR></a>"
+ if Param('useattachmenttracker');
}
if (UserInGroup("creategroups")) {
$html .= ", <a href=editgroups.cgi>groups</a>";
diff --git a/bug_form.pl b/bug_form.pl
index c554cb0b0..d2f6cc3f4 100644
--- a/bug_form.pl
+++ b/bug_form.pl
@@ -283,18 +283,26 @@ if (@::legal_keywords) {
};
}
-print "<tr><td align=right><B>Attachments:</b></td>\n";
-SendSQL("select attach_id, creation_ts, mimetype, description from attachments where bug_id = $id");
-while (MoreSQLData()) {
- my ($attachid, $date, $mimetype, $desc) = (FetchSQLData());
- if ($date =~ /^(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/) {
- $date = "$3/$4/$2 $5:$6";
+# 2001-05-16 myk@mozilla.org: use the attachment tracker to display attachments
+# if this installation has enabled use of the attachment tracker.
+if (Param('useattachmenttracker')) {
+ print "</table>\n";
+ use Attachment;
+ &Attachment::list($id);
+} else {
+ print "<tr><td align=right><B>Attachments:</b></td>\n";
+ SendSQL("select attach_id, creation_ts, mimetype, description from attachments where bug_id = $id");
+ while (MoreSQLData()) {
+ my ($attachid, $date, $mimetype, $desc) = (FetchSQLData());
+ if ($date =~ /^(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/) {
+ $date = "$3/$4/$2 $5:$6";
+ }
+ my $link = "showattachment.cgi?attach_id=$attachid";
+ $desc = value_quote($desc);
+ print qq{<td><a href="$link">$date</a></td><td colspan=6>$desc&nbsp;&nbsp;&nbsp;($mimetype)</td></tr><tr><td></td>};
}
- my $link = "showattachment.cgi?attach_id=$attachid";
- $desc = value_quote($desc);
- print qq{<td><a href="$link">$date</a></td><td colspan=6>$desc&nbsp;&nbsp;&nbsp;($mimetype)</td></tr><tr><td></td>};
+ print "<td colspan=7><a href=\"createattachment.cgi?id=$id\">Create a new attachment</a> (proposed patch, testcase, etc.)</td></tr></table>\n";
}
-print "<td colspan=7><a href=\"createattachment.cgi?id=$id\">Create a new attachment</a> (proposed patch, testcase, etc.)</td></tr></table>\n";
sub EmitDependList {
diff --git a/buglist.cgi b/buglist.cgi
index 2b2a394bf..830b66599 100755
--- a/buglist.cgi
+++ b/buglist.cgi
@@ -415,6 +415,22 @@ sub GenerateSQL {
}
$f = "$table.$field";
},
+ # 2001-05-16 myk@mozilla.org: enable querying against attachment status
+ # if this installation has enabled use of the attachment manager.
+ "^attachstatusdefs.name," => sub {
+ my $attachtable = "attachments_$chartid";
+ my $statustable = "attachstatuses_$chartid";
+ my $statusdefstable = "attachstatusdefs_$chartid";
+ push(@supptables, "attachments $attachtable");
+ push(@supptables, "attachstatuses $statustable");
+ push(@supptables, "attachstatusdefs $statusdefstable");
+ push(@wherepart, "bugs.bug_id = $attachtable.bug_id");
+ push(@wherepart, "$attachtable.attach_id = $statustable.attach_id");
+ push(@wherepart, "$statustable.statusid = $statusdefstable.id");
+ my $table = $statusdefstable;
+ my $field = "name";
+ $f = "$table.$field";
+ },
"^changedin," => sub {
$f = "(to_days(now()) - to_days(bugs.delta_ts))";
},
diff --git a/checksetup.pl b/checksetup.pl
index 4c3ab81d3..7c165b9eb 100755
--- a/checksetup.pl
+++ b/checksetup.pl
@@ -192,6 +192,7 @@ unless (have_vers("DBI","1.13")) { push @missing,"DBI" }
unless (have_vers("Data::Dumper",0)) { push @missing,"Data::Dumper" }
unless (have_vers("DBD::mysql","1.2209")) { push @missing,"DBD::mysql" }
unless (have_vers("Date::Parse",0)) { push @missing,"Date::Parse" }
+unless (have_vers("Template","2.01")) { push @missing,"Template" }
# If CGI::Carp was loaded successfully for version checking, it changes the
# die and warn handlers, we don't want them changed, so we need to stash the
@@ -838,6 +839,7 @@ my %table;
$table{bugs_activity} =
'bug_id mediumint not null,
+ attach_id mediumint null,
who mediumint not null,
bug_when datetime not null,
fieldid mediumint not null,
@@ -859,10 +861,33 @@ $table{attachments} =
filename mediumtext not null,
thedata longblob not null,
submitter_id mediumint not null,
+ isobsolete tinyint not null default 0,
index(bug_id),
index(creation_ts)';
+# 2001-05-05 myk@mozilla.org: Tables to support the attachment tracker.
+# "attachstatuses" stores one record for each status on each attachment.
+# "attachstatusdefs" defines the statuses that can be set on attachments.
+# Note: These tables are only used if the parameter "useattachmenttracker"
+# is turned on via editparameters.cgi.
+
+$table{attachstatuses} =
+ '
+ attach_id MEDIUMINT NOT NULL ,
+ statusid SMALLINT NOT NULL ,
+ PRIMARY KEY(attach_id, statusid)
+ ';
+
+$table{attachstatusdefs} =
+ '
+ id SMALLINT NOT NULL PRIMARY KEY ,
+ name VARCHAR(50) NOT NULL ,
+ description MEDIUMTEXT NULL ,
+ sortkey SMALLINT NOT NULL DEFAULT 0 ,
+ product VARCHAR(64) NOT NULL
+ ';
+
#
# Apostrophe's are not supportied in the enum types.
# See http://bugzilla.mozilla.org/show_bug.cgi?id=27309
@@ -1294,6 +1319,8 @@ AddFDef("attachments.description", "Attachment description", 0);
AddFDef("attachments.thedata", "Attachment data", 0);
AddFDef("attachments.mimetype", "Attachment mime type", 0);
AddFDef("attachments.ispatch", "Attachment is patch", 0);
+AddFDef("attachments.isobsolete", "Attachment is obsolete", 0);
+AddFDef("attachstatusdefs.name", "Attachment Status", 0);
AddFDef("target_milestone", "Target Milestone", 0);
AddFDef("delta_ts", "Last changed date", 0);
AddFDef("(to_days(now()) - to_days(bugs.delta_ts))", "Days since bug changed",
@@ -2322,6 +2349,12 @@ unless (-d 'data/duplicates') {
#
AddField('groups', 'isactive', 'tinyint not null default 1');
+#
+# 2001-06-15 myk@mozilla.org:
+# isobsolete determines whether or not an attachment is pertinent/relevant/valid.
+#
+AddField('attachments', 'isobsolete', 'tinyint not null default 0');
+
# 2001-04-29 jake@acutex.net - Remove oldemailtech
# http://bugzilla.mozilla.org/show_bugs.cgi?id=71552
if (-d 'shadow') {
@@ -2470,6 +2503,11 @@ AddField("bugs", "assignee_accessible", "tinyint not null default 1");
AddField("bugs", "qacontact_accessible", "tinyint not null default 1");
AddField("bugs", "cclist_accessible", "tinyint not null default 1");
+# 2001-08-21 myk@mozilla.org bug84338:
+# Add a field for the attachment ID to the bugs_activity table, so installations
+# using the attachment manager can record changes to attachments.
+AddField("bugs_activity", "attach_id", "mediumint null");
+
# If you had to change the --TABLE-- definition in any way, then add your
# differential change code *** A B O V E *** this comment.
#
diff --git a/defparams.pl b/defparams.pl
index 46a00b78f..31bd57d4f 100644
--- a/defparams.pl
+++ b/defparams.pl
@@ -669,4 +669,9 @@ DefParam("moved-default-component",
"t",
'');
+DefParam("useattachmenttracker",
+ "Whether or not to use the attachment tracker that adds additional features for tracking bug attachments.",
+ "b",
+ 0);
+
1;
diff --git a/processmail b/processmail
index b82c80aca..45f5aa310 100755
--- a/processmail
+++ b/processmail
@@ -135,7 +135,7 @@ sub ProcessOneBug {
SendSQL("SELECT profiles.login_name, fielddefs.description, " .
- " bug_when, removed, added " .
+ " bug_when, removed, added, attach_id " .
"FROM bugs_activity, fielddefs, profiles " .
"WHERE bug_id = $id " .
" AND fielddefs.fieldid = bugs_activity.fieldid " .
@@ -153,13 +153,14 @@ sub ProcessOneBug {
my $difftext = "";
my $lastwho = "";
foreach my $ref (@diffs) {
- my ($who, $what, $when, $old, $new) = (@$ref);
+ my ($who, $what, $when, $old, $new, $attachid) = (@$ref);
if ($who ne $lastwho) {
$lastwho = $who;
$difftext .= "\n$who" . Param('emailsuffix') . " changed:\n\n";
$difftext .= FormatTriple("What ", "Removed", "Added");
$difftext .= ('-' x 76) . "\n";
}
+ $what =~ s/^Attachment/Attachment #$attachid/ if (Param('useattachmenttracker') && $attachid);
$difftext .= FormatTriple($what, $old, $new);
}
diff --git a/template/default/attachment/edit.atml b/template/default/attachment/edit.atml
new file mode 100755
index 000000000..742822369
--- /dev/null
+++ b/template/default/attachment/edit.atml
@@ -0,0 +1,192 @@
+[% INCLUDE global/header
+ title = "Edit Attachment #$attachid for Bug #$bugid"
+ h1 = "Edit Attachment #$attachid for <a href=\"show_bug.cgi?id=$bugid\">Bug #$bugid</a>"
+ h2 = bugsummary
+ style = "
+ th { text-align: right; vertical-align: top; }
+ td { text-align: left; vertical-align: top; }
+ td#info { text-align: right; vertical-align: top; }
+ td#actions { text-align: right; vertical-align: bottom; }
+ td#noview { text-align: left; vertical-align: center; }
+ "
+%]
+
+<form method="post" action="attachment.cgi" onsubmit="normalizeComments();">
+ <input type="hidden" name="id" value="[% attachid %]">
+ <input type="hidden" name="action" value="update">
+
+ <table width="100%">
+
+ <tr>
+ <td width="25%">
+ <small>
+ <b>Description:</b><br>
+ <textarea rows="3" cols="25" name="description">[% description %]</textarea><br>
+
+ <b>MIME Type:</b><br>
+ <input type="text" size="20" name="mimetype" value="[% mimetype %]"><br>
+
+ <b>Flags:</b><br>
+ <input type="checkbox" name="ispatch" value="1"[% " checked" IF ispatch %]>patch
+ <input type="checkbox" name="isobsolete" value="1"[% " checked" IF isobsolete %]>obsolete<br>
+
+ [% IF statusdefs.size %]
+ <b>Status:</b><br>
+ [% FOREACH def = statusdefs %]
+ <input type="checkbox" name="status" value="[% def.id %]"[% " checked" IF statuses.${def.id} %]>[% def.name %]<br>
+ [% END %]
+ [% END %]
+
+ <div id="smallCommentFrame">
+ <b>Comment (on the bug):</b><br>
+ <textarea name="comment" rows="5" cols="25"></textarea><br>
+ </div>
+
+ <input type="submit" value="Submit">
+
+ </small>
+ </td>
+
+ [% IF isviewable %]
+ <td width="75%">
+ <textarea id="editFrame" name="comment" style="height: 400px; width: 100%; display: none;"></textarea>
+ <iframe id="viewFrame" src="attachment.cgi?id=[% attachid %]&action=view" style="height: 400px; width: 100%;">
+ <b>You cannot view the attachment while editing it because your browser does not support IFRAMEs.
+ <a href="attachment.cgi?id=[% attachid %]&action=view">View the attachment on a separate page</a>.</b>
+ </iframe>
+ <button id="editButton" onclick="editAsComment();">Edit Attachment As Comment</button>
+ <button id="undoEditButton" onclick="undoEditAsComment();" style="display: none;">Undo Edit As Comment</button>
+ <button id="redoEditButton" onclick="redoEditAsComment();" style="display: none;">Redo Edit As Comment</button>
+ </td>
+ [% ELSE %]
+ <td id="noview" width="50%">
+ <p><b>
+ Attachment cannot be viewed because its MIME type is not either text/*, image/*, or application/vnd.mozilla.*.
+ <a href="attachment.cgi?id=[% attachid %]&action=view">Download the attachment instead</a>.
+ </b></p>
+ </td>
+ [% END %]
+
+ </tr>
+
+ </table>
+
+ Attachments on this Bug:
+ [% FOREACH a = attachments %]
+ [% IF a == attachid %]
+ #[% a %]
+ [% ELSE %]
+ <a href="attachment.cgi?id=[% a %]&action=edit">#[% a %]</a>
+ [% END %]
+ [% "|" UNLESS loop.last() %]
+ [% END %]
+
+</form>
+
+<script type="application/x-javascript" language="JavaScript">
+ function editAsComment()
+ {
+ // Get the content of the document as a string.
+ var viewFrame = document.getElementById('viewFrame');
+ var aSerializer = new XMLSerializer();
+ var contentDocument = viewFrame.contentDocument;
+ var theContent = aSerializer.serializeToString(contentDocument);
+
+ // If this is a plaintext document, remove cruft that Mozilla adds
+ // because it treats it as an HTML document with a big PRE section.
+ // http://bugzilla.mozilla.org/show_bug.cgi?id=86012
+ var mimeType = '[% mimetype %]';
+ if ( mimeType == 'text/plain' )
+ {
+ theContent = theContent.replace( /^<html><head\/><body><pre>/ , "" );
+ theContent = theContent.replace( /<\/pre><\/body><\/html>$/ , "" );
+ theContent = theContent.replace( /&lt;/gi , "<" );
+ theContent = theContent.replace( /&gt;/gi , ">" );
+ theContent = theContent.replace( /&amp;/gi , "&" );
+ }
+
+ // Add mail-style quote indicators (>) to the beginning of each line.
+ // ".*\n" matches lines that end with a newline, while ".+" matches
+ // the rare situation in which the last line of a file does not end
+ // with a newline.
+ theContent = theContent.replace( /(.*\n|.+)/g , ">$1" );
+
+ hideElementById('viewFrame');
+ hideElementById('editButton');
+ hideElementById('smallCommentFrame');
+
+ showElementById('undoEditButton');
+
+ // Show the TEXTAREA that will contain the editable attachment
+ // and copy the content of the attachment into it.
+ showElementById('editFrame');
+
+ var editFrame = document.getElementById('editFrame');
+ editFrame.value = theContent;
+ editFrame.value += "\n\n";
+ }
+ function undoEditAsComment()
+ {
+ // Hide the "edit attachment as comment" TEXTAREA and the "undo" button.
+ hideElementById('undoEditButton');
+ hideElementById('editFrame');
+
+ // Show the "view attachment" IFRAME, the "redo" button that allows the user
+ // to go back to editing the attachment as a comment, and the small comment field.
+ showElementById('viewFrame');
+ showElementById('redoEditButton');
+ showElementById('smallCommentFrame');
+
+ }
+ function redoEditAsComment()
+ {
+ // Hide the "view attachment" IFRAME, the "redo" button that allows the user
+ // to go back to editing the attachment as a comment, and the small comment field.
+ hideElementById('viewFrame');
+ hideElementById('redoEditButton');
+ hideElementById('smallCommentFrame');
+
+ // Show the "edit attachment as comment" TEXTAREA and the "undo" button.
+ showElementById('undoEditButton');
+ showElementById('editFrame');
+ }
+
+ function hideElementById(id)
+ {
+ var elm = document.getElementById(id);
+ if (!elm) return;
+
+ elm.style.setProperty('display', 'none', '');
+ }
+
+ function showElementById(id, val)
+ {
+ var elm = document.getElementById(id);
+ if (!elm) return;
+
+ if (!val) val = 'inline';
+ elm.style.setProperty('display', val, '');
+ }
+
+ function normalizeComments()
+ {
+ // Remove the unused comment field from the document so its contents
+ // do not get transmitted back to the server.
+
+ var small = document.getElementById('smallCommentFrame');
+ var big = document.getElementById('editFrame');
+ if ( small.style.getProperty('display') == 'none' )
+ {
+ small.parentNode.removeChild(small);
+ }
+ if ( big.style.getProperty('display') == 'none' )
+ {
+ big.parentNode.removeChild(big);
+ }
+
+ }
+</script>
+
+<br>
+
+[% INCLUDE global/footer %]
diff --git a/template/default/attachment/list.atml b/template/default/attachment/list.atml
new file mode 100755
index 000000000..3086c8dc0
--- /dev/null
+++ b/template/default/attachment/list.atml
@@ -0,0 +1,58 @@
+<br>
+<table cellspacing="0" cellpadding="4" border="1">
+ <tr>
+ <th bgcolor="#cccccc" align="left">Attachment</th>
+ <th bgcolor="#cccccc" align="left">Type</th>
+ <th bgcolor="#cccccc" align="left">Modified</th>
+ <th bgcolor="#cccccc" align="left">Status</th>
+ <th bgcolor="#cccccc" align="left">Actions</th>
+ </tr>
+
+ [% FOREACH attachment = attachments %]
+ <tr>
+ <td valign="top">
+ [% IF attachment.isobsolete %]
+ <strike><a href="attachment.cgi?id=[% attachment.attachid %]&action=view">[% attachment.description %]</a></strike>
+ [% ELSE %]
+ <a href="attachment.cgi?id=[% attachment.attachid %]&action=view">[% attachment.description %]</a>
+ [% END %]
+ </td>
+
+ <td valign="top">
+ [% IF attachment.ispatch %]
+ <i>patch</i>
+ [% ELSE %]
+ [% attachment.mimetype %]
+ [% END %]
+ </td>
+
+ <td valign="top">[% attachment.date %]</td>
+
+ <td valign="top">
+ <nobr>
+ [% IF attachment.statuses.size == 0 %]
+ <i>none</i>
+ [% ELSE %]
+ [% FOREACH s = attachment.statuses %]
+ [% s %]<br>
+ [% END %]
+ [% END %]
+ </nobr>
+ </td>
+
+ <td valign="top">
+ <a href="attachment.cgi?id=[% attachment.attachid %]&action=edit">Edit</a>
+ </td>
+ </tr>
+ [% END %]
+
+ <tr>
+ <td colspan="4">
+ <a href="createattachment.cgi?id=[% bugid %]">Create a new attachment</a> (proposed patch, testcase, etc.)
+ </td>
+ <td colspan="1">
+ <a href="attachment.cgi?bugid=[% bugid %]&action=viewall">View All</a>
+ </td>
+ </tr>
+</table>
+<br>
diff --git a/template/default/attachment/updated.atml b/template/default/attachment/updated.atml
new file mode 100755
index 000000000..7b52deebc
--- /dev/null
+++ b/template/default/attachment/updated.atml
@@ -0,0 +1,16 @@
+[% INCLUDE global/header
+ title = "Changes Submitted"
+ style = "th { text-align: left; }"
+%]
+
+<p>
+ Your changes have been submitted for
+ <a href="attachment.cgi?id=[% attachid %]&action=edit">attachment #[% attachid %]</a>
+ of <a href="show_bug.cgi?id=[% bugid %]">bug #[% bugid %]</a>.
+</p>
+
+[% mailresults %]
+
+<br>
+
+[% INCLUDE global/footer %]
diff --git a/template/default/attachment/viewall.atml b/template/default/attachment/viewall.atml
new file mode 100755
index 000000000..9a22b716a
--- /dev/null
+++ b/template/default/attachment/viewall.atml
@@ -0,0 +1,78 @@
+[% INCLUDE global/header
+ title = "View All Attachments for Bug #$bugid"
+ h1 = "View All Attachments for <a href=\"show_bug.cgi?id=$bugid\">Bug #$bugid</a>"
+ h2 = bugsummary
+ style = "
+ th { text-align: right; vertical-align: top; }
+ td { text-align: left; vertical-align: top; }
+ td#info { text-align: right; vertical-align: top; }
+ td#actions { text-align: right; vertical-align: bottom; }
+ "
+%]
+
+<br>
+
+[% FOREACH a = attachments %]
+
+ <div align="center">
+ <table cellspacing="0" cellpadding="4" border="1" width="75%">
+ <tr>
+ <td valign="top" bgcolor="#cccccc" colspan="5">
+ <big><b>Attachment #[% a.attachid %]</b></big>
+ </td>
+ </tr>
+ <tr>
+ <td valign="top">
+ [% IF a.isobsolete %]
+ <strike>[% a.description %]</strike>
+ [% ELSE %]
+ [% a.description %]
+ [% END %]
+ </td>
+
+ <td valign="top">
+ [% IF a.ispatch %]
+ <i>patch</i>
+ [% ELSE %]
+ [% a.mimetype %]
+ [% END %]
+ </td>
+
+ <td valign="top">[% a.date %]</td>
+
+ <td valign="top">
+ <nobr>
+ [% IF a.statuses.size == 0 %]
+ <i>none</i>
+ [% ELSE %]
+ [% FOREACH s = a.statuses %]
+ [% s %]<br>
+ [% END %]
+ [% END %]
+ </nobr>
+ </td>
+
+ <td valign="top">
+ <a href="attachment.cgi?id=[% a.attachid %]&action=edit">Edit</a>
+ </td>
+ </tr>
+ </table>
+
+ [% IF a.isviewable %]
+ <iframe src="attachment.cgi?id=[% a.attachid %]&action=view" width="75%" height="350">
+ <b>You cannot view the attachment on this page because your browser does not support IFRAMEs.
+ <a href="attachment.cgi?id=[% a.attachid %]&action=view">View the attachment on a separate page</a>.</b>
+ </iframe>
+ [% ELSE %]
+ <p><b>
+ Attachment cannot be viewed because its MIME type is not text/*, image/*, or application/vnd.mozilla.*.
+ <a href="attachment.cgi?id=[% a.attachid %]&action=view">Download the attachment instead</a>.
+ </b></p>
+ [% END %]
+ </div>
+
+ <br><br>
+
+[% END %]
+
+[% INCLUDE global/footer %]
diff --git a/template/default/attachstatus/create.atml b/template/default/attachstatus/create.atml
new file mode 100755
index 000000000..602dab92e
--- /dev/null
+++ b/template/default/attachstatus/create.atml
@@ -0,0 +1,56 @@
+[% INCLUDE global/header
+ title = "Create Attachment Status"
+ style = "
+ th { text-align: right; vertical-align: top; }
+ td { text-align: left; vertical-align: top; }
+ "
+%]
+
+<form method="post" action="editattachstatuses.cgi">
+ <input type="hidden" name="action" value="insert">
+ <input type="hidden" name="id" value="[% id %]">
+
+ <table cellspacing="0" cellpadding="4" border="0">
+ <tr>
+ <th>Name:</th>
+ <td>
+ <input type="text" name="name" size="50" maxlength="50">
+ </td>
+ </tr>
+
+ <tr>
+ <th>Description:</th>
+ <td>
+ <textarea name="desc" rows="4" cols="50"></textarea>
+ </td>
+ </tr>
+
+ <tr>
+ <th>Sort Key:</th>
+ <td>
+ <input type="text" name="sortkey" size="5" maxlength="5">
+ </td>
+ </tr>
+
+ <tr>
+ <th>Product:</th>
+ <td>
+ <select name="product">
+ [% FOREACH item = products %]
+ <option value="[% item %]">[% item %]</option>
+ [% END %]
+ </select>
+ </td>
+ </tr>
+
+ <tr>
+ <th></th>
+ <td>
+ <input type="submit" value="Add">
+ </td>
+ </tr>
+
+ </table>
+</form>
+
+[% INCLUDE global/footer %]
diff --git a/template/default/attachstatus/edit.atml b/template/default/attachstatus/edit.atml
new file mode 100755
index 000000000..646e0f0bf
--- /dev/null
+++ b/template/default/attachstatus/edit.atml
@@ -0,0 +1,52 @@
+[% INCLUDE global/header
+ title = "Edit Attachment Status"
+ style = "
+ th { text-align: right; vertical-align: top; }
+ td { text-align: left; vertical-align: top; }
+ "
+%]
+
+<form method="post" action="editattachstatuses.cgi">
+ <input type="hidden" name="action" value="update">
+ <input type="hidden" name="id" value="[% id %]">
+
+ <table cellspacing="0" cellpadding="4" border="0">
+ <tr>
+ <th>Name:</th>
+ <td>
+ <input type="text" name="name" value="[% name %]" size="50" maxlength="50">
+ </td>
+ </tr>
+
+ <tr>
+ <th>Description:</th>
+ <td>
+ <textarea name="desc" rows="4" cols="50">[% desc %]</textarea>
+ </td>
+ </tr>
+
+ <tr>
+ <th>Sort Key:</th>
+ <td>
+ <input type="text" name="sortkey" value="[% sortkey %]" size="5" maxlength="5">
+ </td>
+ </tr>
+
+ <tr>
+ <th>Product:</th>
+ <td>
+ [% product %]
+ </td>
+ </tr>
+
+ <tr>
+ <th></th>
+ <td>
+ <input type="submit" value="Update">
+ </td>
+ </tr>
+
+ </table>
+</form>
+
+[% INCLUDE global/footer %]
diff --git a/template/default/attachstatus/list.atml b/template/default/attachstatus/list.atml
new file mode 100755
index 000000000..acae5cd8d
--- /dev/null
+++ b/template/default/attachstatus/list.atml
@@ -0,0 +1,52 @@
+[% INCLUDE global/header
+ title = 'Administer Attachment Statuses'
+ message = message
+ style = "
+ th { text-align: left; }
+ "
+%]
+
+<table cellspacing="0" cellpadding="4" border="1">
+
+ <tr>
+ <th>Name</th>
+ <th>Description</th>
+ <th>Sort Key</th>
+ <th>Product</th>
+ <th>Action(s)</th>
+ </tr>
+
+ [% FOREACH statusdef = statusdefs %]
+
+ <tr>
+ <td>[% statusdef.name FILTER html %]</td>
+ <td>[% statusdef.description FILTER html %]</td>
+ <td>[% statusdef.sortkey %]</td>
+ <td>[% statusdef.product %]</td>
+ <td>
+ <a href="editattachstatuses.cgi?action=edit&id=[% statusdef.id %]">Edit</a>
+ <a href="editattachstatuses.cgi?action=delete&id=[% statusdef.id %]" onclick="return confirmDelete();">Delete</a>
+ </td>
+ </tr>
+
+ [% END %]
+
+ <tr>
+ <td colspan="4"></td>
+ <td>
+ <a href="editattachstatuses.cgi?action=create">Create</a>
+ </td>
+ </tr>
+
+</table>
+
+<script language="JavaScript">
+ function confirmDelete()
+ {
+ return confirm('Are you sure you want to permanently delete ' +
+ 'this attachment status? All attachments ' +
+ 'with this status will have it unset.');
+ }
+</script>
+
+[% INCLUDE global/footer %]
diff --git a/template/default/global/footer b/template/default/global/footer
new file mode 100755
index 000000000..e06d3d8e1
--- /dev/null
+++ b/template/default/global/footer
@@ -0,0 +1,6 @@
+
+[% PerformSubsts(Param('footerhtml')) %]
+
+</body>
+</html>
+
diff --git a/template/default/global/header b/template/default/global/header
new file mode 100755
index 000000000..676b7ddf4
--- /dev/null
+++ b/template/default/global/header
@@ -0,0 +1,49 @@
+[% DEFAULT
+ title = ""
+ h1 = title
+ h2 = ""
+ extra = ""
+ jscript = ""
+ style = ""
+ message = ""
+%]
+
+<html>
+ <head>
+ <title>[% title %]</title>
+ [% Param('headerhtml') %]
+ [% jscript %]
+ <style>
+ [% style %]
+ </style>
+ </head>
+ <body [% Param('bodyhtml') %] [% extra %]>
+
+ [% PerformSubsts(Param('bannerhtml')) %]
+
+[% IF h1 || h2 %]
+ <table border="0" cellspacing="0" width="100%">
+ <tr>
+ <td width="10%" valign="top" align="left">
+ <table border="0" cellpadding="0" cellspacing="2">
+ <tr>
+ <td valign="top" align="left" nowrap>
+ <font size="+1"><b>[% h1 %]</b></font>
+ </td>
+ </tr>
+ </table>
+ </td>
+ <td valign="center">&nbsp;</td>
+ <td valign="center" align="left">[% h2 %]</td>
+ </tr>
+ </table>
+[% END %]
+
+ [% IF message %]
+ <table width="100%" cellspacing="0" cellpadding="5" border="1"><tr><td>
+ <font color="green">[% message %]</font>
+ </td></tr></table>
+ [% END %]
+
+ [% Param('shutdownhtml') %]
+