Commit b2966165 authored by bugreport%peshkin.net's avatar bugreport%peshkin.net

Bug 149504 Permit a reference to a URL to be treated as an attachment

Patch by Joel Peshkin <bugreport@peshkin.net> r=lpsolit, a=justdave
parent a38539a7
...@@ -91,6 +91,7 @@ sub _retrieve { ...@@ -91,6 +91,7 @@ sub _retrieve {
'%Y.%m.%d %H:%i') . " AS attached", '%Y.%m.%d %H:%i') . " AS attached",
'attachments.filename AS filename', 'attachments.filename AS filename',
'attachments.ispatch AS ispatch', 'attachments.ispatch AS ispatch',
'attachments.isurl AS isurl',
'attachments.isobsolete AS isobsolete', 'attachments.isobsolete AS isobsolete',
'attachments.isprivate AS isprivate' 'attachments.isprivate AS isprivate'
); );
......
...@@ -41,6 +41,11 @@ sub get_param_list { ...@@ -41,6 +41,11 @@ sub get_param_list {
my $class = shift; my $class = shift;
my @param_list = ( my @param_list = (
{ {
name => 'allow_attach_url',
type => 'b',
default => 0
},
{
name => 'maxpatchsize', name => 'maxpatchsize',
type => 't', type => 't',
default => '1000', default => '1000',
......
...@@ -313,6 +313,8 @@ use constant ABSTRACT_SCHEMA => { ...@@ -313,6 +313,8 @@ use constant ABSTRACT_SCHEMA => {
DEFAULT => 'FALSE'}, DEFAULT => 'FALSE'},
isprivate => {TYPE => 'BOOLEAN', NOTNULL => 1, isprivate => {TYPE => 'BOOLEAN', NOTNULL => 1,
DEFAULT => 'FALSE'}, DEFAULT => 'FALSE'},
isurl => {TYPE => 'BOOLEAN', NOTNULL => 1,
DEFAULT => 'FALSE'},
], ],
INDEXES => [ INDEXES => [
attachments_bug_id_idx => ['bug_id'], attachments_bug_id_idx => ['bug_id'],
......
...@@ -911,13 +911,31 @@ sub insert ...@@ -911,13 +911,31 @@ sub insert
ValidateBugID($bugid); ValidateBugID($bugid);
validateCanChangeBug($bugid); validateCanChangeBug($bugid);
ValidateComment(scalar $cgi->param('comment')); ValidateComment(scalar $cgi->param('comment'));
my $filename = validateFilename(); my $attachurl = $cgi->param('attachurl') || '';
my $data;
my $filename;
my $contenttype;
my $isurl;
validateIsPatch(); validateIsPatch();
validateDescription(); validateDescription();
if (($attachurl =~ /^(http|https|ftp):\/\/\S+/)
&& !(defined $cgi->upload('data'))) {
$filename = '';
$data = $attachurl;
$isurl = 1;
$contenttype = SqlQuote('text/plain');
$cgi->param('ispatch', 0);
$cgi->delete('bigfile');
} else {
$filename = validateFilename();
# need to validate content type before data as # need to validate content type before data as
# we now check the content type for image/bmp in validateData() # we now check the content type for image/bmp in validateData()
validateContentType() unless $cgi->param('ispatch'); validateContentType() unless $cgi->param('ispatch');
my $data = validateData(); $data = validateData();
$contenttype = SqlQuote($cgi->param('contenttype'));
$isurl = 0;
}
my @obsolete_ids = (); my @obsolete_ids = ();
@obsolete_ids = validateObsolete() if $cgi->param('obsolete'); @obsolete_ids = validateObsolete() if $cgi->param('obsolete');
...@@ -946,7 +964,6 @@ sub insert ...@@ -946,7 +964,6 @@ sub insert
# Escape characters in strings that will be used in SQL statements. # Escape characters in strings that will be used in SQL statements.
my $sql_filename = SqlQuote($filename); my $sql_filename = SqlQuote($filename);
my $description = SqlQuote($cgi->param('description')); my $description = SqlQuote($cgi->param('description'));
my $contenttype = SqlQuote($cgi->param('contenttype'));
my $isprivate = $cgi->param('isprivate') ? 1 : 0; my $isprivate = $cgi->param('isprivate') ? 1 : 0;
# Figure out when the changes were made. # Figure out when the changes were made.
...@@ -956,10 +973,10 @@ sub insert ...@@ -956,10 +973,10 @@ sub insert
# Insert the attachment into the database. # Insert the attachment into the database.
my $sth = $dbh->prepare("INSERT INTO attachments my $sth = $dbh->prepare("INSERT INTO attachments
(bug_id, creation_ts, filename, description, (bug_id, creation_ts, filename, description,
mimetype, ispatch, isprivate, submitter_id) mimetype, ispatch, isurl, isprivate, submitter_id)
VALUES ($bugid, $sql_timestamp, $sql_filename, VALUES ($bugid, $sql_timestamp, $sql_filename,
$description, $contenttype, " . $cgi->param('ispatch') . ", $description, $contenttype, " . $cgi->param('ispatch') . ",
$isprivate, $userid)"); $isurl, $isprivate, $userid)");
$sth->execute(); $sth->execute();
# Retrieve the ID of the newly created attachment record. # Retrieve the ID of the newly created attachment record.
my $attachid = $dbh->bz_last_key('attachments', 'attach_id'); my $attachid = $dbh->bz_last_key('attachments', 'attach_id');
...@@ -1096,14 +1113,20 @@ sub edit ...@@ -1096,14 +1113,20 @@ sub edit
my ($attach_id) = validateID(); my ($attach_id) = validateID();
# Retrieve the attachment from the database. # Retrieve the attachment from the database.
SendSQL("SELECT description, mimetype, filename, bug_id, ispatch, isobsolete, isprivate, LENGTH(thedata) SendSQL("SELECT description, mimetype, filename, bug_id, ispatch, isurl,
isobsolete, isprivate, LENGTH(thedata)
FROM attachments FROM attachments
INNER JOIN attach_data INNER JOIN attach_data
ON id = attach_id ON id = attach_id
WHERE attach_id = $attach_id"); WHERE attach_id = $attach_id");
my ($description, $contenttype, $filename, $bugid, $ispatch, $isobsolete, $isprivate, $datasize) = FetchSQLData(); my ($description, $contenttype, $filename, $bugid, $ispatch, $isurl, $isobsolete, $isprivate, $datasize) = FetchSQLData();
my $isviewable = isViewable($contenttype); my $isviewable = !$isurl && isViewable($contenttype);
my $thedata;
if ($isurl) {
SendSQL("SELECT thedata FROM attach_data WHERE id = $attach_id");
($thedata) = FetchSQLData();
}
# Retrieve a list of attachments for this bug as well as a summary of the bug # Retrieve a list of attachments for this bug as well as a summary of the bug
# to use in a navigation bar across the top of the screen. # to use in a navigation bar across the top of the screen.
...@@ -1135,9 +1158,11 @@ sub edit ...@@ -1135,9 +1158,11 @@ sub edit
$vars->{'bugid'} = $bugid; $vars->{'bugid'} = $bugid;
$vars->{'bugsummary'} = $bugsummary; $vars->{'bugsummary'} = $bugsummary;
$vars->{'ispatch'} = $ispatch; $vars->{'ispatch'} = $ispatch;
$vars->{'isurl'} = $isurl;
$vars->{'isobsolete'} = $isobsolete; $vars->{'isobsolete'} = $isobsolete;
$vars->{'isprivate'} = $isprivate; $vars->{'isprivate'} = $isprivate;
$vars->{'datasize'} = $datasize; $vars->{'datasize'} = $datasize;
$vars->{'thedata'} = $thedata;
$vars->{'isviewable'} = $isviewable; $vars->{'isviewable'} = $isviewable;
$vars->{'attachments'} = \@bugattachments; $vars->{'attachments'} = \@bugattachments;
$vars->{'GetBugLink'} = \&GetBugLink; $vars->{'GetBugLink'} = \&GetBugLink;
......
...@@ -1742,6 +1742,7 @@ AddFDef("content", "Content", 0); ...@@ -1742,6 +1742,7 @@ AddFDef("content", "Content", 0);
$dbh->do("DELETE FROM fielddefs WHERE name='attachments.thedata'"); $dbh->do("DELETE FROM fielddefs WHERE name='attachments.thedata'");
AddFDef("attach_data.thedata", "Attachment data", 0); AddFDef("attach_data.thedata", "Attachment data", 0);
AddFDef("attachments.isurl", "Attachment is a URL", 0);
########################################################################### ###########################################################################
# Detect changed local settings # Detect changed local settings
...@@ -4033,6 +4034,9 @@ if ($dbh->bz_column_info("series", "public")) { ...@@ -4033,6 +4034,9 @@ if ($dbh->bz_column_info("series", "public")) {
$dbh->bz_rename_column('series', 'public', 'is_public'); $dbh->bz_rename_column('series', 'public', 'is_public');
} }
# 2005-09-28 bugreport@peshkin.net Bug 149504
$dbh->bz_add_column('attachments', 'isurl',
{TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 0});
# If you had to change the --TABLE-- definition in any way, then add your # If you had to change the --TABLE-- definition in any way, then add your
# differential change code *** A B O V E *** this comment. # differential change code *** A B O V E *** this comment.
......
...@@ -25,6 +25,9 @@ ...@@ -25,6 +25,9 @@
%] %]
[% param_descs = { [% param_descs = {
allow_attach_url => "If this option is on, it will be possible to " _
"specify a URL when creating an attachment and " _
"treat the URL itself as if it were an attachment.",
maxpatchsize => "The maximum size (in kilobytes) of patches. $terms.Bugzilla will not " _ maxpatchsize => "The maximum size (in kilobytes) of patches. $terms.Bugzilla will not " _
"accept patches greater than this number of kilobytes in size. " _ "accept patches greater than this number of kilobytes in size. " _
"To accept patches of any size (subject to the limitations of " _ "To accept patches of any size (subject to the limitations of " _
......
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
# Rights Reserved. # Rights Reserved.
# #
# Contributor(s): Myk Melez <myk@mozilla.org> # Contributor(s): Myk Melez <myk@mozilla.org>
# Joel Peshkin <bugreport@peshkin.net>
# Erik Stambaugh <erik@dasbistro.com>
#%] #%]
[% PROCESS global/variables.none.tmpl %] [% PROCESS global/variables.none.tmpl %]
...@@ -53,6 +55,58 @@ ...@@ -53,6 +55,58 @@
onload="setContentTypeDisabledState();" onload="setContentTypeDisabledState();"
%] %]
[% IF Param("allow_attach_url") %]
<script type="text/javascript">
function URLFieldHandler() {
var field_attachurl = document.getElementById("attachurl");
var greyfields = new Array("data", "ispatch", "autodetect",
"list", "manual", "bigfile",
"contenttypeselection",
"contenttypeentry");
var i;
if (field_attachurl.value.match(/^\s*$/)) {
for (i = 0; i < greyfields.length; i++) {
thisfield = document.getElementById(greyfields[i]);
if (thisfield) {
thisfield.removeAttribute("disabled");
}
}
} else {
for (i = 0; i < greyfields.length; i++) {
thisfield = document.getElementById(greyfields[i]);
if (thisfield) {
thisfield.setAttribute("disabled", "disabled");
}
}
}
}
function DataFieldHandler() {
var field_data = document.getElementById("data");
var greyfields = new Array("attachurl");
if (field_data.value.match(/^\s*$/)) {
var i;
for (i = 0; i < greyfields.length; i++) {
thisfield = document.getElementById(greyfields[i]);
if (thisfield) {
thisfield.removeAttribute("disabled");
}
}
} else {
for (i = 0; i < greyfields.length; i++) {
thisfield = document.getElementById(greyfields[i]);
if (thisfield) {
thisfield.setAttribute("disabled", "disabled");
}
}
}
}
</script>
[% END %]
<form name="entryform" method="post" action="attachment.cgi" enctype="multipart/form-data"> <form name="entryform" method="post" action="attachment.cgi" enctype="multipart/form-data">
<input type="hidden" name="bugid" value="[% bugid %]"> <input type="hidden" name="bugid" value="[% bugid %]">
<input type="hidden" name="action" value="insert"> <input type="hidden" name="action" value="insert">
...@@ -62,7 +116,11 @@ ...@@ -62,7 +116,11 @@
<th><label for="data">File:</label></th> <th><label for="data">File:</label></th>
<td> <td>
<em>Enter the path to the file on your computer.</em><br> <em>Enter the path to the file on your computer.</em><br>
<input type="file" id="data" name="data" size="50"> <input type="file" id="data" name="data" size="50"
[% IF Param("allow_attach_url") %]
onchange="DataFieldHandler()"
[% END %]
>
</td> </td>
</tr> </tr>
[% IF Param("maxlocalattachment") %] [% IF Param("maxlocalattachment") %]
...@@ -77,6 +135,17 @@ ...@@ -77,6 +135,17 @@
</td> </td>
</tr> </tr>
[% END %] [% END %]
[% IF Param("allow_attach_url") %]
<tr>
<th><label for="attachurl">AttachURL:</label></th>
<td>
<em>URL to be attached instead.</em><br>
<input type="text" id="attachurl" name="attachurl" size="60"
maxlength="2000"
onkeyup="URLFieldHandler()" onblur="URLFieldHandler()">
</td>
</tr>
[% END %]
<tr> <tr>
<th><label for="description">Description:</label></th> <th><label for="description">Description:</label></th>
<td> <td>
...@@ -104,14 +173,15 @@ ...@@ -104,14 +173,15 @@
<input type="radio" id="list" <input type="radio" id="list"
name="contenttypemethod" value="list"> name="contenttypemethod" value="list">
<label for="list">select from list:</label> <label for="list">select from list:</label>
<select name="contenttypeselection" <select name="contenttypeselection" id="contenttypeselection"
onchange="this.form.contenttypemethod[1].checked = true;"> onchange="this.form.contenttypemethod[1].checked = true;">
[% PROCESS "attachment/content-types.html.tmpl" %] [% PROCESS "attachment/content-types.html.tmpl" %]
</select><br> </select><br>
<input type="radio" id="manual" <input type="radio" id="manual"
name="contenttypemethod" value="manual"> name="contenttypemethod" value="manual">
<label for="manual">enter manually:</label> <label for="manual">enter manually:</label>
<input type="text" name="contenttypeentry" size="30" maxlength="200" <input type="text" name="contenttypeentry" id="contenttypeentry"
size="30" maxlength="200"
onchange="if (this.value) this.form.contenttypemethod[2].checked = true;"> onchange="if (this.value) this.form.contenttypemethod[2].checked = true;">
</td> </td>
</tr> </tr>
......
...@@ -209,6 +209,10 @@ ...@@ -209,6 +209,10 @@
<b>Description:</b><br> <b>Description:</b><br>
<textarea rows="3" cols="25" name="description" wrap="soft">[% description FILTER html %]</textarea><br> <textarea rows="3" cols="25" name="description" wrap="soft">[% description FILTER html %]</textarea><br>
[% IF isurl %]
<input type="hidden" name="filename" value="[% filename FILTER html %]"><br>
<input type="hidden" name="contenttypeentry" value="[% contenttype FILTER html %]"><br>
[% ELSE %]
<b>Filename:</b><br> <b>Filename:</b><br>
<input type="text" size="20" name="filename" value="[% filename FILTER html %]"><br> <input type="text" size="20" name="filename" value="[% filename FILTER html %]"><br>
<b>Size: </b>[% datasize FILTER unitconvert %]<br> <b>Size: </b>[% datasize FILTER unitconvert %]<br>
...@@ -219,6 +223,7 @@ ...@@ -219,6 +223,7 @@
<input type="checkbox" id="ispatch" name="ispatch" value="1" <input type="checkbox" id="ispatch" name="ispatch" value="1"
[% 'checked="checked"' IF ispatch %]> [% 'checked="checked"' IF ispatch %]>
<label for="ispatch">patch</label> <label for="ispatch">patch</label>
[% END %]
<input type="checkbox" id="isobsolete" name="isobsolete" value="1" <input type="checkbox" id="isobsolete" name="isobsolete" value="1"
[% 'checked="checked"' IF isobsolete %]> [% 'checked="checked"' IF isobsolete %]>
<label for="isobsolete">obsolete</label><br> <label for="isobsolete">obsolete</label><br>
...@@ -237,7 +242,8 @@ ...@@ -237,7 +242,8 @@
</div> </div>
<input type="submit" value="Submit"><br><br> <input type="submit" value="Submit"><br><br>
<strong>Actions:</strong> <a href="attachment.cgi?id=[% attachid %]">View</a> <strong>Actions:</strong>
<a href="attachment.cgi?id=[% attachid %]">View</a>
[% IF ispatch && patchviewerinstalled %] [% IF ispatch && patchviewerinstalled %]
| <a href="attachment.cgi?id=[% attachid %]&action=diff">Diff</a> | <a href="attachment.cgi?id=[% attachid %]&action=diff">Diff</a>
[% END %] [% END %]
...@@ -268,6 +274,18 @@ ...@@ -268,6 +274,18 @@
//--> //-->
</script> </script>
</td> </td>
[% ELSIF isurl %]
<td width="75%">
<a href="[% thedata FILTER html %]">
[% IF datasize < 120 %]
[% thedata FILTER html %]
[% ELSE %]
[% thedata FILTER truncate(80) FILTER html %]
&nbsp;...
[% thedata.match(".*(.{20})$").0 FILTER html %]
[% END %]
</a>
</td>
[% ELSE %] [% ELSE %]
<td id="noview" width="50%"> <td id="noview" width="50%">
<p><b> <p><b>
......
...@@ -43,6 +43,8 @@ ...@@ -43,6 +43,8 @@
<td valign="top"> <td valign="top">
[% IF attachment.ispatch %] [% IF attachment.ispatch %]
<i>patch</i> <i>patch</i>
[% ELSIF attachment.isurl %]
<i>url</i>
[% ELSE %] [% ELSE %]
[% attachment.contenttype FILTER html %] [% attachment.contenttype FILTER html %]
[% END %] [% END %]
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment