#!/usr/bin/perl # # DW::Controller::Manage::Tracking # # converted /manage/tracking pages # # Authors: # Jen Griffin # # Copyright (c) 2023 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::Controller::Manage::Tracking; use strict; use warnings; use Carp qw(confess); use DW::Controller; use DW::Routing; use DW::Template; use LJ::Subscription; use LJ::Entry; use LJ::Comment; our $ml_scope = "/tracking/manage.tt"; DW::Routing->register_string( "/manage/tracking/comments", \&comments_handler, app => 1 ); DW::Routing->register_string( "/manage/tracking/entry", \&entry_handler, app => 1 ); DW::Routing->register_string( "/manage/tracking/user", \&user_handler, app => 1 ); sub _tracking_controller { return ( 0, error_ml("$ml_scope.error.disabled") ) unless LJ::is_enabled('esn'); my ( $ok, $rv ) = controller( anonymous => 0 ); return ( 0, $rv ) unless $ok; my $r = $rv->{r}; my $get = $r->get_args; my $post = $r->post_args; my $journalname = $post->{journal} || $get->{journal}; return ( 0, error_ml("$ml_scope.error.nojournal") ) unless $journalname; my $journal = LJ::load_user($journalname); return ( 0, error_ml( "$ml_scope.error.invalidjournal", { journal => $journalname } ) ) unless $journal; $rv->{journal} = $journal; return ( 1, $rv ); } sub _validate_referer { my $referer = DW::Request->get->header_in("Referer"); $referer = $LJ::SITEROOT . $referer if $referer && $referer =~ '^/'; return undef unless DW::Controller::validate_redirect_url($referer); my ( $url, $args ) = ( $referer =~ /^(.*)\?(.*)$/ ); return $referer unless $url && $args; # validate args my %args = map { split( /=/, $_ ) } split( /&/, $args ); $args = LJ::viewing_style_args(%args); return $args ? "$url?$args" : $url; } sub _page_template { my ($rv) = @_; my $vars = { ret_url => $rv->{ret_url}, subscribe_interface => LJ::subscribe_interface( $rv->{remote}, journal => $rv->{journal}, categories => $rv->{categories}, default_selected_notifications => $rv->{default_selected}, ) }; # print cancel button? if ( my $r = DW::Request->get ) { my $referer = { $r->headers_in }->{'Referer'} || ''; my $uri = $LJ::SITEROOT . $r->uri; # normalize the URLs -- ../index.tt doesn't make it a different page. $uri =~ s/index\.bml//; $referer =~ s/index\.bml//; $vars->{referer} = $referer; $vars->{do_refer} = $referer && $referer ne $uri; } return DW::Template->render_template( 'tracking/manage.tt', $vars ); } sub comments_handler { my ( $ok, $rv ) = _tracking_controller(); return $rv unless $ok; my $r = $rv->{r}; my $get = $r->get_args; my $post = $r->post_args; my $ditemid = $post->{itemid} || $get->{itemid} || $post->{ditemid} || $get->{ditemid}; my $dtalkid = $post->{talkid} || $get->{talkid} || $post->{dtalkid} || $get->{dtalkid}; return error_ml("$ml_scope.error.talkid") unless $dtalkid && $dtalkid =~ /^\d+$/; my $remote = $rv->{remote}; my $journal = $rv->{journal}; my $comment = LJ::Comment->new( $journal, dtalkid => $dtalkid ); return error_ml("$ml_scope.error.invalidcomment") unless $comment && $comment->visible_to($remote); return error_ml("$ml_scope.error.nocomment") if $comment->is_deleted; my $entry = $comment->entry; $ditemid = undef unless $ditemid =~ /^\d+$/; $ditemid ||= $entry->ditemid; # build the list of notification classes to display on this page my $build = sub { my ( $event, %opts ) = @_; confess "No event defined" unless $event; $opts{event} = $event; $opts{journal} = $journal; $opts{flags} = LJ::Subscription::TRACKING; return LJ::Subscription::Pending->new( $remote, %opts ); }; my $cat_title = 'Track Comments'; my @notifs; my $thread_sub = $build->( "JournalNewComment", arg2 => $comment && $comment->jtalkid, default_selected => 1, ); # $thread_sub will be disabled by subscribe_interface if it's not available; # but the availability also affects other form fields on the page below my $can_watch = $thread_sub->available_for_user; push @notifs, $thread_sub; push @notifs, $build->( "JournalNewComment", arg1 => $ditemid, default_selected => $can_watch ? 0 : 1, # only if they can't watch the subthread above ); push @notifs, $build->("JournalNewComment") if $remote->can_track_all_community_comments($journal); $rv->{categories} = [ { $cat_title => \@notifs } ]; $rv->{default_selected} = ['LJ::NotificationMethod::Email']; my $referer = $r->header_in("Referer") // ''; my ($args) = ( $referer =~ /\?(.*)$/ ); my %style_args = map { split( /=/, $_ ) } split( /&/, ( $args // '' ) ); $rv->{ret_url} = $can_watch ? $comment->url( LJ::viewing_style_args(%style_args) ) : $entry->url( style_opts => LJ::viewing_style_opts(%style_args) ); return _page_template($rv); } sub entry_handler { my ( $ok, $rv ) = _tracking_controller(); return $rv unless $ok; my $r = $rv->{r}; my $get = $r->get_args; my $post = $r->post_args; my $ditemid = $post->{itemid} || $get->{itemid} || $post->{ditemid} || $get->{ditemid}; return error_ml("$ml_scope.error.noentry") unless $ditemid && $ditemid =~ /^\d+$/; my $remote = $rv->{remote}; my $journal = $rv->{journal}; my $entry = LJ::Entry->new( $journal, ditemid => $ditemid ); return error_ml("$ml_scope.error.invalidentry") unless $entry && $entry->valid; return error_ml("$ml_scope.error.hiddenentry") unless $entry->visible_to($remote); # build the list of notification classes to display on this page my $build = sub { my ( $event, %opts ) = @_; confess "No event defined" unless $event; $opts{event} = $event; $opts{journal} = $journal; $opts{flags} = LJ::Subscription::TRACKING; return LJ::Subscription::Pending->new( $remote, %opts ); }; my $entry_cat_title = 'Track Entry'; my $journal_cat_title = 'Track Journal'; my @e_notifs = ( $build->( "JournalNewComment", arg1 => $ditemid, default_selected => 1 ), $build->( "JournalNewComment::TopLevel", arg1 => $ditemid, default_selected => 0 ), ); my @j_notifs; # all comments in a community push @j_notifs, $build->("JournalNewComment") if $remote->can_track_all_community_comments($journal); push @j_notifs, $build->("JournalNewEntry"); # passing arg1 => '?' here is a magic invocation for tracking by entry tag push @j_notifs, $build->( "JournalNewEntry", arg1 => '?', entry => $entry ); # new community entries by a specific poster push @j_notifs, $build->( "JournalNewEntry", arg2 => $entry->posterid ) if $journal->is_community; $rv->{categories} = [ { $entry_cat_title => \@e_notifs }, { $journal_cat_title => \@j_notifs } ]; $rv->{default_selected} = ['LJ::NotificationMethod::Email']; my $referer = $r->header_in("Referer") // ''; my ($args) = ( $referer =~ /\?(.*)$/ ); my %style_args = map { split( /=/, $_ ) } split( /&/, ( $args // '' ) ); $rv->{ret_url} = $entry->url( style_opts => LJ::viewing_style_opts(%style_args) ); return _page_template($rv); } sub user_handler { my ( $ok, $rv ) = _tracking_controller(); return $rv unless $ok; my $remote = $rv->{remote}; my $journal = $rv->{journal}; my $r = $rv->{r}; return $r->redirect("$LJ::SITEROOT/manage/settings/?cat=notifications") if $remote->equals($journal); # build the list of notification classes to display on this page my $build = sub { my ( $event, $disabled, $arg1 ) = @_; confess "No event defined" unless $event; return LJ::Subscription::Pending->new( $remote, journal => $journal, flags => LJ::Subscription::TRACKING, event => $event, arg1 => $arg1, disabled => $disabled, ); }; my $cat_title = 'Track User'; my @notifs; # passing arg1 => '?' here is a magic invocation for tracking by entry tag push @notifs, $build->( "JournalNewEntry", undef, '?' ) if $journal->is_visible; push @notifs, $build->("JournalNewEntry") if $journal->is_visible; push @notifs, $build->("UserExpunged") unless LJ::User->is_protected_username( $journal->user ); push @notifs, $build->("JournalNewComment") if $remote->can_track_all_community_comments($journal) && $journal->is_visible; push @notifs, $build->( "NewUserpic", !$remote->can_track_new_userpic ) if $journal->is_visible; push @notifs, $build->("Birthday") if $journal->is_visible; $rv->{categories} = [ { $cat_title => \@notifs } ]; $rv->{ret_url} = _validate_referer(); return _page_template($rv); } 1;