| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package LaTeXML::Plugin::LtxMojo; |
|
2
|
1
|
|
|
1
|
|
1156
|
use Mojo::Base 'Mojolicious'; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
7
|
|
|
3
|
1
|
|
|
1
|
|
196117
|
use Mojo::JSON; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
44
|
|
|
4
|
1
|
|
|
1
|
|
18
|
use Mojo::IOLoop; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
11
|
|
|
5
|
1
|
|
|
1
|
|
22
|
use Mojo::ByteStream qw(b); |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
49
|
|
|
6
|
|
|
|
|
|
|
|
|
7
|
1
|
|
|
1
|
|
5
|
use File::Basename 'dirname'; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
42
|
|
|
8
|
1
|
|
|
1
|
|
5
|
use File::Spec::Functions qw(catdir catfile); |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
46
|
|
|
9
|
1
|
|
|
1
|
|
1180
|
use File::Temp qw(tempdir); |
|
|
1
|
|
|
|
|
11513
|
|
|
|
1
|
|
|
|
|
100
|
|
|
10
|
1
|
|
|
1
|
|
11
|
use File::Path qw(remove_tree); |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
58
|
|
|
11
|
|
|
|
|
|
|
|
|
12
|
1
|
|
|
1
|
|
1053
|
use Archive::Zip qw(:CONSTANTS :ERROR_CODES); |
|
|
1
|
|
|
|
|
44936
|
|
|
|
1
|
|
|
|
|
225
|
|
|
13
|
1
|
|
|
1
|
|
851
|
use IO::String; |
|
|
1
|
|
|
|
|
3053
|
|
|
|
1
|
|
|
|
|
58
|
|
|
14
|
1
|
|
|
1
|
|
8
|
use Encode; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
142
|
|
|
15
|
|
|
|
|
|
|
|
|
16
|
1
|
|
|
1
|
|
479
|
use LaTeXML::Common::Config; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
use LaTeXML::Util::Pathname qw(pathname_protocol); |
|
18
|
|
|
|
|
|
|
use LaTeXML; |
|
19
|
|
|
|
|
|
|
use LaTeXML::Plugin::LtxMojo::Startup; |
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
our $dbfile = '.LaTeXML_Mojo.cache'; |
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
# Every CPAN module needs a version |
|
24
|
|
|
|
|
|
|
our $VERSION = '0.4'; |
|
25
|
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
# This method will run once at server start |
|
27
|
|
|
|
|
|
|
sub startup { |
|
28
|
|
|
|
|
|
|
my $app = shift; |
|
29
|
|
|
|
|
|
|
# Switch to installable home directory |
|
30
|
|
|
|
|
|
|
$app->home->parse(catdir(dirname(__FILE__), 'LtxMojo')); |
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
# Switch to installable "public" directory |
|
33
|
|
|
|
|
|
|
$app->static->paths->[0] = $app->home->rel_dir('public'); |
|
34
|
|
|
|
|
|
|
# Switch to installable "templates" directory |
|
35
|
|
|
|
|
|
|
$app->renderer->paths->[0] = $app->home->rel_dir('templates'); |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
$ENV{MOJO_MAX_MESSAGE_SIZE} = 107374182; # close to 100 MB file upload limit |
|
38
|
|
|
|
|
|
|
$ENV{MOJO_REQUEST_TIMEOUT} = 600;# 10 minutes; |
|
39
|
|
|
|
|
|
|
$ENV{MOJO_CONNECT_TIMEOUT} = 120; # 2 minutes |
|
40
|
|
|
|
|
|
|
$ENV{MOJO_INACTIVITY_TIMEOUT} = 600; # 10 minutes; |
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
# Make signed cookies secure |
|
43
|
|
|
|
|
|
|
$app->secrets(['LaTeXML is the way to go!']); |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
#Prep a LaTeXML Startup instance |
|
46
|
|
|
|
|
|
|
my $startup = LaTeXML::Plugin::LtxMojo::Startup->new(dbfile => catfile($app->home,$dbfile)); |
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
# Do a one-time check for admin, add if none: |
|
49
|
|
|
|
|
|
|
$startup->modify_user('admin', 'admin', 'admin') |
|
50
|
|
|
|
|
|
|
unless $startup->exists_user('admin'); |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
$app->helper(convert_zip => sub { |
|
53
|
|
|
|
|
|
|
my ($self) = @_; |
|
54
|
|
|
|
|
|
|
# Make sure we point to the actual source directory |
|
55
|
|
|
|
|
|
|
my $name = $self->req->headers->header('x-file-name'); |
|
56
|
|
|
|
|
|
|
$name =~ s/\.zip$//; |
|
57
|
|
|
|
|
|
|
# HTTP GET parameters hold the conversion options |
|
58
|
|
|
|
|
|
|
my @all_params = @{ $self->req->url->query->params || [] }; |
|
59
|
|
|
|
|
|
|
my $opts=[]; |
|
60
|
|
|
|
|
|
|
# Ugh, disallow 'null' as a value!!! (TODO: Smarter fix??) |
|
61
|
|
|
|
|
|
|
while (my ($key,$value) = splice(@all_params,0,2)) { |
|
62
|
|
|
|
|
|
|
if ($key=~/^(?:local|path|destination|directory)$/) { |
|
63
|
|
|
|
|
|
|
# You don't get to specify harddrive info in the web service |
|
64
|
|
|
|
|
|
|
next; } |
|
65
|
|
|
|
|
|
|
$value = '' if ($value && ($value eq 'null')); |
|
66
|
|
|
|
|
|
|
push @$opts, ($key,$value); } |
|
67
|
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
my $config = LaTeXML::Common::Config->new(); |
|
69
|
|
|
|
|
|
|
$config->read_keyvals($opts); |
|
70
|
|
|
|
|
|
|
my @latexml_inputs = ('.',grep {defined} split(':',($ENV{LATEXMLINPUTS}||''))); |
|
71
|
|
|
|
|
|
|
$config->set('paths',\@latexml_inputs); |
|
72
|
|
|
|
|
|
|
$config->set('whatsin','archive'); |
|
73
|
|
|
|
|
|
|
$config->set('whatsout','archive'); |
|
74
|
|
|
|
|
|
|
$config->set('log',"$name.log"); |
|
75
|
|
|
|
|
|
|
$config->set('local',($self->tx->remote_address eq '127.0.0.1')); |
|
76
|
|
|
|
|
|
|
# Only HTML5 for now. |
|
77
|
|
|
|
|
|
|
$config->set('format','html5'); |
|
78
|
|
|
|
|
|
|
# Prepare and convert |
|
79
|
|
|
|
|
|
|
my $converter = LaTeXML->get_converter($config); |
|
80
|
|
|
|
|
|
|
$converter->prepare_session($config); |
|
81
|
|
|
|
|
|
|
my $source = $self->req->body; |
|
82
|
|
|
|
|
|
|
$source = "literal:".$source if ($source && (pathname_protocol($source) eq 'file')); |
|
83
|
|
|
|
|
|
|
my $response = $converter->convert($source); |
|
84
|
|
|
|
|
|
|
# Catch errors |
|
85
|
|
|
|
|
|
|
$self->render(text=>'Fatal: Internal Conversion Error, please contact the administrator.') unless |
|
86
|
|
|
|
|
|
|
(defined $response && ($response->{result})); |
|
87
|
|
|
|
|
|
|
# Return |
|
88
|
|
|
|
|
|
|
my $headers = Mojo::Headers->new; |
|
89
|
|
|
|
|
|
|
$headers->add('Content-Type',"application/zip;name=$name.zip"); |
|
90
|
|
|
|
|
|
|
$headers->add('Content-Disposition',"attachment;filename=$name.zip"); |
|
91
|
|
|
|
|
|
|
$self->res->content->headers($headers); |
|
92
|
|
|
|
|
|
|
return $self->render(data=>$response->{result}); |
|
93
|
|
|
|
|
|
|
}); |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
# TODO: Maybe reintegrate IF we support username-based profiles |
|
96
|
|
|
|
|
|
|
# if (!defined $opt->{profile}) { |
|
97
|
|
|
|
|
|
|
# if (defined $opt->{user} |
|
98
|
|
|
|
|
|
|
# && $startup->verify_user($opt->{user}, $opt->{password})) |
|
99
|
|
|
|
|
|
|
# { |
|
100
|
|
|
|
|
|
|
# $opt->{profile} = |
|
101
|
|
|
|
|
|
|
# $startup->lookup_user_property($opt->{user}, 'default') || 'custom'; |
|
102
|
|
|
|
|
|
|
# } |
|
103
|
|
|
|
|
|
|
# else { |
|
104
|
|
|
|
|
|
|
# $opt->{profile} = 'custom'; |
|
105
|
|
|
|
|
|
|
# } |
|
106
|
|
|
|
|
|
|
# } |
|
107
|
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
$app->helper(convert_string => sub { |
|
109
|
|
|
|
|
|
|
my ($self) = @_; |
|
110
|
|
|
|
|
|
|
my ($source,$is_jsonp); |
|
111
|
|
|
|
|
|
|
my $get_params = $self->req->url->query->params || []; |
|
112
|
|
|
|
|
|
|
my $post_params = $self->req->body_params->params || []; |
|
113
|
|
|
|
|
|
|
if (scalar(@$post_params) == 1) { |
|
114
|
|
|
|
|
|
|
$source = $post_params->[0]; |
|
115
|
|
|
|
|
|
|
$post_params=[]; |
|
116
|
|
|
|
|
|
|
} elsif ((scalar(@$post_params) == 2) && ($post_params->[0] !~ /^(?:tex|source)$/)) { |
|
117
|
|
|
|
|
|
|
$source = $post_params->[0].$post_params->[1]; |
|
118
|
|
|
|
|
|
|
$post_params=[]; |
|
119
|
|
|
|
|
|
|
} |
|
120
|
|
|
|
|
|
|
# We need to be careful to preserve the parameter order, so use arrayrefs |
|
121
|
|
|
|
|
|
|
my @all_params = (@$get_params, @$post_params); |
|
122
|
|
|
|
|
|
|
my $opts = []; |
|
123
|
|
|
|
|
|
|
# Ugh, disallow 'null' as a value!!! (TODO: Smarter fix??) |
|
124
|
|
|
|
|
|
|
while (my ($key,$value) = splice(@all_params,0,2)) { |
|
125
|
|
|
|
|
|
|
# JSONP ? |
|
126
|
|
|
|
|
|
|
if ($key eq 'jsonp') { |
|
127
|
|
|
|
|
|
|
$is_jsonp = $value; |
|
128
|
|
|
|
|
|
|
next; |
|
129
|
|
|
|
|
|
|
} elsif ($key =~ /^(?:tex|source)$/) { |
|
130
|
|
|
|
|
|
|
# TeX is data, separate |
|
131
|
|
|
|
|
|
|
$source = $value unless defined $source; |
|
132
|
|
|
|
|
|
|
next; |
|
133
|
|
|
|
|
|
|
} elsif ($key=~/^(?:local|path|destination|directory)$/) { |
|
134
|
|
|
|
|
|
|
# You don't get to specify harddrive info in the web service |
|
135
|
|
|
|
|
|
|
next; |
|
136
|
|
|
|
|
|
|
} elsif ($key=~/^(?:preamble|postamble)$/) { |
|
137
|
|
|
|
|
|
|
$value = "literal:".$value if ($value && (pathname_protocol($value) eq 'file')); |
|
138
|
|
|
|
|
|
|
} |
|
139
|
|
|
|
|
|
|
$value = '' if ($value && ($value eq 'null')); |
|
140
|
|
|
|
|
|
|
push @$opts, ($key,$value); |
|
141
|
|
|
|
|
|
|
} |
|
142
|
|
|
|
|
|
|
my $config = LaTeXML::Common::Config->new(); |
|
143
|
|
|
|
|
|
|
$config->read_keyvals($opts); |
|
144
|
|
|
|
|
|
|
# We now have a LaTeXML config object - $config. |
|
145
|
|
|
|
|
|
|
my @latexml_inputs = grep {defined} split(':',($ENV{LATEXMLINPUTS}||'')); |
|
146
|
|
|
|
|
|
|
$config->set('paths',\@latexml_inputs); |
|
147
|
|
|
|
|
|
|
my $converter = LaTeXML->get_converter($config); |
|
148
|
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
#Override/extend with session-specific options in $opt: |
|
150
|
|
|
|
|
|
|
$converter->prepare_session($config); |
|
151
|
|
|
|
|
|
|
# If there are no protocols, use literal: as default: |
|
152
|
|
|
|
|
|
|
if ((! defined $source) || (length($source)<1)) { |
|
153
|
|
|
|
|
|
|
$self->render(json => {result => '', status => "Fatal:input:empty No TeX provided on input", status_code=>3, |
|
154
|
|
|
|
|
|
|
log => "Status:conversion:3\nFatal:input:empty No TeX provided on input"}); |
|
155
|
|
|
|
|
|
|
} else { |
|
156
|
|
|
|
|
|
|
$source = "literal:".$source if ($source && (pathname_protocol($source) eq 'file')); |
|
157
|
|
|
|
|
|
|
#Send a request: |
|
158
|
|
|
|
|
|
|
my $response = $converter->convert($source); |
|
159
|
|
|
|
|
|
|
my ($result, $status, $status_code, $log); |
|
160
|
|
|
|
|
|
|
if (defined $response) { |
|
161
|
|
|
|
|
|
|
($result, $status, $status_code, $log) = map { $response->{$_} } qw(result status status_code log); |
|
162
|
|
|
|
|
|
|
} |
|
163
|
|
|
|
|
|
|
# Delete converter if Fatal occurred |
|
164
|
|
|
|
|
|
|
undef $converter unless defined $result; |
|
165
|
|
|
|
|
|
|
# TODO: This decode business is fishy... very fishy! |
|
166
|
|
|
|
|
|
|
if ($is_jsonp) { |
|
167
|
|
|
|
|
|
|
my $json_result = $self->render( |
|
168
|
|
|
|
|
|
|
json => {result => $result, |
|
169
|
|
|
|
|
|
|
status => $status, status_code=>$status_code, log => $log, partial=>1}); |
|
170
|
|
|
|
|
|
|
$self->render(data => "$is_jsonp($json_result)", format => 'js'); |
|
171
|
|
|
|
|
|
|
} elsif ($config->get('whatsout') eq 'archive') { # Archive conversion returns a ZIP |
|
172
|
|
|
|
|
|
|
$self->render(data => $result); |
|
173
|
|
|
|
|
|
|
} else { |
|
174
|
|
|
|
|
|
|
$self->render(json => {result => $result, status => $status, status_code=>$status_code, log => $log}); |
|
175
|
|
|
|
|
|
|
} |
|
176
|
|
|
|
|
|
|
} |
|
177
|
|
|
|
|
|
|
}); |
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
################################################ |
|
181
|
|
|
|
|
|
|
## ## |
|
182
|
|
|
|
|
|
|
## ROUTES ## |
|
183
|
|
|
|
|
|
|
## ## |
|
184
|
|
|
|
|
|
|
################################################ |
|
185
|
|
|
|
|
|
|
my $r = $app->routes; |
|
186
|
|
|
|
|
|
|
$r->post('/convert' => sub { |
|
187
|
|
|
|
|
|
|
my $self = shift; |
|
188
|
|
|
|
|
|
|
my $type = $self->req->headers->header('x-file-type'); |
|
189
|
|
|
|
|
|
|
if ($type && $type =~ 'zip' && ($self->req->headers->header('content-type') eq 'multipart/form-data')) { |
|
190
|
|
|
|
|
|
|
$self->convert_zip; |
|
191
|
|
|
|
|
|
|
} else { |
|
192
|
|
|
|
|
|
|
$self->convert_string; |
|
193
|
|
|
|
|
|
|
} |
|
194
|
|
|
|
|
|
|
}); |
|
195
|
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
$r->websocket('/convert' => sub { |
|
197
|
|
|
|
|
|
|
my $self = shift; |
|
198
|
|
|
|
|
|
|
my $json = Mojo::JSON->new; |
|
199
|
|
|
|
|
|
|
# Connected |
|
200
|
|
|
|
|
|
|
$self->app->log->debug('WebSocket connected.'); |
|
201
|
|
|
|
|
|
|
# Increase inactivity timeout for connection a bit |
|
202
|
|
|
|
|
|
|
Mojo::IOLoop->stream($self->tx->connection)->timeout(300); |
|
203
|
|
|
|
|
|
|
$self->on('message' => sub { |
|
204
|
|
|
|
|
|
|
my ($tx, $bytes) = @_; |
|
205
|
|
|
|
|
|
|
#TODO: We want the options in the right order, is this Decode safe in this respect? |
|
206
|
|
|
|
|
|
|
my $opts = $json->decode($bytes); |
|
207
|
|
|
|
|
|
|
my $source = $opts->{source}; delete $opts->{source}; |
|
208
|
|
|
|
|
|
|
$source = $opts->{tex} unless defined $opts->{source}; delete $opts->{tex}; |
|
209
|
|
|
|
|
|
|
my $config = LaTeXML::Common::Config->new(); |
|
210
|
|
|
|
|
|
|
$config->read_keyvals([%$opts]); |
|
211
|
|
|
|
|
|
|
# We now have a LaTeXML options object - $opt. |
|
212
|
|
|
|
|
|
|
my $converter = LaTeXML->get_converter($config); |
|
213
|
|
|
|
|
|
|
#Override/extend with session-specific options in $opt: |
|
214
|
|
|
|
|
|
|
$converter->prepare_session($config); |
|
215
|
|
|
|
|
|
|
#Send a request: |
|
216
|
|
|
|
|
|
|
my $response = $converter->convert($source); |
|
217
|
|
|
|
|
|
|
my ($result, $status, $log); |
|
218
|
|
|
|
|
|
|
if (defined $response) { |
|
219
|
|
|
|
|
|
|
if (! defined $response->{result}) { |
|
220
|
|
|
|
|
|
|
# Delete converter if Fatal occurred |
|
221
|
|
|
|
|
|
|
undef $converter; |
|
222
|
|
|
|
|
|
|
} else { |
|
223
|
|
|
|
|
|
|
#$response->{result} = decode('UTF-8',$response->{result}); |
|
224
|
|
|
|
|
|
|
} |
|
225
|
|
|
|
|
|
|
} |
|
226
|
|
|
|
|
|
|
$tx->send({text=>$json->encode($response)}); |
|
227
|
|
|
|
|
|
|
}); |
|
228
|
|
|
|
|
|
|
# Disconnected |
|
229
|
|
|
|
|
|
|
$self->on('finish' => sub { |
|
230
|
|
|
|
|
|
|
my $self = shift; |
|
231
|
|
|
|
|
|
|
$self->app->log->debug('WebSocket disconnected.'); |
|
232
|
|
|
|
|
|
|
}); |
|
233
|
|
|
|
|
|
|
}); |
|
234
|
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
$r->get('/login' => sub { |
|
236
|
|
|
|
|
|
|
my $self = shift; |
|
237
|
|
|
|
|
|
|
my $name = $self->param('name') || ''; |
|
238
|
|
|
|
|
|
|
my $pass = $self->param('pass') || ''; |
|
239
|
|
|
|
|
|
|
return $self->render |
|
240
|
|
|
|
|
|
|
unless ($startup->verify_user($name, $pass) eq 'admin'); |
|
241
|
|
|
|
|
|
|
$self->session(name => $name); |
|
242
|
|
|
|
|
|
|
$self->flash(message => "Thanks for logging in $name!"); |
|
243
|
|
|
|
|
|
|
$self->redirect_to('admin'); |
|
244
|
|
|
|
|
|
|
} => 'login'); |
|
245
|
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
$r->get('/about' => sub { |
|
247
|
|
|
|
|
|
|
my $self = shift; |
|
248
|
|
|
|
|
|
|
my $headers = Mojo::Headers->new; |
|
249
|
|
|
|
|
|
|
$headers->add('Content-Type', 'application/xhtml+xml'); |
|
250
|
|
|
|
|
|
|
$self->res->content->headers($headers); |
|
251
|
|
|
|
|
|
|
$self->render(); |
|
252
|
|
|
|
|
|
|
} => 'about'); |
|
253
|
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
$r->get('/demo' => sub { |
|
255
|
|
|
|
|
|
|
my $self = shift; |
|
256
|
|
|
|
|
|
|
} => 'demo'); |
|
257
|
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
$r->get('/editor' => sub { |
|
259
|
|
|
|
|
|
|
my $self = shift; |
|
260
|
|
|
|
|
|
|
my $headers = Mojo::Headers->new; |
|
261
|
|
|
|
|
|
|
$headers->add('Content-Type', 'application/xhtml+xml'); |
|
262
|
|
|
|
|
|
|
$self->res->content->headers($headers); |
|
263
|
|
|
|
|
|
|
$self->render(); |
|
264
|
|
|
|
|
|
|
} => 'editor'); |
|
265
|
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
$r->get('/editor5' => sub { |
|
267
|
|
|
|
|
|
|
my $self = shift; |
|
268
|
|
|
|
|
|
|
my $headers = Mojo::Headers->new; |
|
269
|
|
|
|
|
|
|
$headers->add('Content-Type', 'text/html'); |
|
270
|
|
|
|
|
|
|
$self->res->content->headers($headers); |
|
271
|
|
|
|
|
|
|
$self->render(); |
|
272
|
|
|
|
|
|
|
} => 'editor5'); |
|
273
|
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
$r->get('/ws-editor' => sub { |
|
275
|
|
|
|
|
|
|
my $self = shift; |
|
276
|
|
|
|
|
|
|
my $headers = Mojo::Headers->new; |
|
277
|
|
|
|
|
|
|
$headers->add('Content-Type', 'application/xhtml+xml'); |
|
278
|
|
|
|
|
|
|
$self->res->content->headers($headers); |
|
279
|
|
|
|
|
|
|
$self->render(); |
|
280
|
|
|
|
|
|
|
} => 'ws-editor'); |
|
281
|
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
$r->get('/' => sub { |
|
284
|
|
|
|
|
|
|
my $self = shift; |
|
285
|
|
|
|
|
|
|
return $self->redirect_to('about'); |
|
286
|
|
|
|
|
|
|
}); |
|
287
|
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
$r->get('/logout' => sub { |
|
289
|
|
|
|
|
|
|
my $self = shift; |
|
290
|
|
|
|
|
|
|
$self->session(expires => 1); |
|
291
|
|
|
|
|
|
|
$self->flash(message => "Successfully logged out!"); |
|
292
|
|
|
|
|
|
|
$self->redirect_to('login'); |
|
293
|
|
|
|
|
|
|
}); |
|
294
|
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
$r->get('/admin' => sub { |
|
296
|
|
|
|
|
|
|
my $self = shift; |
|
297
|
|
|
|
|
|
|
return $self->redirect_to('login') unless $self->session('name'); |
|
298
|
|
|
|
|
|
|
$self->stash(startup => $startup); |
|
299
|
|
|
|
|
|
|
$self->render; |
|
300
|
|
|
|
|
|
|
} => 'admin'); |
|
301
|
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
$r->get('/help' => sub { |
|
303
|
|
|
|
|
|
|
my $self = shift; |
|
304
|
|
|
|
|
|
|
$self->render; |
|
305
|
|
|
|
|
|
|
} => 'help'); |
|
306
|
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
$r->get('/upload' => sub { |
|
309
|
|
|
|
|
|
|
my $self = shift; |
|
310
|
|
|
|
|
|
|
$self->render; |
|
311
|
|
|
|
|
|
|
} => 'upload'); |
|
312
|
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
$r->post('/upload' => sub { |
|
314
|
|
|
|
|
|
|
my $self = shift; |
|
315
|
|
|
|
|
|
|
# TODO: Need a session? |
|
316
|
|
|
|
|
|
|
my $type = $self->req->headers->header('x-file-type'); |
|
317
|
|
|
|
|
|
|
if ($type && $type =~ 'zip' && ($self->req->headers->header('content-type') eq 'multipart/form-data')) { |
|
318
|
|
|
|
|
|
|
$self->convert_zip; |
|
319
|
|
|
|
|
|
|
} else { |
|
320
|
|
|
|
|
|
|
#.tex , .sty , .jpg and so on - write to filesystem (when are we done?) |
|
321
|
|
|
|
|
|
|
$self->render(text=>"Uploaded, but ignored!"); |
|
322
|
|
|
|
|
|
|
} |
|
323
|
|
|
|
|
|
|
}); |
|
324
|
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
$r->any('/ajax' => sub { |
|
326
|
|
|
|
|
|
|
my $self = shift; |
|
327
|
|
|
|
|
|
|
return $self->redirect_to('login') unless $self->session('name'); |
|
328
|
|
|
|
|
|
|
my $header = $self->req->headers->header('X-Requested-With'); |
|
329
|
|
|
|
|
|
|
if ($header && $header eq 'XMLHttpRequest') { |
|
330
|
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
# Users API: |
|
332
|
|
|
|
|
|
|
my $user_action = $self->param('user_action'); |
|
333
|
|
|
|
|
|
|
if ($user_action) { |
|
334
|
|
|
|
|
|
|
my $name = $self->param('name'); |
|
335
|
|
|
|
|
|
|
my $message = 'This request was empty, please resend with Name set!'; |
|
336
|
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
if ($user_action eq 'modify') { |
|
338
|
|
|
|
|
|
|
if ($name) { |
|
339
|
|
|
|
|
|
|
my $pass = $self->param('pass'); |
|
340
|
|
|
|
|
|
|
my $role = $self->param('role'); |
|
341
|
|
|
|
|
|
|
my $default = $self->param('default_profile'); |
|
342
|
|
|
|
|
|
|
$message = $startup->modify_user($name, $pass, $role, $default); |
|
343
|
|
|
|
|
|
|
} |
|
344
|
|
|
|
|
|
|
} |
|
345
|
|
|
|
|
|
|
elsif ($user_action eq 'add') { |
|
346
|
|
|
|
|
|
|
if ($name) { |
|
347
|
|
|
|
|
|
|
my $pass = $self->param('pass'); |
|
348
|
|
|
|
|
|
|
my $role = $self->param('role'); |
|
349
|
|
|
|
|
|
|
my $default = $self->param('default_profile'); |
|
350
|
|
|
|
|
|
|
$message = $startup->modify_user($name, $pass, $role, $default); } |
|
351
|
|
|
|
|
|
|
} |
|
352
|
|
|
|
|
|
|
elsif ($user_action eq 'delete') { $message = $startup->delete_user($name) if $name; } |
|
353
|
|
|
|
|
|
|
elsif ($user_action eq 'startup_users') { |
|
354
|
|
|
|
|
|
|
$self->render( |
|
355
|
|
|
|
|
|
|
json => { |
|
356
|
|
|
|
|
|
|
users => $startup->users |
|
357
|
|
|
|
|
|
|
} |
|
358
|
|
|
|
|
|
|
);} |
|
359
|
|
|
|
|
|
|
elsif ($user_action eq 'overview_users') { |
|
360
|
|
|
|
|
|
|
my $users = $startup->users; |
|
361
|
|
|
|
|
|
|
my $summary = []; |
|
362
|
|
|
|
|
|
|
push @$summary, $startup->summary_user($_) foreach (@$users); |
|
363
|
|
|
|
|
|
|
$self->render(json => {users => $users, summary => $summary}); |
|
364
|
|
|
|
|
|
|
} |
|
365
|
|
|
|
|
|
|
else { $message = "Unrecognized Profile Action!" } |
|
366
|
|
|
|
|
|
|
$self->render(json => {message => $message}); |
|
367
|
|
|
|
|
|
|
} |
|
368
|
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
# Profiles API: |
|
370
|
|
|
|
|
|
|
my $profile_action = $self->param('profile_action'); |
|
371
|
|
|
|
|
|
|
if ($profile_action) { |
|
372
|
|
|
|
|
|
|
my $message = |
|
373
|
|
|
|
|
|
|
'This request was empty, please resend with profile_action set!'; |
|
374
|
|
|
|
|
|
|
if ($profile_action eq 'startup_profiles') { |
|
375
|
|
|
|
|
|
|
$self->render( |
|
376
|
|
|
|
|
|
|
json => { |
|
377
|
|
|
|
|
|
|
profiles => [@{$startup->profiles}] |
|
378
|
|
|
|
|
|
|
} |
|
379
|
|
|
|
|
|
|
); |
|
380
|
|
|
|
|
|
|
} |
|
381
|
|
|
|
|
|
|
elsif ($profile_action eq 'select') { |
|
382
|
|
|
|
|
|
|
my $pname = $self->param('profile_name'); |
|
383
|
|
|
|
|
|
|
$self->render(json => {message => 'Please provide a profile name!'}) |
|
384
|
|
|
|
|
|
|
unless $pname; |
|
385
|
|
|
|
|
|
|
my $form = $startup->summary_profile($pname); |
|
386
|
|
|
|
|
|
|
my $lines = 0; |
|
387
|
|
|
|
|
|
|
$lines++ while ($form =~ /<[tb]r/g); |
|
388
|
|
|
|
|
|
|
my $minh = "min-height: " . ($lines * 5) . "px;"; |
|
389
|
|
|
|
|
|
|
my $message = "Selected profile: " . $pname; |
|
390
|
|
|
|
|
|
|
my $json = Mojo::JSON->new; |
|
391
|
|
|
|
|
|
|
open TMP, ">", "/tmp/json.txt"; |
|
392
|
|
|
|
|
|
|
print TMP $json->encode( |
|
393
|
|
|
|
|
|
|
{form => $form, style => $minh, message => $message}); |
|
394
|
|
|
|
|
|
|
close TMP; |
|
395
|
|
|
|
|
|
|
$self->render( |
|
396
|
|
|
|
|
|
|
text => $json->encode( |
|
397
|
|
|
|
|
|
|
{form => $form, style => $minh, message => $message} |
|
398
|
|
|
|
|
|
|
) |
|
399
|
|
|
|
|
|
|
); |
|
400
|
|
|
|
|
|
|
} |
|
401
|
|
|
|
|
|
|
else {$self->render(json => {message => "Unrecognized Profile Action!"});} |
|
402
|
|
|
|
|
|
|
$self->render(json => {message => $message}); |
|
403
|
|
|
|
|
|
|
} |
|
404
|
|
|
|
|
|
|
# General Actions API: |
|
405
|
|
|
|
|
|
|
} |
|
406
|
|
|
|
|
|
|
else { |
|
407
|
|
|
|
|
|
|
$self->render(text => "Only AJAX request are acceptexd at this route!\n"); |
|
408
|
|
|
|
|
|
|
} |
|
409
|
|
|
|
|
|
|
}); |
|
410
|
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
} |
|
412
|
|
|
|
|
|
|
1; |
|
413
|
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
__END__ |