mourningdove/cgi-bin/LJ/Event/JournalNewEntry.pm
2026-05-24 01:03:05 +00:00

520 lines
16 KiB
Perl

# This code was forked from the LiveJournal project owned and operated
# by Live Journal, Inc. The code has been modified and expanded by
# Dreamwidth Studios, LLC. These files were originally licensed under
# the terms of the license supplied by Live Journal, Inc, which can
# currently be found at:
#
# http://code.livejournal.org/trac/livejournal/browser/trunk/LICENSE-LiveJournal.txt
#
# In accordance with the original license, this code and all its
# modifications are provided under the GNU General Public License.
# A copy of that license can be found in the LICENSE file included as
# part of this distribution.
# Event that is fired when there is a new post in a journal.
# sarg1 = optional tag id to filter on
# sarg2 = optional poster id to filter on (in a community)
package LJ::Event::JournalNewEntry;
use strict;
use Scalar::Util qw(blessed);
use LJ::Entry;
use Carp qw(croak);
use base 'LJ::Event';
sub new {
my ( $class, $entry ) = @_;
croak 'Not an LJ::Entry' unless blessed $entry && $entry->isa("LJ::Entry");
return $class->SUPER::new( $entry->journal, $entry->ditemid );
}
sub arg_list {
return ("Entry ditemid");
}
sub is_common { 1 }
sub entry {
my $self = shift;
return LJ::Entry->new( $self->u, ditemid => $self->arg1 );
}
sub matches_filter {
my ( $self, $subscr ) = @_;
return 0 unless $subscr->available_for_user;
my $ditemid = $self->arg1;
my $evtju = $self->event_journal;
return 0 unless $evtju && $ditemid; # TODO: throw error?
my $entry = LJ::Entry->new( $evtju, ditemid => $ditemid );
return 0 unless $entry && $entry->valid; # TODO: throw error?
return 0 unless $entry->visible_to( $subscr->owner );
# filter by tag?
my $stagid = $subscr->arg1;
if ($stagid) {
my $usertaginfo = LJ::Tags::get_usertags( $entry->journal, { remote => $subscr->owner } );
if ($usertaginfo) {
my %tagmap = (); # tagname => tagid
while ( my ( $tagid, $taginfo ) = each %$usertaginfo ) {
$tagmap{ $taginfo->{name} } = $tagid;
}
return 0 unless grep { $tagmap{$_} == $stagid } $entry->tags;
}
}
# filter by user?
my $suserid = $subscr->arg2;
my $su = LJ::load_userid($suserid);
if ($su) {
return 0 unless $subscr->journalid && $entry->poster->equals($su);
}
# all posts by friends
return 1 if !$subscr->journalid && $subscr->owner->watches( $self->event_journal );
# a post on a specific journal
return $evtju->equals( $subscr->journal );
}
sub _can_view_content {
my ( $self, $entry, $target ) = @_;
return undef unless $entry && $entry->valid;
return undef unless $entry->visible_to($target);
return 1;
}
sub content {
my ( $self, $target ) = @_;
my $entry = $self->entry;
return undef unless $self->_can_view_content( $entry, $target );
my $entry_body = $entry->event_html(
{
# double negatives, ouch!
ljcut_disable => !$target->cut_inbox,
cuturl => $entry->url,
sandbox => 1,
preformatted => $entry->prop("opt_preformatted"),
}
) . $self->as_html_tags($target);
$entry_body = "<div class='actions_top'>" . $self->as_html_actions . "</div>" . $entry_body
if LJ::has_too_many( $entry_body, linebreaks => 10, chars => 2000 );
$entry_body .= $self->as_html_actions;
my $admin_post = "";
if ( $entry->admin_post ) {
$admin_post = '<div class="AdminPost">'
. LJ::Lang::get_default_text( "esn.journal_new_entry.admin_post",
{ img => LJ::img('admin-post') } )
. '</div>';
}
return $admin_post . $entry_body;
}
sub as_html_tags {
my ( $self, $u ) = @_;
my $tags = '';
my $url = $self->entry->journal->journal_base;
# this is retrieving the values of a hash - needs sorting
my @taglist = sort { $a cmp $b } $self->entry->tags;
# add tag info for entries that have tags
if (@taglist) {
my @htmltags = ();
push @htmltags, qq{<a href="$url/tag/$_">$_</a>} foreach @taglist;
$tags =
"<div class='entry-tags'>"
. LJ::Lang::get_default_text( 'esn.tags.short', { tags => join( ', ', @htmltags ) } )
. "</div>";
}
return $tags;
}
sub content_summary {
my ( $self, $target ) = @_;
my $entry = $self->entry;
return undef unless $self->_can_view_content( $entry, $target );
my $truncated;
my $event_summary = $entry->event_html_summary( 300, { cuturl => $entry->url }, \$truncated );
my $ret = $event_summary;
$ret .= "..." if $truncated;
$ret .= $self->as_html_actions;
return $ret;
}
sub as_string {
my $self = shift;
my $entry = $self->entry;
my $about = $entry->subject_text ? ' titled "' . $entry->subject_text . '"' : '';
my $poster = $entry->poster->user;
my $journal = $entry->journal->user;
return "$poster has posted a new entry$about at " . $entry->url
if $entry->journal->is_person;
return "$poster has posted a new entry$about in $journal at " . $entry->url;
}
sub as_html {
my ( $self, $target ) = @_;
croak "No target passed to as_html" unless LJ::isu($target);
my $journal = $self->u;
my $entry = $self->entry;
return sprintf( "(Deleted entry in %s)", $journal->ljuser_display )
unless $entry && $entry->valid;
return "(You are not authorized to view this entry)"
unless $self->entry->visible_to($target);
my $ju = LJ::ljuser($journal);
my $pu = LJ::ljuser( $entry->poster );
my $url = $entry->url;
my $about = $entry->subject_text ? ' titled "' . $entry->subject_text . '"' : '';
my $where = $journal->equals( $entry->poster ) ? "$pu" : "$pu in $ju";
return "New <a href=\"$url\">entry</a>$about by $where.";
}
sub as_html_actions {
my ($self) = @_;
my $entry = $self->entry;
my $url = $entry->url;
my $reply_url = $entry->url( mode => 'reply' );
my $ret .= "<div class='actions'>";
$ret .= " <a href='$reply_url'>Reply</a> |";
$ret .= " <a href='$url'>Link</a>";
$ret .= "</div>";
return $ret;
}
my @_ml_strings_en = (
'esn.journal_new_entry.posted_new_entry', # '[[who]] posted a new entry in [[journal]]!',
'esn.journal_new_entry.updated_their_journal', # '[[who]] updated their journal!',
'esn.hi', # 'Hi [[username]],',
'esn.journal_new_entry.about', # ' titled "[[title]]"',
'esn.tags', # 'The entry is tagged "[[tags]]"',
'esn.tags.short',
'esn.journal_new_entry.head_comm2'
, # 'There is a new entry by [[poster]][[about]][[postsecurity]] in [[journal]]![[tags]]',
'esn.journal_new_entry.head_user2'
, # '[[poster]] has posted a new entry[[about]][[postsecurity]].[[tags]]',
'esn.you_can', # 'You can:',
'esn.view_entry.nosubject', # '[[openlink]]View entry [[ditemid]][[closelink]]'
'esn.view_entry.subject', # '[[openlink]]View entry titled [[subject]][[closelink]]',
'esn.reply_to_entry', # '[[openlink]]Leave a reply to this entry[[closelink]]',
'esn.read_recent_entries', # '[[openlink]]Read the recent entries in [[journal]][[closelink]]',
'esn.join_community'
, # '[[openlink]]Join [[journal]] to read Members-only entries[[closelink]]',
'esn.read_user_entries', # '[[openlink]]Read [[poster]]\'s recent entries[[closelink]]',
'esn.add_watch' # '[[openlink]]Subscribe to [[journal]][[closelink]]',
);
sub as_email_subject {
my ( $self, $u ) = @_;
# Precache text lines
LJ::Lang::get_default_text_multi( \@_ml_strings_en );
if ( $self->entry->journal->is_comm ) {
return LJ::Lang::get_default_text(
'esn.journal_new_entry.posted_new_entry',
{
who => $self->entry->poster->display_username,
journal => $self->entry->journal->display_username,
}
);
}
else {
return LJ::Lang::get_default_text(
'esn.journal_new_entry.updated_their_journal',
{
who => $self->entry->journal->display_username,
}
);
}
}
sub _as_email {
my ( $self, $u, $is_html ) = @_;
my $username = $is_html ? $u->ljuser_display : $u->display_username;
my $poster_text = $self->entry->poster->display_username;
my $poster = $is_html ? $self->entry->poster->ljuser_display : $poster_text;
# $journal - html or plaintext version depends of $is_html
# $journal_text - text version
# $journal_user - text version, local journal user (ext_* if OpenId).
my $journal_text = $self->entry->journal->display_username;
my $journal = $is_html ? $self->entry->journal->ljuser_display : $journal_text;
my $journal_user = $self->entry->journal->user;
my $entry_url = $self->entry->url;
my $journal_url = $self->entry->journal->journal_base;
my $subject_text = $self->entry->subject_text;
# Precache text lines, using DEFAULT_LANG for $u
my $lang = $LJ::DEFAULT_LANG;
LJ::Lang::get_text_multi( $lang, undef, \@_ml_strings_en );
my $email = LJ::Lang::get_text( $lang, 'esn.hi', undef, { username => $username } ) . "\n\n";
my $about =
$subject_text
? (
LJ::Lang::get_text(
$lang, 'esn.journal_new_entry.about',
undef, { title => $self->entry->subject_text }
)
)
: '';
my $tags = '';
# add tag info for entries that have tags
if ( my @taglist = sort { $a cmp $b } $self->entry->tags ) {
$tags = ' '
. LJ::Lang::get_text( $lang, 'esn.tags', undef, { tags => join( ', ', @taglist ) } );
}
# indicate post security if it is locked or filtered
my $postsecurity = '';
if ( $self->entry->security eq 'usemask' ) {
$postsecurity = ' [locked]';
}
my $head_ml = 'esn.journal_new_entry.head_user2';
my $entry = $self->entry;
if ( $entry->journal->is_comm ) {
$head_ml = 'esn.journal_new_entry.head_comm2';
$head_ml .= '.admin_post'
if $entry->admin_post;
}
$email .= LJ::Lang::get_text(
$lang, $head_ml, undef,
{
poster => $poster,
about => $about,
journal => $journal,
tags => $tags,
}
) . "\n\n";
# make hyperlinks for options
# tags 'poster' and 'journal' cannot contain html <a> tags
# when it used between [[openlink]] and [[closelink]] tags.
my $vars = {
poster => $poster_text,
journal => $journal_text,
ditemid => $self->entry->ditemid,
subject => $subject_text,
};
my $has_subject = $subject_text ? "subject" : "nosubject";
$email .= LJ::Lang::get_text( $lang, 'esn.you_can', undef )
. $self->format_options(
$is_html, $lang, $vars,
{
"esn.view_entry.$has_subject" => [ 1, $entry_url ],
'esn.reply_to_entry' => [ 2, "$entry_url?mode=reply" ],
'esn.read_recent_entries' => [ $self->entry->journal->is_comm ? 3 : 0, $journal_url ],
'esn.join_community' => [
( $self->entry->journal->is_comm && !$u->member_of( $self->entry->journal ) )
? 4
: 0,
"$LJ::SITEROOT/circle/$journal_user/edit"
],
'esn.read_user_entries' => [ ( $self->entry->journal->is_comm ) ? 0 : 5, $journal_url ],
'esn.add_watch' => [
$u->watches( $self->entry->journal ) ? 0 : 6,
"$LJ::SITEROOT/circle/$journal_user/edit?action=subscribe"
],
}
);
return $email;
}
sub as_email_string {
my ( $self, $u ) = @_;
return unless $self->entry && $self->entry->valid;
return _as_email( $self, $u, 0 );
}
sub as_email_html {
my ( $self, $u ) = @_;
return unless $self->entry && $self->entry->valid;
return _as_email( $self, $u, 1 );
}
sub subscription_applicable {
my ( $class, $subscr ) = @_;
return 1 unless $subscr->arg1;
# subscription is for entries with tags.
# not applicable if user has no tags
my $journal = $subscr->journal;
return 1 unless $journal; # ?
my $usertags = LJ::Tags::get_usertags($journal);
if ( $usertags && ( scalar keys %$usertags ) ) {
my @unsub = $class->unsubscribed_tags($subscr);
return ( scalar @unsub ) ? 1 : 0;
}
return 0;
}
# returns list of (hashref of (tagid => name))
sub unsubscribed_tags {
my ( $class, $subscr ) = @_;
my $journal = $subscr->journal;
return () unless $journal;
my $usertags = LJ::Tags::get_usertags( $journal, { remote => $subscr->owner } );
return () unless $usertags;
my @tagids = sort { $usertags->{$a}->{name} cmp $usertags->{$b}->{name} } keys %$usertags;
return grep { $_ } map {
$subscr->owner->has_subscription(
etypeid => $class->etypeid,
arg1 => $_,
journal => $journal
) ? undef : { $_ => $usertags->{$_}->{name} };
} @tagids;
}
sub subscription_as_html {
my ( $class, $subscr ) = @_;
my $journal = $subscr->journal;
# are we filtering on a tag?
my $arg1 = $subscr->arg1;
my $usertags;
if ( $arg1 eq '?' ) {
my @unsub_tags = $class->unsubscribed_tags($subscr);
my %entry_tags = $subscr->entry ? map { $_ => 1 } $subscr->entry->tags : ();
my @entrytagdropdown;
my @fulltagdropdown;
foreach my $unsub_tag (@unsub_tags) {
while ( my ( $tagid, $name ) = each %$unsub_tag ) {
if ( $entry_tags{$name} ) {
push @entrytagdropdown, { value => $tagid, text => $name };
}
else {
push @fulltagdropdown, { value => $tagid, text => $name };
}
}
}
my @tagdropdown;
if (@entrytagdropdown) {
@tagdropdown = (
{
optgroup => LJ::Lang::ml('event.journal_new_entry.taglist.entry'),
items => \@entrytagdropdown
},
{
optgroup => LJ::Lang::ml('event.journal_new_entry.taglist.full'),
items => \@fulltagdropdown
},
);
}
else {
@tagdropdown = @fulltagdropdown;
}
$usertags = LJ::html_select(
{
name => $subscr->freeze('arg1'),
},
@tagdropdown
);
}
elsif ($arg1) {
$usertags =
LJ::Tags::get_usertags( $journal, { remote => $subscr->owner } )->{$arg1}->{'name'};
}
if ($arg1) {
return BML::ml(
'event.journal_new_entry.tag.' . ( $journal->is_comm ? 'community' : 'user' ),
{
user => $journal->ljuser_display,
tags => $usertags,
}
);
}
# are we filtering on a poster?
my $arg2 = $subscr->arg2;
if ($arg2) {
my $postu = LJ::load_userid($arg2);
if ($postu) {
return BML::ml(
'event.journal_new_entry.poster',
{
user => $journal->ljuser_display,
poster => $postu->ljuser_display,
}
);
}
}
return BML::ml('event.journal_new_entry.friendlist') unless $journal;
return BML::ml(
'event.journal_new_entry.' . ( $journal->is_comm ? 'community' : 'user' ),
{
user => $journal->ljuser_display,
}
);
}
# when was this entry made?
sub eventtime_unix {
my $self = shift;
my $entry = $self->entry;
return $entry ? $entry->logtime_unix : $self->SUPER::eventtime_unix;
}
sub zero_journalid_subs_means { undef }
1;