diff --git a/lib/NGCP/Panel/Controller/Subscriber.pm b/lib/NGCP/Panel/Controller/Subscriber.pm
index 3d9552da21..4e21209633 100644
--- a/lib/NGCP/Panel/Controller/Subscriber.pm
+++ b/lib/NGCP/Panel/Controller/Subscriber.pm
@@ -14,6 +14,7 @@ use NGCP::Panel::Utils::Preferences;
use NGCP::Panel::Utils::Message;
use NGCP::Panel::Utils::DateTime;
use NGCP::Panel::Utils::Sems;
+use NGCP::Panel::Utils::Hylafax;
use NGCP::Panel::Form::Subscriber;
use NGCP::Panel::Form::SubscriberEdit;
use NGCP::Panel::Form::Customer::PbxExtensionSubscriberEdit;
@@ -41,6 +42,7 @@ use NGCP::Panel::Form::Faxserver::Active;
use NGCP::Panel::Form::Faxserver::SendStatus;
use NGCP::Panel::Form::Faxserver::SendCopy;
use NGCP::Panel::Form::Faxserver::Destination;
+use NGCP::Panel::Form::Subscriber::Webfax;
use NGCP::Panel::Utils::XMLDispatcher;
use UUID;
@@ -294,8 +296,103 @@ sub base :Chained('sub_list') :PathPart('') :CaptureArgs(1) {
{ name => "choice", search => 1, title => $c->loc('Slot') },
{ name => "destination", search => 1, title => $c->loc('Destination') },
]);
+ $c->stash->{fax_dt_columns} = NGCP::Panel::Utils::Datatables::set_columns($c, [
+ { name => "id", search => 1, title => $c->loc('#') },
+ { name => "the_timestamp", search_from_epoch => 1, search_to_epoch => 1, title => $c->loc('Timestamp') },
+ { name => "status", search => 1, title => $c->loc('Status') },
+ { name => "duration", search => 1, title => $c->loc('Duration') },
+ { name => "direction", search => 1, title => $c->loc('Direction') },
+ { name => "peer_number", search => 1, title => $c->loc('Peer Number') },
+ { name => "pages", search => 1, title => $c->loc('Pages') },
+ ]);
+}
+
+sub webfax :Chained('base') :PathPart('webfax') :Args(0) {
+ my ($self, $c) = @_;
+
+ $c->stash(
+ template => 'subscriber/webfax.tt',
+ );
+}
+
+sub webfax_send :Chained('base') :PathPart('webfax/send') :Args(0) {
+ my ($self, $c) = @_;
+
+ my $subscriber = $c->stash->{subscriber};
+ my $form = NGCP::Panel::Form::Subscriber::Webfax->new;
+ my $posted = ($c->request->method eq 'POST');
+
+ my $params = {};
+ if($posted) {
+ $c->req->params->{faxfile} = $c->req->upload('faxfile');
+ }
+ $form->process(
+ posted => $posted,
+ params => $c->req->params,
+ );
+
+ NGCP::Panel::Utils::Navigation::check_form_buttons(
+ c => $c,
+ form => $form,
+ fields => {},
+ back_uri => $c->uri_for_action('/subscriber/webfax', $c->req->captures),
+ );
+
+ if($posted && $form->validated) {
+ try {
+ if(defined $form->params->{faxfile}) {
+ NGCP::Panel::Utils::Hylafax::send_fax(
+ c => $c,
+ subscriber => $subscriber,
+ destination => $form->params->{destination},
+ #resolution => 'low', # opt (low, medium, extended)
+ #notify => 'someone@example.org', # TODO: handle in send_fax, read from prefs!
+ #coverpage => 1,
+ upload => $form->params->{faxfile},
+ );
+ } else {
+ NGCP::Panel::Utils::Hylafax::send_fax(
+ c => $c,
+ subscriber => $subscriber,
+ destination => $form->params->{destination},
+ #resolution => 'low', # opt (low, medium, extended)
+ #notify => 'someone@example.org', # TODO: handle in send_fax, read from prefs!
+ #coverpage => 1,
+ data => $form->params->{data},
+ );
+ }
+ } catch($e) {
+ NGCP::Panel::Utils::Message->error(
+ c => $c,
+ error => "failed to send fax: $e",
+ desc => $c->loc('Internal error while sending fax'),
+ );
+ NGCP::Panel::Utils::Navigation::back_or($c, $c->uri_for_action('/subscriber/webfax', $c->req->captures));
+ return;
+ }
+ }
+
+ $c->stash(
+ template => 'subscriber/webfax.tt',
+ form => $form,
+ create_flag => 1,
+ );
}
+sub webfax_ajax :Chained('base') :PathPart('webfax/ajax') :Args(0) {
+ my ($self, $c) = @_;
+
+ my $s = $c->stash->{subscriber}->provisioning_voip_subscriber;
+ my $fax_rs = $c->model('DB')->resultset('fax_journal')->search({
+ subscriber_id => $s->id,
+ });
+
+ NGCP::Panel::Utils::Datatables::process($c, $fax_rs, $c->stash->{fax_dt_columns});
+
+ $c->detach( $c->view("JSON") );
+}
+
+
sub webphone :Chained('base') :PathPart('webphone') :Args(0) {
my ($self, $c) = @_;
diff --git a/lib/NGCP/Panel/Form/Subscriber/Webfax.pm b/lib/NGCP/Panel/Form/Subscriber/Webfax.pm
new file mode 100644
index 0000000000..398f673c3d
--- /dev/null
+++ b/lib/NGCP/Panel/Form/Subscriber/Webfax.pm
@@ -0,0 +1,76 @@
+package NGCP::Panel::Form::Subscriber::Webfax;
+
+use HTML::FormHandler::Moose;
+extends 'HTML::FormHandler';
+use Moose::Util::TypeConstraints;
+
+use HTML::FormHandler::Widget::Block::Bootstrap;
+
+has '+widget_wrapper' => ( default => 'Bootstrap' );
+has '+enctype' => ( default => 'multipart/form-data');
+has_field 'submitid' => ( type => 'Hidden' );
+sub build_render_list {[qw/submitid fields actions/]}
+sub build_form_element_class {[qw(form-horizontal)]}
+
+has_field 'destination' => (
+ type => 'Text',
+ label => 'Destination Number',
+ required => 1,
+ element_attr => {
+ rel => ['tooltip'],
+ title => ['The number to send the fax to']
+ },
+);
+
+has_field 'data' => (
+ type => 'TextArea',
+ label => 'Content',
+ cols => 200,
+ rows => 10,
+ maxlength => '1048576', # 1MB
+ element_class => [qw/ngcp-autoconf-area/],
+);
+
+has_field 'faxfile' => (
+ type => 'Upload',
+ max_size => 67108864,
+ label => 'or File',
+ element_attr => {
+ rel => ['tooltip'],
+ title => ['Supported File Types are TXT, PDF, PS, TIFF']
+ },
+);
+
+has_field 'save' => (
+ type => 'Submit',
+ value => 'Send',
+ element_class => [qw/btn btn-primary/],
+ label => '',
+);
+
+has_block 'fields' => (
+ tag => 'div',
+ class => [qw/modal-body/],
+ render_list => [qw/destination data faxfile/],
+);
+
+has_block 'actions' => (
+ tag => 'div',
+ class => [qw/modal-footer/],
+ render_list => [qw/save/],
+);
+
+sub validate {
+ my $self = shift;
+ my $data = $self->field('data')->value;
+ my $upload = $self->field('faxfile')->value;
+
+ use Data::Printer; print ">>>>>>>>>>>>>>>>>>>>>> upload\n"; p $data; p $upload; p $self->fields;
+
+ unless($data || $upload) {
+ $self->field('faxfile')->add_error("You need to specify a file to fax, if no text is entered in the content field");
+ }
+}
+
+1;
+# vim: set tabstop=4 expandtab:
diff --git a/lib/NGCP/Panel/Utils/Hylafax.pm b/lib/NGCP/Panel/Utils/Hylafax.pm
new file mode 100644
index 0000000000..03cc7fd634
--- /dev/null
+++ b/lib/NGCP/Panel/Utils/Hylafax.pm
@@ -0,0 +1,89 @@
+package NGCP::Panel::Utils::Hylafax;
+
+use Sipwise::Base;
+use File::Temp qw/tempfile/;
+use TryCatch;
+
+use Data::Dumper;
+
+sub send_fax {
+ my (%args) = @_;
+
+ my $c = $args{c};
+ my $subscriber = $args{subscriber};
+ my $prov_subscriber = $subscriber->provisioning_voip_subscriber;
+
+ my $sendfax = $c->config->{faxserver}->{sendfax} // '/usr/bin/sendfax';
+ my @sendfax_args = ();
+
+ my $sender = 'webfax';
+ my $number;
+ if($subscriber->primary_number) {
+ $number = $subscriber->primary_number->cc .
+ ($subscriber->primary_number->ac // '').
+ $subscriber->primary_number->sn;
+ } else {
+ $number = $sender;
+ }
+ if($prov_subscriber->voip_fax_preference) {
+ $sender = $prov_subscriber->voip_fax_preference->name;
+ if($prov_subscriber->voip_fax_preference->password) {
+ push @sendfax_args, '-o '.$number.':'.$prov_subscriber->voip_fax_preference->password;
+ } else {
+ push @sendfax_args, '-o '.$number;
+ }
+ } else {
+ push @sendfax_args, '-o '.$number;
+ }
+
+ push @sendfax_args, '-h '.($c->config->{faxserver}->{ip} // '127.0.0.1');
+ if($args{notify}) {
+ push @sendfax_args, '-D';
+ push @sendfax_args, "-f '$sender <$args{notify}>'";
+ } else {
+ push @sendfax_args, "-f '$sender'";
+ }
+ unless($args{coverpage}) {
+ push @sendfax_args, '-n';
+ }
+ if($args{resolution}) {
+ if($args{resolution} eq 'low') {
+ push @sendfax_args, '-l';
+ } elsif($args{resolution} eq 'medium') {
+ push @sendfax_args, '-m';
+ } elsif($args{resolution} eq 'extended') {
+ push @sendfax_args, '-G';
+ }
+ }
+
+ push @sendfax_args, '-d '.$args{destination};
+
+ my ($fh, $filename);
+ if($args{data}) {
+ ($fh, $filename) = tempfile;
+ unless(print $fh $args{data}) {
+ my $err = $!;
+ close $fh;
+ unlink $filename;
+ die $c->loc("Failed to write fax data to temporary file: [_1]", $err);
+ }
+ close $fh;
+ } else {
+ $filename = eval { $args{upload}->tempname };
+ }
+ push @sendfax_args, $filename;
+
+ my $sa = join(' ', @sendfax_args);
+ my $output = `$sendfax $sa 2>&1`;
+ my $exit = $?;
+ unlink $filename;
+
+ if($exit ne '0') {
+ chomp $output;
+ die $output."\n";
+ }
+}
+
+1;
+
+# vim: set tabstop=4 expandtab:
diff --git a/ngcp_panel.conf b/ngcp_panel.conf
index d044f19384..8371fc452a 100644
--- a/ngcp_panel.conf
+++ b/ngcp_panel.conf
@@ -28,6 +28,11 @@ log4perl.appender.Default.layout.ConversionPattern=%d{ISO8601} [%p] [%F +%L] %m{
cloudpbx 1
+
+ sendfax /usr/bin/sendfax
+ ip 127.0.0.1
+
+
element_order source
element_order destination
diff --git a/share/templates/helpers/datatables.tt b/share/templates/helpers/datatables.tt
index ebf6f9a695..b1da51715c 100644
--- a/share/templates/helpers/datatables.tt
+++ b/share/templates/helpers/datatables.tt
@@ -26,6 +26,7 @@ $.extend( $.fn.dataTableExt.oStdClasses, {
} );
$(document).ready(function() {
+ var date_search_rendered = 0;
var [% helper.id_from_name %]_table = $('#[% helper.id_from_name %]_table')
.dataTable( {
"sDom": "<'row-fluid ngcp_dt_top_elements'lf>t<'row-fluid'<'pull-left'i><'pull-right'p>>",
@@ -114,6 +115,43 @@ $(document).ready(function() {
function() { $(this).find('.sw_actions').css('visibility','visible'); },
function() { $(this).find('.sw_actions').css('visibility','hidden'); }
);
+
+ if(date_search_rendered) return;
+ date_search_rendered = 1;
+ [% has_from = 0; has_to = 0; -%]
+ [% FOR col IN helper.dt_columns -%]
+ [% IF col.search_from_epoch && !has_from -%]
+ var f = '';
+ $('#[% helper.id_from_name %]_table_filter').prepend(f);
+ $('#[% helper.id_from_name %]_datepicker_start').datepicker({
+ "dateFormat": "yy-mm-dd",
+ "onSelect": function(date) {
+ [% helper.id_from_name %]_table.fnFilter(date, 0);
+ }
+ }).keyup( function () {
+ [% helper.id_from_name %]_table.fnFilter(this.value, 0);
+ });
+ [% has_from = 1 -%]
+ [% END -%]
+ [% IF col.search_to_epoch && !has_to -%]
+
+ var t = '';
+ if($('#[% helper.id_from_name %]_datepicker_start').length > 0) {
+ $('#[% helper.id_from_name %]_datepicker_start').parent().after(t);
+ } else {
+ $('#[% helper.id_from_name %]_table_filter').prepend(t);
+ }
+ $('#[% helper.id_from_name %]_datepicker_end').datepicker({
+ "dateFormat": "yy-mm-dd",
+ "onSelect": function(date) {
+ [% helper.id_from_name %]_table.fnFilter(date, 1);
+ }
+ }).keyup( function () {
+ [% helper.id_from_name %]_table.fnFilter(this.value, 1);
+ });
+ [% has_to = 1-%]
+ [% END -%]
+ [% END %]
},
"fnRowCallback": function(nRow, aData, iDisplayIndex) {
nRow.className = "sw_action_row";
@@ -121,40 +159,6 @@ $(document).ready(function() {
},
} );
- [% has_from = 0; has_to = 0; -%]
- [% FOR col IN helper.dt_columns -%]
- [% IF col.search_from_epoch && !has_from -%]
- var f = '';
- $('#[% helper.id_from_name %]_table_filter').prepend(f);
- $('#[% helper.id_from_name %]_datepicker_start').datepicker({
- "dateFormat": "yy-mm-dd",
- "onSelect": function(date) {
- [% helper.id_from_name %]_table.fnFilter(date, 0);
- }
- }).keyup( function () {
- [% helper.id_from_name %]_table.fnFilter(this.value, 0);
- });
- [% has_from = 1 -%]
- [% END -%]
- [% IF col.search_to_epoch && !has_to -%]
-
- var t = '';
- if($('#[% helper.id_from_name %]_datepicker_start').length > 0) {
- $('#[% helper.id_from_name %]_datepicker_start').parent().after(t);
- } else {
- $('#[% helper.id_from_name %]_table_filter').prepend(t);
- }
- $('#[% helper.id_from_name %]_datepicker_end').datepicker({
- "dateFormat": "yy-mm-dd",
- "onSelect": function(date) {
- [% helper.id_from_name %]_table.fnFilter(date, 1);
- }
- }).keyup( function () {
- [% helper.id_from_name %]_table.fnFilter(this.value, 1);
- });
- [% has_to = 1-%]
- [% END -%]
- [% END %]
} );
diff --git a/share/templates/subscriber/webfax.tt b/share/templates/subscriber/webfax.tt
new file mode 100644
index 0000000000..51beae383a
--- /dev/null
+++ b/share/templates/subscriber/webfax.tt
@@ -0,0 +1,19 @@
+[% site_config.title = c.loc('Fax Journal for [_1]@[_2]', subscriber.username, subscriber.domain.domain) -%]
+
+[%
+ helper.name = c.loc('Fax');
+ helper.dt_columns = fax_dt_columns;
+ helper.column_sort = 'the_timestamp';
+ helper.form_object = form;
+ helper.create_flag = create_flag;
+ helper.ajax_uri = c.uri_for_action('/subscriber/webfax_ajax', [c.req.captures.0]);
+ helper.messages = messages;
+
+ helper.top_buttons = [
+ { name = c.loc('Send Fax'), uri = c.uri_for_action('/subscriber/webfax_send', [ subscriber.id ]), icon = 'icon-print' },
+ ];
+
+ PROCESS 'helpers/datatables.tt';
+%]
+
+[% # vim: set tabstop=4 syntax=html expandtab: -%]