218 lines
6.4 KiB
Perl
218 lines
6.4 KiB
Perl
|
|
#!/usr/bin/perl
|
||
|
|
#
|
||
|
|
# DW::Media::Base
|
||
|
|
#
|
||
|
|
# This is the base module to represent media items. You should never instantiate
|
||
|
|
# this class directly...
|
||
|
|
#
|
||
|
|
# Authors:
|
||
|
|
# Mark Smith <mark@dreamwidth.org>
|
||
|
|
#
|
||
|
|
# Copyright (c) 2010-2013 by Dreamwidth Studios, LLC.
|
||
|
|
#
|
||
|
|
# This program is free software; you may redistribute it and/or modify it under
|
||
|
|
# the same terms as Perl itself. For a copy of the license, please reference
|
||
|
|
# 'perldoc perlartistic' or 'perldoc perlgpl'.
|
||
|
|
#
|
||
|
|
|
||
|
|
package DW::Media::Base;
|
||
|
|
|
||
|
|
use strict;
|
||
|
|
use Carp qw/ croak confess /;
|
||
|
|
|
||
|
|
use DW::BlobStore;
|
||
|
|
|
||
|
|
sub new_from_row {
|
||
|
|
croak "Children must override this method.";
|
||
|
|
}
|
||
|
|
|
||
|
|
# accessors for our internal data
|
||
|
|
sub u { LJ::load_userid( $_[0]->{userid} ) }
|
||
|
|
sub userid { $_[0]->{userid} }
|
||
|
|
sub id { $_[0]->{mediaid} }
|
||
|
|
sub anum { $_[0]->{anum} }
|
||
|
|
sub displayid { $_[0]->{displayid} }
|
||
|
|
sub state { $_[0]->{state} }
|
||
|
|
sub mediatype { $_[0]->{mediatype} }
|
||
|
|
sub security { $_[0]->{security} }
|
||
|
|
sub allowmask { $_[0]->{allowmask} }
|
||
|
|
sub logtime { $_[0]->{logtime} }
|
||
|
|
sub mimetype { $_[0]->{mimetype} }
|
||
|
|
sub mogkey { "media:$_[0]->{userid}:$_[0]->{versionid}" }
|
||
|
|
sub ext { $_[0]->{ext} }
|
||
|
|
|
||
|
|
# These change depending on the version we're showing.
|
||
|
|
sub versionid { $_[0]->{versionid} }
|
||
|
|
sub size { $_[0]->{filesize} }
|
||
|
|
sub width { $_[0]->{width} }
|
||
|
|
sub height { $_[0]->{height} }
|
||
|
|
|
||
|
|
# helper state subs
|
||
|
|
sub is_active { $_[0]->{state} eq 'A' }
|
||
|
|
sub is_deleted { $_[0]->{state} eq 'D' }
|
||
|
|
|
||
|
|
# Property method, loads our properties and fetches one when called, also
|
||
|
|
# handles updating and deleting them.
|
||
|
|
sub prop {
|
||
|
|
my ( $self, $prop, $val ) = @_;
|
||
|
|
|
||
|
|
my $u = $self->u;
|
||
|
|
my $pobj = LJ::get_prop( media => $prop )
|
||
|
|
or confess 'Attempted to get/set invalid media property';
|
||
|
|
my $propid = $pobj->{id};
|
||
|
|
|
||
|
|
unless ( $self->{_loaded_props} ) {
|
||
|
|
my $props = $u->selectall_hashref(
|
||
|
|
q{SELECT propid, value FROM media_props WHERE userid = ? AND mediaid = ?},
|
||
|
|
'propid', undef, $self->{userid}, $self->{mediaid} );
|
||
|
|
confess $u->errstr if $u->err;
|
||
|
|
|
||
|
|
$self->{_props} = { map { $_->{propid} => $_->{value} } values %$props };
|
||
|
|
$self->{_loaded_props} = 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
# Getting an argument if they didn't provide a third. If they did, however,
|
||
|
|
# then this fails and we go into the set logic.
|
||
|
|
return $self->{_props}->{$propid} if scalar @_ == 2;
|
||
|
|
|
||
|
|
# Setting logic. Delete vs update.
|
||
|
|
if ( defined $val ) {
|
||
|
|
$u->do(
|
||
|
|
q{REPLACE INTO media_props (userid, mediaid, propid, value)
|
||
|
|
VALUES (?, ?, ?, ?)},
|
||
|
|
undef, $self->{userid}, $self->{mediaid}, $propid, $val
|
||
|
|
);
|
||
|
|
confess $u->errstr if $u->err;
|
||
|
|
|
||
|
|
return $self->{_props}->{$propid} = $val;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
$u->do(
|
||
|
|
q{DELETE FROM media_props WHERE userid = ? AND mediaid = ?
|
||
|
|
AND propid = ?},
|
||
|
|
undef, $self->{userid}, $self->{mediaid}, $propid
|
||
|
|
);
|
||
|
|
confess $u->errstr if $u->err;
|
||
|
|
|
||
|
|
delete $self->{_props}->{$propid};
|
||
|
|
return undef;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# construct a URL for this resource
|
||
|
|
sub url {
|
||
|
|
my ( $self, %opts ) = @_;
|
||
|
|
|
||
|
|
# If we're using a version (versionid defined) then we want to insert the
|
||
|
|
# width and height to the URL.
|
||
|
|
my $extra = $opts{extra} // '';
|
||
|
|
if ( $self->{mediaid} != $self->{versionid} ) {
|
||
|
|
$extra .= ( $self->{url_width} // $self->{width} ) . 'x'
|
||
|
|
. ( $self->{url_height} // $self->{height} ) . '/';
|
||
|
|
}
|
||
|
|
|
||
|
|
return $self->u->journal_base . '/file/' . $extra . $self->{displayid} . '.' . $self->{ext};
|
||
|
|
}
|
||
|
|
|
||
|
|
# construct a URL for the fullsize version of this url. This is the same as
|
||
|
|
# url if the object is the orignal, fullsize version, but returns the url of
|
||
|
|
# the original if we're using version of it.
|
||
|
|
sub full_url {
|
||
|
|
my $self = $_[0];
|
||
|
|
|
||
|
|
return $self->u->journal_base . '/file/' . $self->{displayid} . '.' . $self->{ext};
|
||
|
|
}
|
||
|
|
|
||
|
|
# if user can see this
|
||
|
|
sub visible_to {
|
||
|
|
my ( $self, $other_u ) = @_;
|
||
|
|
|
||
|
|
# test that the user that owns this item is still visible, that we're still active,
|
||
|
|
# and return a true if we're public.
|
||
|
|
my $u = $self->u;
|
||
|
|
return 0 unless $self->is_active && $u->is_visible;
|
||
|
|
return 1 if $self->security eq 'public';
|
||
|
|
|
||
|
|
# at this point, if we don't have a remote user, fail
|
||
|
|
return 0 unless LJ::isu($other_u);
|
||
|
|
|
||
|
|
# private check. if it's us or an admin, allow, else fail.
|
||
|
|
my %headers = defined DW::Request->get ? DW::Request->get->headers_in : ();
|
||
|
|
my $refer = $headers{Referer};
|
||
|
|
my $is_sitepage = ( defined $refer && $refer eq "$LJ::SITEROOT/" ) ? 1 : 0;
|
||
|
|
return 1 if $is_sitepage && $other_u->has_priv( 'canview', 'images' );
|
||
|
|
|
||
|
|
return 1 if $u->equals($other_u);
|
||
|
|
return 0 if $self->security eq 'private';
|
||
|
|
|
||
|
|
# simple usemask checking...
|
||
|
|
if ( $self->security eq 'usemask' ) {
|
||
|
|
my $gmask = $u->trustmask($other_u);
|
||
|
|
|
||
|
|
my $allowed = int $gmask & int $self->allowmask;
|
||
|
|
return $allowed ? 1 : 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
# totally failed.
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
# we delete the actual file
|
||
|
|
# but we keep the metadata around for record-keeping purpose
|
||
|
|
# returns 1/0 on success or failure
|
||
|
|
sub delete {
|
||
|
|
my $self = $_[0];
|
||
|
|
return 0 if $self->is_deleted;
|
||
|
|
|
||
|
|
my $u = $self->u
|
||
|
|
or croak 'Sorry, unable to load the user.';
|
||
|
|
|
||
|
|
$u->do( q{UPDATE media SET state = 'D' WHERE userid = ? AND mediaid = ?},
|
||
|
|
undef, $u->id, $self->id );
|
||
|
|
confess $u->errstr if $u->err;
|
||
|
|
|
||
|
|
$self->{state} = 'D';
|
||
|
|
|
||
|
|
DW::BlobStore->delete( media => $self->mogkey );
|
||
|
|
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
# change the security of this item. returns 0/1 for successfulness.
|
||
|
|
sub set_security {
|
||
|
|
my ( $self, %opts ) = @_;
|
||
|
|
return 0 if $self->is_deleted;
|
||
|
|
|
||
|
|
my $security = $opts{security};
|
||
|
|
confess 'Invalid argument hash passed to set_security.'
|
||
|
|
unless defined $security;
|
||
|
|
|
||
|
|
my $mask = 0;
|
||
|
|
if ( $security eq 'usemask' ) {
|
||
|
|
|
||
|
|
# default allowmask of 0 unless defined otherwise
|
||
|
|
$opts{allowmask} //= 0;
|
||
|
|
$mask = int $opts{allowmask};
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( $security =~ /^(?:friends|access)$/ ) {
|
||
|
|
$security = 'usemask';
|
||
|
|
$mask = 1;
|
||
|
|
}
|
||
|
|
confess 'Invalid security type passed to set_security.'
|
||
|
|
unless $security =~ /^(?:private|public|usemask)$/;
|
||
|
|
|
||
|
|
my $u = $self->u
|
||
|
|
or croak 'Sorry, unable to load the user.';
|
||
|
|
$u->do( q{UPDATE media SET security = ?, allowmask = ? WHERE userid = ? AND mediaid = ?},
|
||
|
|
undef, $security, $mask, $u->id, $self->id );
|
||
|
|
confess $u->errstr if $u->err;
|
||
|
|
|
||
|
|
$self->{security} = $security;
|
||
|
|
$self->{allowmask} = $mask;
|
||
|
|
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
1;
|