summaryrefslogtreecommitdiff
path: root/httemplate
diff options
context:
space:
mode:
Diffstat (limited to 'httemplate')
-rw-r--r--httemplate/browse/msg_template.html3
-rw-r--r--httemplate/browse/template_image.html68
-rw-r--r--httemplate/edit/msg_template.html11
-rw-r--r--httemplate/elements/form-file_upload.html1
-rw-r--r--httemplate/elements/images/ui-icons_ef8c08_256x240.pngbin0 -> 4369 bytes
-rw-r--r--httemplate/elements/template_image-dialog.html279
-rw-r--r--httemplate/misc/email-customers.html2
-rw-r--r--httemplate/misc/process/template_image-delete.cgi28
-rw-r--r--httemplate/misc/process/template_image-upload.cgi26
-rw-r--r--httemplate/misc/xmlhttp-template_image.cgi48
10 files changed, 462 insertions, 4 deletions
diff --git a/httemplate/browse/msg_template.html b/httemplate/browse/msg_template.html
index ef0b2dafd..1646bc169 100644
--- a/httemplate/browse/msg_template.html
+++ b/httemplate/browse/msg_template.html
@@ -28,6 +28,7 @@ my @menubar = ();
if ( $curuser->access_right(['Edit templates', 'Edit global templates']) ) {
push @menubar, 'Add a new template' => $p.'edit/msg_template.html';
}
+push @menubar, 'View template images' => $p.'browse/template_image.html';
my $link = [ "${p}edit/msg_template.html?msgnum=", 'msgnum' ];
@@ -52,7 +53,7 @@ my $disable_link = sub {
action => $p.'misc/disable-msg_template.cgi?msgnum=' .
$template->msgnum .
($template->disabled ? ';enable=1' : ''),
- actionlabel => 'Disable lemplate',
+ actionlabel => 'Disable template',
);
};
diff --git a/httemplate/browse/template_image.html b/httemplate/browse/template_image.html
new file mode 100644
index 000000000..eb4325f15
--- /dev/null
+++ b/httemplate/browse/template_image.html
@@ -0,0 +1,68 @@
+<% include('/elements/init_overlib.html') %>
+
+<% include( 'elements/browse.html',
+ 'title' => 'Template images',
+ 'name_singular' => 'image',
+ 'menubar' => \@menubar,
+ 'query' => { 'table' => 'template_image', },
+ 'count_query' => 'SELECT COUNT(*) FROM template_image',
+ 'agent_virt' => 1,
+ 'agent_null_right' => ['View global templates','Edit global templates'],
+ 'agent_pos' => 1,
+ 'header' => [ 'Name', '', '' ],
+ 'fields' => [ 'name', $tag, $delete_text ],
+ 'links' => [ '', '', '' ],
+ 'cell_style' => [ '', '', '' ],
+ )
+%>
+
+<% include('/elements/template_image-dialog.html',
+ 'url' => $p.'browse/template_image.html'
+ ) %>
+
+<%init>
+use FS::template_image;
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+ unless $curuser->access_right([ 'View templates', 'View global templates',
+ 'Edit templates', 'Edit global templates', ]);
+
+my $canedit = $curuser->access_right(['Edit templates', 'Edit global templates']);
+
+my @menubar = ();
+if ($canedit) {
+ push @menubar, 'Upload a new image' => 'javascript:insertImageDialog(\'upload\')';
+}
+push @menubar, ( 'View message templates' => $p.'browse/msg_template.html' );
+
+my $tag = sub { qq!<A HREF="javascript:insertImageDialog(! . $_[0]->imgnum . qq!)">view</A>! };
+
+my $delete_text = $canedit ? sub {
+ my $image = shift;
+ my $imgnum = $image->imgnum;
+ unless ($image->agentnum) {
+ unless ($FS::CurrentUser::CurrentUser->access_right('Edit global templates')) {
+ return '';
+ }
+ }
+ my $out = <<EOF;
+<FORM name="delete_template_image_$imgnum">
+<INPUT TYPE="hidden" name="imgnum" value="$imgnum">
+</FORM>
+EOF
+ $out .= include('/elements/progress-init.html',
+ "delete_template_image_$imgnum",
+ [ 'imgnum' ],
+ $p.'misc/process/template_image-delete.cgi',
+ $p.'browse/template_image.html',
+ "imgnum$imgnum",
+ );
+ my $onclick = 'if ( confirm(\'';
+ $onclick .= emt('Are you sure you want to delete template image ') . $imgnum;
+ $onclick .= '\') ) { imgnum' . $imgnum . 'process() }';
+ return $out . '<A HREF="javascript:void(0)" ONCLICK="' . $onclick . '">delete</A>';
+} : '';
+
+</%init>
diff --git a/httemplate/edit/msg_template.html b/httemplate/edit/msg_template.html
index 7f3824127..df72c5b66 100644
--- a/httemplate/edit/msg_template.html
+++ b/httemplate/edit/msg_template.html
@@ -27,6 +27,7 @@
'no_submit' => $no_submit,
&>
<%init>
+use FS::template_image;
my $curuser = $FS::CurrentUser::CurrentUser;
@@ -345,10 +346,16 @@ function areyousure(url, message) {
<TD valign="top"><FORM name="dummy">
Substitutions: '
. $widget->html .
-'<BR>Click links to insert.
-<BR>Enclose substitutions and other Perl expressions in braces:
+'<P>Click above links to insert substitution code.</P>
+<P>
+Enclose substitutions and other Perl expressions in braces:
<BR>{ $name } = ExampleCo (Smith, John)
<BR>{ time2str("%D", time) } = '.time2str("%D", time).'
+</P>';
+$sidebar .= include('/elements/template_image-dialog.html',
+ 'callback' => 'insertHtml'
+ );
+$sidebar .= '<P><A HREF="javascript:insertImageDialog()">Insert Uploaded Image</A></P>
</FONT></TD>
';
diff --git a/httemplate/elements/form-file_upload.html b/httemplate/elements/form-file_upload.html
index 45b6c97f2..3542a5a8e 100644
--- a/httemplate/elements/form-file_upload.html
+++ b/httemplate/elements/form-file_upload.html
@@ -69,6 +69,7 @@ Example:
<div style="display:none:" id="uploadError"></div>
<FORM NAME = "<% $opt{name} %>"
+ ID = "<% $opt{id} %>"
ACTION = "<% $fsurl %>misc/file-upload.html"
METHOD = "POST"
ENCTYPE = "multipart/form-data"
diff --git a/httemplate/elements/images/ui-icons_ef8c08_256x240.png b/httemplate/elements/images/ui-icons_ef8c08_256x240.png
new file mode 100644
index 000000000..85e63e9f6
--- /dev/null
+++ b/httemplate/elements/images/ui-icons_ef8c08_256x240.png
Binary files differ
diff --git a/httemplate/elements/template_image-dialog.html b/httemplate/elements/template_image-dialog.html
new file mode 100644
index 000000000..5691d52b5
--- /dev/null
+++ b/httemplate/elements/template_image-dialog.html
@@ -0,0 +1,279 @@
+<%doc>
+
+Creates a jquery dialog box that opens when javascript function insertImageDialog
+is called, allows user to select an image and specify attributes for it, then passes
+img tag with base64 encoded data url to a callback javascript function.
+
+Accepts the following options:
+
+callback - pass the html for the selected img to this javascript function;
+if omitted, will only include fields for viewing/uploading image
+
+url - to redirect to after upload, otherwise just refreshes dialog window
+
+</%doc>
+
+<% include('/elements/xmlhttp.html',
+ 'url' => $p.'misc/xmlhttp-template_image.cgi',
+ 'subs' => [ 'get_template_image' ],
+ ) %>
+
+<DIV ID="insert_image_dialog" title="Template Images">
+
+<TABLE BORDER="0" STYLE="width: 100%"><TR><TD>
+
+<FORM ID="insert_image_form">
+
+<% &ntable("#cccccc", 2) %>
+
+ <TR>
+ <TH>Image</TH>
+ <TD>
+ <SELECT ID="insert_image_imgnum" ONCHANGE="insertImageDialog($('#insert_image_imgnum').val())">
+ <OPTION VALUE="">(select an image)</OPTION>
+ </SELECT>
+ </TD>
+ </TR>
+% if ($opt{'callback'}) {
+ <TR>
+ <TH>Width</TH>
+ <TD><INPUT TYPE="text" SIZE="5" ID="insert_image_width" ONCHANGE="previewInsertImage()"></TD>
+ </TR>
+ <TR>
+ <TH>Height</TH>
+ <TD><INPUT TYPE="text" SIZE="5" ID="insert_image_height" ONCHANGE="previewInsertImage()"></TD>
+ </TR>
+ <TR>
+ <TH>Align</TH>
+ <TD>
+ <SELECT ID="insert_image_float" ONCHANGE="previewInsertImage()">
+ <OPTION VALUE="none">inline</OPTION>
+ <OPTION VALUE="left">left</OPTION>
+ <OPTION VALUE="right">right</OPTION>
+ </SELECT>
+ </TD>
+ </TR>
+ <TR>
+ <TH>Alt Text</TH>
+ <TD><INPUT TYPE="text" SIZE="20" ID="insert_image_alt" ONCHANGE="previewInsertImage()"></TD>
+ </TR>
+ <TR>
+ <TD COLSPAN="2" ALIGN="center" STYLE="padding-top:6px">
+ <INPUT TYPE="button" ID="insert_image_button" VALUE="Insert Image" ONCLICK="insertImage()">
+ </TD>
+ </TR>
+% } # if $opt{'callback'}
+
+</TABLE>
+
+</FORM>
+
+% if ($canedit) {
+
+<P><B><% emt('Upload New Image') %></B></P>
+
+<% include('/elements/form-file_upload.html',
+ 'name' => 'TemplateImageUploadForm',
+ 'id' => 'TemplateImageUploadForm',
+ 'action' => $p.'misc/process/template_image-upload.cgi',
+ 'num_files' => 1,
+ 'fields' => [ 'name', 'agentnum' ],
+ 'url' => $opt{'url'} || 'javascript:refreshImageList(1)',
+ )
+ %>
+
+ <% &ntable("#cccccc", 2) %>
+
+ <% include( '/elements/tr-input-text.html',
+ 'field' => 'name',
+ 'label' => 'Name',
+ 'required' => 1,
+ 'id' => 'upload_form_name',
+ )
+ %>
+
+ <% include( '/elements/tr-select-agent.html',
+ 'label' => "<B>Agent</B>",
+ 'empty_label' => 'Select agent',
+ 'agent_virt' => 1,
+ 'agent_null_right' => 'Edit global templates',
+ )
+ %>
+
+ <% include( '/elements/tr-file-upload.html',
+ 'field' => 'file',
+ 'label' => 'File',
+ )
+ %>
+
+ <TR>
+ <TD COLSPAN="2" ALIGN="center" STYLE="padding-top:6px">
+ <INPUT TYPE = "submit"
+ NAME = "submitButton"
+ ID = "submitButton"
+ VALUE = "Upload image"
+ >
+ </TD>
+ </TR>
+
+</TABLE>
+
+</FORM>
+
+% } #if canedit
+
+</TD><TD width="100%">
+
+<DIV ID="insert_image_preview_box">
+ <P><B><% emt('Image Preview') %></B></P>
+ <SPAN ID="insert_image_loading"><B>(<% emt('Loading image...') %>)</B></SPAN>
+ <IMG SRC="" ID="insert_image_preview">
+</DIV>
+
+</TD></TR></TABLE>
+</DIV>
+
+<SCRIPT>
+
+// initialize & close dialog window, initialize imgobj cache && image list
+$( '#insert_image_dialog' ).dialog({
+ width: 800,
+ height: 550,
+ resizable: true,
+ autoOpen: false,
+});
+var imgobj = new Object;
+refreshImageList(0);
+
+// this is the main func to invoke from links outside this file.
+// opens dialog if needed
+// updates dialog with passed imgnum
+// caches image info through an xmlhttp request if needed
+// pass 'upload' as imgnum for upload-only view
+function insertImageDialog (imgnum) {
+ if (imgnum == 'upload') {
+ $('#insert_image_form').hide();
+ $('#insert_image_preview_box').hide();
+ imgnum = undefined;
+ } else {
+ $('#insert_image_form').show();
+ $('#insert_image_preview_box').show();
+ }
+ if (imgnum && !imgobj[imgnum]) {
+ clearInsertImageDialog();
+ $('#insert_image_loading').show();
+ $('#insert_image_imgnum').val(imgnum);
+ get_template_image('imgnum',imgnum,
+ function (result) {
+ var images = JSON.parse(result) || [];
+ for (i = 0; i < images.length; i++) {
+ imgobj[images[i].imgnum] = images[i];
+ }
+ updateInsertImageDialog();
+ }
+ );
+ } else if (imgnum) {
+ $('#insert_image_imgnum').val(imgnum);
+ updateInsertImageDialog();
+ } else {
+ clearInsertImageDialog();
+ }
+ if (!$( '#insert_image_dialog' ).dialog( 'isOpen' )) {
+ $( '#insert_image_dialog' ).dialog( 'open' );
+ }
+}
+
+// sets dialog values to a default "Loading..." state, including imgnum
+function clearInsertImageDialog () {
+ $('#insert_image_imgnum').val('');
+ $('#insert_image_preview').attr('src','');
+ $('#insert_image_loading').hide();
+}
+
+// updates preview src from cache based on imgnum from form
+// then calls previewInsertImage
+function updateInsertImageDialog () {
+ var imgnum = $('#insert_image_imgnum').val();
+ $('#insert_image_loading').hide();
+ $('#insert_image_preview').attr('src',imgobj[imgnum].src);
+ previewInsertImage();
+}
+
+// updates preview width/height/alt/float based on current form values
+function previewInsertImage () {
+ $('#insert_image_preview').css('width',$('#insert_image_width').val());
+ $('#insert_image_preview').css('height',$('#insert_image_height').val());
+ $('#insert_image_preview').css('float',$('#insert_image_float').val());
+ $('#insert_image_preview').attr('alt',$('#insert_image_alt').val());
+}
+
+// constructs html based on the form contents,
+// passes it to callback & closes dialog
+function insertImage() {
+ var imgnum = $('#insert_image_imgnum').val();
+ if (!(imgnum && imgobj[imgnum])) {
+ return '';
+ }
+ var width = $('#insert_image_width').val() || '';
+ var height = $('#insert_image_height').val() || '';
+ var alt = $('#insert_image_alt').val() || '';
+ var float = $('#insert_image_float').val();
+ var imgtag = '<IMG SRC="' + imgobj[imgnum].src + '"';
+ if (width) {
+ imgtag += ' WIDTH="' + width + '"';
+ }
+ if (height) {
+ imgtag += ' HEIGHT="' + height + '"';
+ }
+ if (alt) {
+ imgtag += ' ALT="' + alt + '"';
+ }
+ if (float) {
+ imgtag += ' STYLE="float: ' + float + '"';
+ }
+ imgtag += '>';
+ <% $opt{'callback'} %>(imgtag);
+ $( '#insert_image_dialog' ).dialog( 'close' );
+}
+
+// uses xmlhttp request to initialize image list & refresh it after uploads
+function refreshImageList (fromupload) {
+ get_template_image('no_src','1',
+ function (result) {
+ if (fromupload) {
+ $("#TemplateImageUploadForm")[0].reset();
+ }
+ var images = JSON.parse(result) || [];
+ var latest;
+ for (i = 0; i < images.length; i++) {
+ if ( $("#insert_image_imgnum option[value='" + images[i].imgnum + "']").length == 0 ) {
+ $("#insert_image_imgnum").append('<OPTION VALUE="'+images[i].imgnum+'">'+images[i].name+'</OPTION>');
+ latest = images[i].imgnum;
+ }
+ }
+ if (fromupload) {
+ location.hash = "insert_image_dialog";
+ if (latest) {
+ // small risk of a race condition with other newly-uploaded images,
+ // but does no real damage (our image still shows up in the list)
+ insertImageDialog(latest);
+ }
+ }
+ }
+ );
+}
+
+</SCRIPT>
+
+<%init>
+my %opt = @_;
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+ unless $curuser->access_right([ 'View templates', 'View global templates',
+ 'Edit templates', 'Edit global templates', ]);
+
+my $canedit = $curuser->access_right([ 'Edit templates', 'Edit global templates' ]);
+</%init>
+
diff --git a/httemplate/misc/email-customers.html b/httemplate/misc/email-customers.html
index 47e6a5b48..8ac44afc1 100644
--- a/httemplate/misc/email-customers.html
+++ b/httemplate/misc/email-customers.html
@@ -36,7 +36,7 @@ should be used to set msgnum or from/subject/html_body cgi params
% }
-<FORM NAME="OneTrueForm" ACTION="<% $form_action %>" METHOD="GET">
+<FORM NAME="OneTrueForm" ACTION="<% $form_action %>" METHOD="POST">
<INPUT TYPE="hidden" NAME="table" VALUE="<% $table %>">
%# Mixing search params with from address, subject, etc. required special-case
%# handling of those, risked name conflicts, and caused massive problems with
diff --git a/httemplate/misc/process/template_image-delete.cgi b/httemplate/misc/process/template_image-delete.cgi
new file mode 100644
index 000000000..58c3f2c68
--- /dev/null
+++ b/httemplate/misc/process/template_image-delete.cgi
@@ -0,0 +1,28 @@
+<% $server->process %>
+
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+# make sure user can generally edit
+die "access denied"
+ unless $curuser->access_right([ 'Edit templates', 'Edit global templates' ]);
+
+# make sure user can edit this particular image
+my %arg = $cgi->param('arg');
+my $imgnum = $arg{'imgnum'};
+die "bad imgnum" unless $imgnum =~ /^\d+$/;
+die "access denied" unless qsearchs({
+ 'table' => 'template_image',
+ 'select' => 'imgnum',
+ 'hashref' => { 'imgnum' => $imgnum },
+ 'extra_sql' => ' AND ' .
+ $curuser->agentnums_sql(
+ 'null_right' => ['Edit global templates']
+ ),
+ });
+
+my $server =
+ new FS::UI::Web::JSRPC 'FS::template_image::process_image_delete', $cgi;
+
+</%init>
diff --git a/httemplate/misc/process/template_image-upload.cgi b/httemplate/misc/process/template_image-upload.cgi
new file mode 100644
index 000000000..c3c905981
--- /dev/null
+++ b/httemplate/misc/process/template_image-upload.cgi
@@ -0,0 +1,26 @@
+<% $server->process %>
+
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+ unless $curuser->access_right([ 'Edit templates', 'Edit global templates' ]);
+
+my %arg = $cgi->param('arg');
+my $agentnum = $arg{'agentnum'};
+
+if (!$agentnum) {
+ die "access denied"
+ unless $curuser->access_right([ 'Edit global templates' ]);
+} else {
+ die "bad agentnum"
+ unless $agentnum =~ /^\d+$/;
+ die "access denied"
+ unless $curuser->agentnum($agentnum);
+}
+
+my $server =
+ new FS::UI::Web::JSRPC 'FS::template_image::process_image_upload', $cgi;
+
+</%init>
diff --git a/httemplate/misc/xmlhttp-template_image.cgi b/httemplate/misc/xmlhttp-template_image.cgi
new file mode 100644
index 000000000..a8c50edf0
--- /dev/null
+++ b/httemplate/misc/xmlhttp-template_image.cgi
@@ -0,0 +1,48 @@
+<%doc>
+Returns JSON encoded array of objects with details about FS::template_image
+objects. Attributes in each returned object are imgnum, name, and src.
+
+Accepts the following options:
+
+imgnum - only return object for this imgnum
+
+no_src - do not include the src field
+
+</%doc>
+<% encode_json(\@result) %>\
+<%init>
+use FS::template_image;
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+ unless $curuser->access_right([ 'View templates', 'View global templates',
+ 'Edit templates', 'Edit global templates', ]);
+
+my %arg = $cgi->param('arg');
+
+my $search = {
+ 'table' => 'template_image',
+ 'hashref' => {},
+};
+
+my $imgnum = $arg{'imgnum'} || '';
+die "Bad imgnum" unless $imgnum =~ /^\d*$/;
+$search->{'hashref'}->{'imgnum'} = $imgnum if $imgnum;
+
+$search->{'select'} = 'imgnum, name' if $arg{'no_src'};
+
+$search->{'extra_sql'} = ($imgnum ? ' AND ' : ' WHERE ')
+ . $curuser->agentnums_sql(
+ 'null_right' => ['View global templates','Edit global templates']
+ );
+
+my @images = qsearch($search); #needs agent virtualization
+
+my @result = map { +{
+ 'imgnum' => $_->imgnum,
+ 'name' => $_->name,
+ 'src' => $arg{'no_src'} ? '' : $_->src,
+} } @images;
+
+</%init>