]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Bug 149504 Permit a reference to a URL to be treated as an attachment
authorbugreport%peshkin.net <>
Thu, 20 Oct 2005 03:16:12 +0000 (03:16 +0000)
committerbugreport%peshkin.net <>
Thu, 20 Oct 2005 03:16:12 +0000 (03:16 +0000)
Patch by Joel Peshkin <bugreport@peshkin.net>
r=lpsolit, a=justdave

Bugzilla/Attachment.pm
Bugzilla/Config/Attachment.pm
Bugzilla/DB/Schema.pm
attachment.cgi
checksetup.pl
template/en/default/admin/params/attachment.html.tmpl
template/en/default/attachment/create.html.tmpl
template/en/default/attachment/edit.html.tmpl
template/en/default/attachment/list.html.tmpl

index 4a61154b78d069eb89f0572cb40a29b0284483f0..c4e7580ae58e36f11a7eeff06ee1805daa8cd207 100644 (file)
@@ -91,6 +91,7 @@ sub _retrieve {
                                        '%Y.%m.%d %H:%i') . " AS attached",
         'attachments.filename AS filename',
         'attachments.ispatch AS ispatch',
+        'attachments.isurl AS isurl',
         'attachments.isobsolete AS isobsolete',
         'attachments.isprivate AS isprivate'
     );
index 8192d5f9fea472c2cb3d23c145477dcff8cffdc8..449908528a6bbd7d8629f76ab3e54629a35d9964 100644 (file)
@@ -40,6 +40,11 @@ $Bugzilla::Config::Attachment::sortkey = "025";
 sub get_param_list {
   my $class = shift;
   my @param_list = (
+  {
+  name => 'allow_attach_url',
+  type => 'b',
+  default => 0
+  },
   {
    name => 'maxpatchsize',
    type => 't',
index 6abe41cdde32c2565f1645f2746622bbdc272cb5..8379f0b122649b97a1e3482005e339eab042a31c 100644 (file)
@@ -313,6 +313,8 @@ use constant ABSTRACT_SCHEMA => {
                              DEFAULT => 'FALSE'},
             isprivate    => {TYPE => 'BOOLEAN', NOTNULL => 1,
                              DEFAULT => 'FALSE'},
+            isurl        => {TYPE => 'BOOLEAN', NOTNULL => 1,
+                             DEFAULT => 'FALSE'},
         ],
         INDEXES => [
             attachments_bug_id_idx => ['bug_id'],
index 3aa1a68d6138f9b1edb082c7baec8cbfa97a119a..964e6cbbc742d30a44aa78a2b7a1b8b3d9cd5bf5 100755 (executable)
@@ -911,13 +911,31 @@ sub insert
     ValidateBugID($bugid);
     validateCanChangeBug($bugid);
     ValidateComment(scalar $cgi->param('comment'));
-    my $filename = validateFilename();
+    my $attachurl = $cgi->param('attachurl') || '';
+    my $data;
+    my $filename;
+    my $contenttype;
+    my $isurl;
     validateIsPatch();
     validateDescription();
-    # need to validate content type before data as
-    # we now check the content type for image/bmp in validateData()
-    validateContentType() unless $cgi->param('ispatch');
-    my $data = validateData();
+  
+    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
+        # we now check the content type for image/bmp in validateData()
+        validateContentType() unless $cgi->param('ispatch');
+        $data = validateData();
+        $contenttype = SqlQuote($cgi->param('contenttype'));
+        $isurl = 0;
+    }
 
     my @obsolete_ids = ();
     @obsolete_ids = validateObsolete() if $cgi->param('obsolete');
@@ -946,7 +964,6 @@ sub insert
     # Escape characters in strings that will be used in SQL statements.
     my $sql_filename = SqlQuote($filename);
     my $description = SqlQuote($cgi->param('description'));
-    my $contenttype = SqlQuote($cgi->param('contenttype'));
     my $isprivate = $cgi->param('isprivate') ? 1 : 0;
 
   # Figure out when the changes were made.
@@ -956,10 +973,10 @@ sub insert
   # Insert the attachment into the database.
   my $sth = $dbh->prepare("INSERT INTO attachments
       (bug_id, creation_ts, filename, description,
-       mimetype, ispatch, isprivate, submitter_id) 
+       mimetype, ispatch, isurl, isprivate, submitter_id) 
       VALUES ($bugid, $sql_timestamp, $sql_filename,
               $description, $contenttype, " . $cgi->param('ispatch') . ",
-              $isprivate, $userid)");
+              $isurl, $isprivate, $userid)");
   $sth->execute();
   # Retrieve the ID of the newly created attachment record.
   my $attachid = $dbh->bz_last_key('attachments', 'attach_id');
@@ -1096,14 +1113,20 @@ sub edit
   my ($attach_id) = validateID();
 
   # 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
            INNER JOIN attach_data
            ON 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
   # to use in a navigation bar across the top of the screen.
@@ -1135,9 +1158,11 @@ sub edit
   $vars->{'bugid'} = $bugid; 
   $vars->{'bugsummary'} = $bugsummary; 
   $vars->{'ispatch'} = $ispatch; 
+  $vars->{'isurl'} = $isurl; 
   $vars->{'isobsolete'} = $isobsolete; 
   $vars->{'isprivate'} = $isprivate; 
   $vars->{'datasize'} = $datasize;
+  $vars->{'thedata'} = $thedata;
   $vars->{'isviewable'} = $isviewable; 
   $vars->{'attachments'} = \@bugattachments; 
   $vars->{'GetBugLink'} = \&GetBugLink;
index 372d4ff87f8c8788cea867749dc8257c6aae5b00..e3c5a4af5e9f7b07d8190be16c379ac9548adab0 100755 (executable)
@@ -1742,6 +1742,7 @@ AddFDef("content", "Content", 0);
 
 $dbh->do("DELETE FROM fielddefs WHERE name='attachments.thedata'");
 AddFDef("attach_data.thedata", "Attachment data", 0);
+AddFDef("attachments.isurl", "Attachment is a URL", 0);
 
 ###########################################################################
 # Detect changed local settings
@@ -4033,6 +4034,9 @@ if ($dbh->bz_column_info("series", "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
 # differential change code *** A B O V E *** this comment.
index 963d89e0893e8ea8b00edbeb8ebc530ab31854f8..ef89c4af00ad44e3ea21d37f28fcce5757db096c 100644 (file)
@@ -25,6 +25,9 @@
 %]
 
 [% 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 " _
                   "accept patches greater than this number of kilobytes in size. " _
                   "To accept patches of any size (subject to the limitations of " _
@@ -44,4 +47,4 @@
   convert_uncompressed_images => "If this option is on, attachments with content type image/bmp " _
                                  "will be converted to image/png and compressed before uploading to " _
                                  "the database to conserve disk space." }
-%]
\ No newline at end of file
+%]
index 43af6e638b5751e62ce7bee42160592bdae890eb..ba725eae70a23021070f034b7b0d7f8dc0ec979e 100644 (file)
@@ -17,6 +17,8 @@
   # Rights Reserved.
   #
   # Contributor(s): Myk Melez <myk@mozilla.org>
+  #                 Joel Peshkin <bugreport@peshkin.net>
+  #                 Erik Stambaugh <erik@dasbistro.com>
   #%]
 
 [% PROCESS global/variables.none.tmpl %]
   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">
   <input type="hidden" name="bugid" value="[% bugid %]">
   <input type="hidden" name="action" value="insert">
       <th><label for="data">File:</label></th>
       <td>
         <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>
     </tr>
     [% IF Param("maxlocalattachment") %]
       </td>
     </tr>
     [% 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>
       <th><label for="description">Description:</label></th>
       <td>
         <input type="radio" id="list"
                name="contenttypemethod" value="list">
           <label for="list">select from list:</label>
-          <select name="contenttypeselection"
+          <select name="contenttypeselection" id="contenttypeselection"
                   onchange="this.form.contenttypemethod[1].checked = true;">
             [% PROCESS "attachment/content-types.html.tmpl" %]
           </select><br>
         <input type="radio" id="manual"
                      name="contenttypemethod" value="manual">
           <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;">
       </td>
     </tr>
index e46d5e552d1ad934dca37224ddec896c2007019f..7addd3278acde3c24f648544b28d3eeeac99e27d 100644 (file)
         <b>Description:</b><br>
           <textarea rows="3" cols="25" name="description" wrap="soft">[% description FILTER html %]</textarea><br>
 
-        <b>Filename:</b><br>
-          <input type="text" size="20" name="filename" value="[% filename FILTER html %]"><br>
-        <b>Size: </b>[% datasize FILTER unitconvert %]<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>
+            <input type="text" size="20" name="filename" value="[% filename FILTER html %]"><br>
+          <b>Size: </b>[% datasize FILTER unitconvert %]<br>
 
-        <b>MIME Type:</b><br>
-          <input type="text" size="20" name="contenttypeentry" value="[% contenttype FILTER html %]"><br>
+          <b>MIME Type:</b><br>
+            <input type="text" size="20" name="contenttypeentry" value="[% contenttype FILTER html %]"><br>
 
           <input type="checkbox" id="ispatch" name="ispatch" value="1"
                  [% 'checked="checked"' IF ispatch %]>
           <label for="ispatch">patch</label>
+        [% END %]
           <input type="checkbox" id="isobsolete" name="isobsolete" value="1"
                  [% 'checked="checked"' IF isobsolete %]>
           <label for="isobsolete">obsolete</label><br>
         </div>
 
         <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 %]
          | <a href="attachment.cgi?id=[% attachid %]&action=diff">Diff</a>
         [% END %]
             //-->
           </script>
         </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 %]
         <td id="noview" width="50%">
           <p><b>
index 61b68ee5377feb9922bef7e6254419df375e979a..7de5fb3714883edc3769d94828e51ca5032d6c1a 100644 (file)
@@ -43,6 +43,8 @@
         <td valign="top">
           [% IF attachment.ispatch %]
             <i>patch</i>
+          [% ELSIF attachment.isurl %]
+            <i>url</i>
           [% ELSE %]
             [% attachment.contenttype FILTER html %]
           [% END %]