#!/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 # # 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;