mourningdove/cgi-bin/DW/Media/Base.pm

218 lines
6.4 KiB
Perl
Raw Permalink Normal View History

2026-05-24 01:03:05 +00:00
#!/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;