#!/usr/bin/perl # # DW::User::Edges::CommMembership # # Implements community membership edges. # # Authors: # Mark Smith # # Copyright (c) 2008 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::User::Edges::CommMembership; use strict; use Carp qw/ confess /; use DW::User::Edges; # membership edges are for someone who is a member of a community DW::User::Edges::define_edge( member => { type => 'hashref', db_edge => 'E', options => { moderated_add => { required => 0, type => 'bool', default => 0 }, }, add_sub => \&_add_m_edge, del_sub => \&_del_m_edge, } ); # internal method used to add a membership edge to an account sub _add_m_edge { my ( $from_u, $to_u, $edges ) = @_; # bail unless there is a membership edge; note that we have to remove # the edge, as per the Edges specification. (if we don't, we will get # called over and over...) my $member_edge = delete $edges->{member} or return; # error check adding an edge return 0 unless $from_u->can_join( $to_u, moderated_add => $member_edge->{moderated_add} ? 1 : 0 ); # simply add the reluser edge my $rv = LJ::set_rel( $to_u, $from_u, 'E' ); # delete memcache key for community reading pages LJ::memcache_kill( $to_u, 'c_wt_list' ); # success? return $rv; } # internal method to delete an edge sub _del_m_edge { my ( $from_u, $to_u, $edges ) = @_; $from_u = LJ::want_user($from_u) or return 0; $to_u = LJ::want_user($to_u) or return 0; # same logic as in _add_m_edge delete $edges->{member} or return; # now remove it; note we don't do any extraneous checking. if the user # wants to remove an edge that doesn't exist? more power to them. LJ::clear_rel( $to_u, $from_u, 'E' ); # delete memcache key for community reading pages LJ::memcache_kill( $to_u, 'c_wt_list' ); # success! return 1; } ############################################################################### # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ############################################################################### # push methods up into the DW::User namespace package DW::User; use strict; use Carp qw/ confess /; # returns 1 if the given user is a member of the community # returns 0 otherwise sub member_of { my ( $from_u, $to_u ) = @_; $from_u = LJ::want_user($from_u) or return 0; $to_u = LJ::want_user($to_u) or return 0; # individual->comm return 0 unless $from_u->is_individual && $to_u->is_community; # check it return 1 if LJ::check_rel( $to_u, $from_u, 'E' ); return 0; } *LJ::User::member_of = \&member_of; # returns array of userids we're member of sub member_of_userids { my ( $u, %args ) = @_; $u = LJ::want_user($u) or return (); return () unless $u->is_individual; return @{ LJ::load_rel_target_cache( $u, 'E' ) || [] }; } *LJ::User::member_of_userids = \&member_of_userids; # returns array of userids that are our members sub member_userids { my ( $u, %args ) = @_; $u = LJ::want_user($u) or return (); return () unless $u->is_community; return @{ LJ::load_rel_user_cache( $u, 'E' ) || [] }; } *LJ::User::member_userids = \&member_userids; # returns 1/0 depending on if the source is allowed to add a member edge # to the target. note: if you don't pass a target user, then we return # a generic 1/0 meaning "this account is allowed to have a member edge". sub can_join { my ( $u, $tu, %opts ) = @_; $u = LJ::want_user($u) or confess 'invalid user object'; $tu = LJ::want_user($tu); my $errref = $opts{errref}; my $membership_ref = $opts{membership_ref}; my $moderated_add = $opts{moderated_add} ? 1 : 0; # if the user is a maintainer, skip every other check return 1 if $tu && $u->can_manage($tu); # the user must be a personal account or identity account unless ( $u->is_individual ) { $$errref = LJ::Lang::ml('edges.join.error.usernotindividual'); return 0; } # the user must be visible unless ( $u->is_visible ) { $$errref = LJ::Lang::ml('edges.join.error.usernotvisible'); return 0; } if ($tu) { # the target must be a community unless ( $tu->is_community ) { $$errref = LJ::Lang::ml('edges.join.error.targetnotcommunity'); return 0; } # the target must be visible unless ( $tu->is_visible ) { $$errref = LJ::Lang::ml('edges.join.error.targetnotvisible'); return 0; } # the target must not have banned the user if ( $tu->has_banned($u) ) { $$errref = LJ::Lang::ml('edges.join.error.targetbanneduser'); return 0; } # make sure the user isn't underage and trying to join an adult community my $adult_content; unless ( $u->can_join_adult_comm( comm => $tu, adultref => \$adult_content ) ) { if ( $adult_content eq "explicit" ) { $$errref = LJ::Lang::ml('edges.join.error.userunderage'); } unless ( $u->best_guess_age ) { $$errref .= " " . LJ::Lang::ml( 'edges.join.error.setage', { url => LJ::create_url("/manage/profile/") } ); } return 0; } # the community must be open membership or we must be adding to a moderated community unless ( $tu->is_open_membership || $opts{moderated_add} ) { $$errref = LJ::Lang::ml('edges.join.error.targetnotopen'); $$membership_ref = 1; return 0; } } # okay, good to go! return 1; } *LJ::User::can_join = \&can_join; # returns 1/0 depending on if the source is allowed to remove a member edge # from the target. note: if you don't pass a target user, then we return # a generic 1/0 meaning "this account is allowed to not have a member edge". sub can_leave { my ( $u, $tu, %opts ) = @_; $u = LJ::want_user($u) or confess 'invalid user object'; $tu = LJ::want_user($tu); my $errref = $opts{errref}; # if the user is the last maintainer, they can't leave if ($tu) { my @maintids = $tu->maintainer_userids; my $ismaint = grep { $_ == $u->id } @maintids; my $othermaints = grep { $_ != $u->id } @maintids; if ( $ismaint && !$othermaints ) { if ( $tu->is_deleted ) { # one exception: maintainer can remove themselves from a deleted community return 1; } else { $$errref = LJ::Lang::ml( 'edges.leave.error.lastmaintainer2', { url => $tu->community_manage_members_url } ); return 0; } } } # okay, good to go! return 1; } *LJ::User::can_leave = \&can_leave; 1;