/
File.pm
255 lines (174 loc) · 5.78 KB
/
File.pm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
package Convos::Core::User::File;
use Mojo::Base -base;
use Carp qw(confess);
use Convos::Util qw(DEBUG short_checksum);
use Digest::MD5 ();
use Mojo::Asset::File;
use Mojo::File;
use Mojo::JSON qw(false true);
use Mojo::Path;
use Mojo::Util qw(encode);
use Time::HiRes qw(time);
has asset => sub { Mojo::Asset::File->new };
has filename => sub { die 'filename() cannot be built' };
has id => sub {
my $self = shift;
my $asset = $self->asset;
confess "Cannot create id() from empty file" unless $asset->path and -s $asset->path;
return short_checksum(Digest::MD5->new->add($asset->slurp)->hexdigest);
};
has path => sub {
my $self = shift;
my @uri = split '/', $self->uri;
$uri[-1] =~ s!\.json$!.data!;
return $self->user->core->home->child(@uri);
};
has saved => sub { Mojo::Date->new->to_datetime };
has types => sub { state $types = Mojolicious::Types->new->type(xhtml => 'application/xhtml+xml') };
has user => undef;
has write_only => sub {false};
sub handle_message_to_paste_p {
my ($class, $backend, $connection, $message) = @_;
my $self = $class->new(user => $connection->user);
my $filename = $message =~ m!(\w.{4,})!m ? lc substr $1, 0, 28 : 'paste';
$filename =~ s![^A-Za-z-]+!_!g;
$filename = 'paste' if 5 > length $filename;
$self->filename("$filename.txt");
$self->asset->add_chunk(encode 'UTF-8', $message);
return $self->save_p;
}
sub load_p {
my $self = shift;
return $self->user->core->backend->load_object_p($self)->then(sub {
my $attrs = shift;
return $self->_parse_attrs($attrs) if $attrs->{id};
# back compat
$self->{uri} = Mojo::Path->new(join '/', $self->user->email, 'upload', $self->id);
return $self->user->core->backend->load_object_p($self)
->then(sub { $self->_move_legacy_p(shift) });
});
}
sub mime_type {
my $self = shift;
my $type = $self->types->type($self->_ext) || 'application/octet-stream';
return $type eq 'application/octet-stream' && -T $self->path ? 'text/plain' : $type;
}
sub public_url {
my $self = shift;
my $id = $self->id;
$id .= '.' . $self->_ext if shift;
return $self->user->core->web_url(join '/', 'file', $self->user->uid, $id)->to_abs;
}
sub save_p {
my $self = shift;
my $asset = $self->asset;
my $core = $self->user->core;
if ($asset->cleanup) {
my $dir = $self->path->dirname;
$dir->make_path unless -d $dir;
$asset->move_to($self->path);
warn "[@{[$self->id]}] Moved asset to @{[$self->path]}\n" if DEBUG;
}
return $core->backend->save_object_p($self);
}
sub to_message {
shift->public_url->to_string;
}
sub uri {
my $self = shift;
return delete $self->{uri} if $self->{uri}; # back compat
return Mojo::Path->new(join '/', $self->user->email, 'upload', $self->id . '.json');
}
sub _ext { shift->filename =~ m!\.(\w+)$! ? $1 : 'bin' }
sub _move_legacy_p {
my ($self, $attrs) = @_;
return $self unless defined $attrs->{content};
$self->asset->add_chunk($attrs->{content});
$self->filename('paste.txt');
$self->saved(Mojo::Date->new($attrs->{created_at})->to_datetime);
return $self->save_p->then(sub {
$self->{uri} = Mojo::Path->new(join '/', $self->user->email, 'upload', $self->id);
return $self->user->core->backend->delete_object_p($self);
});
}
sub _parse_attrs {
my ($self, $attrs) = @_;
$self->$_($attrs->{$_} // '') for qw(filename id saved write_only);
$self->asset->path($self->path) if $attrs->{id};
return $self;
}
sub TO_JSON {
my ($self, $persist) = @_;
my $json = {
ext => $self->_ext,
id => $self->id,
filename => $self->filename,
saved => $self->saved,
uid => '' . $self->user->uid, # force to string
write_only => $self->write_only,
};
$json->{url} = $self->public_url->to_string unless $persist;
$json->{author} = $self->user->email if $persist;
return $json;
}
1;
=encoding utf8
=head1 NAME
Convos::Core::User::File - Represents a file for a user
=head1 DESCRIPTION
L<Convos::Core::User::File> is a class used by represent an uploaded file.
=head1 ATTRIBUTES
=head2 asset
$str = $file->asset;
Holds a L<Mojo::Asset::File> object.
=head2 filename
$str = $file->filename;
$file = $file->filename($str);
Holds the original filename.
=head2 id
$str = $file->id;
Returns an ID for the file that will be used publically in the generated URL.
=head2 path
$path = $file->path;
Returns a L<Mojo::File> object for where the asset should be on disk.
=head2 saved
$dt = $file->saved;
Holds a date-tim string for when the file was saved.
=head2 types
$types = $file->types;
Holds a L<Mojolicious::Types> object, used by L</mime_type>.
=head2 user
$user = $file->user;
Holds a L<Convos::Core::User> object.
=head2 write_only
$bool = $file->write_only;
$file = $file->write_only(true);
Used to write files that should only be used internally by L<Convos>, instead
of read by visitors on the web.
=head1 METHODS
=head2 handle_message_to_paste_p
$p = Convos::Core::User::File
->handle_message_to_paste_p($backend, $connection, $message)
->then(sub { my $file = shift });
This method will be called when a L<Convos::Core::Connection> wants to create a
paste.
=head2 load_p
$p = $file->load_p->then(sub { my $file = shift });
Used to load meta information from disk.
=head2 mime_type
$str = $file->mime_type;
Used to get the mime type of this file. Defaults to "application/octet-stream".
=head2 public_url
$path = $file->public_url;
Returns a L<Mojo::Path> object useful for making a public URL.
=head2 save_p
$p = $file->save_p->then(sub { my $file = shift });
=head2 to_message
$str = $file->to_message;
Converts this objcet into a message you can send to a channel or user.
=head2 uri
$path = $file->uri;
Returns a L<Mojo::Path> object representing the file on disk.
=head1 SEE ALSO
L<Convos>.
=cut