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__ |