Bug 87188 - SUPA image upload

git-svn-id: svn://svn.office.custis.ru/3rdparty/bugzilla.org/trunk@1404 6955db30-a419-402b-8a0d-67ecbb4d7f56
master
vfilippov 2011-09-30 15:14:36 +00:00
parent d5c6a82002
commit ab32f0f3ea
13 changed files with 201 additions and 58 deletions

View File

@ -60,6 +60,7 @@ use Bugzilla::Field;
use Bugzilla::Hook;
use LWP::MediaTypes;
use MIME::Base64;
use base qw(Bugzilla::Object);
@ -558,6 +559,9 @@ sub _check_data {
$params->{ispatch} = 0;
$params->{store_in_file} = 0;
}
elsif ($params->{base64_content}) {
$data = decode_base64($params->{base64_content});
}
else {
if ($params->{store_in_file} || !ref $params->{data}) {
# If it's a filehandle, just store it, not the content of the file
@ -608,11 +612,15 @@ sub _check_description {
}
sub _check_filename {
my ($invocant, $filename, $is_url) = @_;
my ($invocant, $filename, $params) = @_;
$is_url = $invocant->isurl if ref $invocant;
# No file is attached, so it has no name.
return '' if $is_url;
return '' if ref $invocant && $invocant->isurl || $params && $params->{isurl};
if ($params && $params->{base64_content})
{
$filename = $params->{description};
}
$filename = trim($filename);
$filename || ThrowUserError('file_not_specified');
@ -914,11 +922,12 @@ sub run_create_validators {
$params->{data} = $class->_check_data($params);
$params = $class->SUPER::run_create_validators($params);
$params->{filename} = $class->_check_filename($params->{filename}, $params->{isurl});
$params->{filename} = $class->_check_filename($params->{filename}, $params);
$params->{creation_ts} ||= Bugzilla->dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
$params->{modification_time} = $params->{creation_ts};
$params->{submitter_id} = Bugzilla->user->id || ThrowCodeError('invalid_user');
delete $params->{base64_content};
return $params;
}

View File

@ -65,6 +65,12 @@ sub get_param_list {
default => 0
},
{
name => 'use_supa_applet',
type => 'b',
default => 0,
},
# The maximum size (in bytes) for patches and non-patch attachments.
# The default limit is 1000KB, which is 24KB less than mysql's default
# maximum packet size (which determines how much data can be sent in a

View File

@ -521,6 +521,7 @@ sub insert {
isurl => scalar $cgi->param('attachurl'),
mimetype => $content_type,
store_in_file => scalar $cgi->param('bigfile'),
base64_content => scalar $cgi->param('base64_content'),
});
foreach my $obsolete_attachment (@obsolete_attachments) {

View File

@ -29,6 +29,11 @@ my $OPTIONAL_MODULES =
module => 'Net::IP::Match::XS',
feature => 'FOF-Sudo system-to-system authorization',
},
{
package => 'LWP-MediaTypes',
module => 'LWP::MediaTypes',
feature => 'Guessing attachment types',
},
];
required_modules('custis', $REQUIRED_MODULES);

BIN
js/Supa.jar Normal file

Binary file not shown.

View File

@ -21,15 +21,103 @@
* Marc Schumann <wurblzap@gmail.com>
*/
function switch_aft(n)
var SUPA_JAVA_DISABLED_ERROR = 'You cannot paste images from clipboard because Java support'+
' is not enabled in your browser. Please download Java plugin'+
' from http://java.com/';
function htmlspecialchars_decode(str)
{
var f = document.getElementById('form_type_file'+n);
var u = document.getElementById('form_type_url'+n);
var t = document.getElementById('form_type_text'+n);
document.getElementById('tr_file'+n).style.display = f && f.checked ? '' : 'none';
document.getElementById('tr_text'+n).style.display = t && t.checked ? '' : 'none';
if (u)
document.getElementById('tr_url'+n).style.display = u.checked ? '' : 'none';
str = str.replace(/&gt;/g, '>');
str = str.replace(/&lt;/g, '<');
str = str.replace(/&quot;/g, '"');
str = str.replace(/&apos;/g, '\'');
str = str.replace(/&amp;/g, '&');
return str;
}
function switchAttype(chk)
{
var t = chk.value=='text';
var s = chk.value=='supa';
var u = chk.value=='url';
var ur = document.getElementById('attype_url_row');
var sr = document.getElementById('attype_supa_row');
u = u && ur;
s = s && sr;
document.getElementById('attype_text_row').style.display = t ? '' : 'none';
document.getElementById('attype_file_row').style.display = t || s || u ? 'none' : '';
if (ur) ur.style.display = u ? '' : 'none';
if (sr) sr.style.display = s ? '' : 'none';
document.getElementById('content_type_row').style.display = s ? 'none' : '';
var d = document.getElementById('description');
if (s)
{
if (d.value == '')
d.value = 'Screenshot.png';
document.getElementById('manual').checked = true;
document.getElementById('contenttypeentry').value = 'image/png';
document.getElementById('ispatch').checked = false;
setContentTypeDisabledState(chk.form);
var sc = document.getElementById('supa_container');
if (sc.innerHTML.toLowerCase().indexOf('<applet') < 0)
sc.innerHTML = htmlspecialchars_decode(sc.innerHTML);
}
else
{
if (d.value == 'Screenshot.png')
d.value = '';
document.getElementById('base64_content').value = '';
}
}
function supaPasteAgain()
{
var s = document.getElementById('SupaApplet');
if (!s)
return false;
try
{
s.pasteFromClipboard();
}
catch(e)
{
alert(SUPA_JAVA_DISABLED_ERROR);
return false;
}
return true;
}
function setContentTypeDisabledState(form)
{
var isdisabled = false;
if (form.ispatch.checked)
isdisabled = true;
for (var i = 0; i < form.contenttypemethod.length; i++)
form.contenttypemethod[i].disabled = isdisabled;
form.contenttypeselection.disabled = isdisabled;
form.contenttypeentry.disabled = isdisabled;
}
function encodeSupaContent()
{
var s = document.getElementById('attype_supa');
if (s && s.checked)
{
try
{
s = document.getElementById('SupaApplet').getEncodedString();
}
catch(e)
{
alert(SUPA_JAVA_DISABLED_ERROR);
var c = document.getElementById('attype_file');
c.checked = true;
switchAttype(c);
return false;
}
document.getElementById('base64_content').value = s;
}
return true;
}
function validateAttachmentForm(theform)
@ -40,6 +128,8 @@ function validateAttachmentForm(theform)
alert(BUGZILLA.string.attach_desc_required);
return false;
}
if (!encodeSupaContent())
return false;
return true;
}

View File

@ -209,7 +209,8 @@ if (defined $cgi->param('version') && length $cgi->param('version'))
# Add an attachment if requested.
if (defined($cgi->upload('data')) || $cgi->param('attachurl') ||
$cgi->param('text_attachment')) {
$cgi->param('text_attachment') || $cgi->param('base64_content'))
{
$cgi->param('isprivate', $cgi->param('commentprivacy'));
# Must be called before create() as it may alter $cgi->param('ispatch').
@ -240,6 +241,7 @@ if (defined($cgi->upload('data')) || $cgi->param('attachurl') ||
isurl => scalar $cgi->param('attachurl'),
mimetype => $content_type,
store_in_file => scalar $cgi->param('bigfile'),
base64_content => scalar $cgi->param('base64_content'),
});
};
Bugzilla->error_mode($error_mode_cache);
@ -305,9 +307,10 @@ if (Bugzilla->usage_mode != USAGE_MODE_EMAIL)
sent => \@all_mail_results,
title => $title,
header => $header,
message => $vars->{message},
};
# CustIS Bug 38616 - CC list restriction
if ($bug->{restricted_cc})
if (!$ses->{message} && $bug->{restricted_cc})
{
$ses->{message_vars} = {
restricted_cc => [ map { $_->login } @{ $bug->{restricted_cc} } ],

View File

@ -128,7 +128,7 @@ if (my @f = $cgi->param("includefield")) {
%displayfields = map { $_ => 1 } grep { $displayfields{$_} } @f;
}
$vars->{'displayfields'} = \%displayfields;
$vars->{displayfields} = \%displayfields;
my $sd;
if (Bugzilla->session && ($sd = Bugzilla->session_data) && $sd->{sent})
@ -139,6 +139,8 @@ if (Bugzilla->session && ($sd = Bugzilla->session_data) && $sd->{sent})
header => undef,
sent_attrs => undef,
failed_checkers => undef,
message => undef,
message_vars => undef,
});
$vars->{last_title} = $sd->{title};
$vars->{last_header} = $sd->{header};
@ -152,7 +154,8 @@ if (Bugzilla->session && ($sd = Bugzilla->session_data) && $sd->{sent})
$vars->{$_} = $sd->{sent_attrs}->{$_} for keys %{$sd->{sent_attrs} || {}};
}
$cgi->send_header($format->{'ctype'});
$cgi->send_header($format->{ctype});
$template->process("$format->{'template'}", $vars)
|| ThrowTemplateError($template->error());
$template->process($format->{template}, $vars)
|| ThrowTemplateError($template->error());
exit;

View File

@ -66,6 +66,11 @@
"specify a URL when creating an attachment and " _
"treat the URL itself as if it were an attachment.",
use_supa_applet =>
"If this option is on, the <a href='http://supa.sourceforge.net/'>SUPA</a> java applet " _
"(Screenshot UPload Applet) will be enabled to allow uploading of images from the clipboard. " _
"Note this requires <a href='http://www.java.com/'>Java</a> support in user's browser.",
maxattachmentsize =>
"The maximum size (in kilobytes) of attachments <b>stored in the database</b>. " _
"$terms.Bugzilla will not accept attachments greater than this number " _

View File

@ -21,46 +21,66 @@
# Marc Schumann <wurblzap@gmail.com>
#%]
<script language="JavaScript">
function switch_afot(fot)
{
if (fot)
{
document.getElementById('afot_text_row').style.display='';
document.getElementById('afot_file_row').style.display='none';
}
else
{
document.getElementById('afot_text_row').style.display='none';
document.getElementById('afot_file_row').style.display='';
}
}
</script>
<tr class="expert_fields">
<th style="visibility: hidden; white-space: nowrap">Attachment text:</th><td><input onclick="switch_afot(false)" type="radio" name="att_file_or_text" id="afot_file" value="0" checked /> <label for="afot_file">Attach file</label> or <input onclick="switch_afot(true)" type="radio" name="att_file_or_text" id="afot_text" value="1" /> <label for="afot_text">Enter text</label> </td>
</tr>
<tr id="afot_file_row">
<th><label for="data">File</label>:</th>
[%# Don't remove this hidden text, its purpose is to align columns correctly! %]
<th style="visibility: hidden; white-space: nowrap">Attachment text:</th>
<td>
<em>Enter the path to the file on your computer.</em><br>
<input type="file" id="data" name="data" size="50"
onchange="DataFieldHandler([% Param("allow_attach_url") ? 1 : 0 %])"
>
<input onclick="switchAttype(this)" type="radio" name="attype" id="attype_file" value="file" checked="checked" />
<label for="attype_file">Attach file</label>
[% IF Param("allow_attach_url") %]
<input onclick="switchAttype(this)" type="radio" name="attype" id="attype_url" value="url" />
<label for="attype_url">Attach URL</label>
[% END %]
<input onclick="switchAttype(this)" type="radio" name="attype" id="attype_text" value="text" />
<label for="attype_text">Enter text</label>
[% IF Param("use_supa_applet") %]
<input onclick="switchAttype(this)" type="radio" name="attype" id="attype_supa" value="supa" />
<label for="attype_supa">Paste image from clipboard</label>
[% END %]
</td>
</tr>
<tr class="expert_fields" id="afot_text_row" style="display: none">
<tr id="attype_file_row">
<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"
onchange="DataFieldHandler([% Param("allow_attach_url") ? 1 : 0 %])" />
</td>
</tr>
<tr class="expert_fields" id="attype_text_row" style="display: none">
<th><label for="text_attachment">Attachment text:</label></th>
<td>
<em>Enter attachment text here instead of selecting file.</em><br>
<em>Enter or paste attachment text here:</em><br />
<textarea wrap="soft" id="text_attachment" name="text_attachment" rows="4" cols="80"></textarea>
</td>
</tr>
[% IF Param("use_supa_applet") %]
<tr class="expert_fields" id="attype_supa_row" style="display: none">
<th>
<a href="javascript:void supaPasteAgain()">Paste again</a>
<input type="hidden" name="base64_content" id="base64_content" value="" />
</th>
<td id="supa_container">
[% FILTER html %]
<applet id="SupaApplet" archive="js/Supa.jar"
code="de.christophlinder.supa.SupaApplet" width="400" height="300">
<param name="trace" value="true" />
<param name="pasteonload" value="true" />
<param name="clickforpaste" value="true" />
<param name="imagecodec" value="png" />
<param name="encoding" value="base64" />
<param name="previewscaler" value="fit to canvas" />
Please enable <a href="http://www.java.com/">Java</a> Applet support in your browser.
</applet>
[% END %]
</td>
</tr>
[% END %]
[% IF Param("maxlocalattachment") && !Param("force_attach_bigfile") %]
<tr class="expert_fields">
<th>BigFile:</th>
<td>
<input type="checkbox" id="bigfile"
name="bigfile" value="bigfile">
<input type="checkbox" id="bigfile" name="bigfile" value="bigfile" />
<label for="bigfile">
Big File - Stored locally and may be purged
</label>
@ -68,10 +88,10 @@ function switch_afot(fot)
</tr>
[% END %]
[% IF Param("allow_attach_url") %]
<tr class="expert_fields">
<tr class="expert_fields" id="attype_url_row" style="display: none">
<th><label for="attachurl">AttachURL</label>:</th>
<td>
<em>URL to be attached instead.</em><br>
<em>URL to be attached instead.</em><br />
<input type="text" id="attachurl" name="attachurl" size="60"
maxlength="2000"
onkeyup="URLFieldHandler()" onblur="URLFieldHandler()">
@ -82,16 +102,16 @@ function switch_afot(fot)
<tr>
<th><label for="description">Description</label>:</th>
<td>
<em>Describe the attachment briefly.</em><br>
<em>Describe the attachment briefly.</em><br />
<input type="text" id="description" name="description" size="60" maxlength="200" onchange="this._changed=true">
</td>
</tr>
<tr class="expert_fields">
<tr class="expert_fields" id="content_type_row">
<th>Content Type:</th>
<td>
<em>If the attachment is a patch, check the box below.</em><br>
<em>If the attachment is a patch, check the box below.</em><br />
<input type="checkbox" id="ispatch" name="ispatch" value="1"
onchange="setContentTypeDisabledState(this.form);">
onchange="setContentTypeDisabledState(this.form);" />
<label for="ispatch">patch</label><br /><br />
[%# Reset this whenever the page loads so that the JS state is up to date %]
<script type="text/javascript">
@ -100,17 +120,17 @@ function switch_afot(fot)
});
</script>
<em>Otherwise, choose a method for determining the content type.</em><br>
<em>Otherwise, choose a method for determining the content type.</em><br />
<input type="radio" id="autodetect"
name="contenttypemethod" value="autodetect" checked="checked">
<label for="autodetect">auto-detect</label><br>
<label for="autodetect">auto-detect</label><br />
<input type="radio" id="list"
name="contenttypemethod" value="list">
<label for="list">select from list</label>:
<select name="contenttypeselection" id="contenttypeselection"
onchange="this.form.contenttypemethod[1].checked = true;">
[% PROCESS "attachment/content-types.html.tmpl" %]
</select><br>
</select><br />
<input type="radio" id="manual"
name="contenttypemethod" value="manual">
<label for="manual">enter manually</label>:
@ -123,7 +143,7 @@ function switch_afot(fot)
<td> </td>
<td>
[% IF flag_types && flag_types.size > 0 %]
[% PROCESS "flag/list.html.tmpl" bug_id=bugid attach_id=attachid %]<br>
[% PROCESS "flag/list.html.tmpl" bug_id=bugid attach_id=attachid %]<br />
[% END %]
</td>
</tr>

View File

@ -272,7 +272,8 @@ TUI_hide_default('expert_fields');
[% END %]
<form name="Create" id="Create" method="post" action="post_bug.cgi"
enctype="multipart/form-data" onkeypress="return ctrlEnter(event||window.event,this)">
enctype="multipart/form-data" onkeypress="return ctrlEnter(event||window.event,this)"
onsubmit="return encodeSupaContent()">
<input type="hidden" name="product" value="[% product.name FILTER html %]">
<input type="hidden" name="token" value="[% token FILTER html %]">
<input type="hidden" name="cloned_bug_id" value="[% cloned_bug_id FILTER html %]">

View File

@ -393,7 +393,7 @@ document.write(' <input type="button" name="check_all" value="Check All" onclick
[% bug_status.name FILTER html %]
</option>
[% IF !bug_status.is_open %]
[% filtered_status = bug_status.name FILTER js %]
[% filtered_status = bug_status.name FILTER js %]
[% closed_status_array.push( filtered_status ) %]
[% END %]
[% END %]

View File

@ -153,7 +153,7 @@
[% IF dotweak %]
<td class="bz_checkbox_column">
<input type="checkbox" name="id_[% bug.bug_id %]">
<input type="checkbox" id="id_[% bug.bug_id %]" name="id_[% bug.bug_id %]" />
</td>
[% END %]