#!/usr/bin/perl # # # lib: cgi-bin/ljlib.pl, cgi-bin/ljconfig.pl, cgi-bin/ljlang.pl, cgi-bin/cleanhtml.pl # use strict; package LJ::S1; use vars qw(@themecoltypes); eval "use LJR::Distributed;"; my $ljr = $@ ? 0 : 1; if ($ljr) { use LJR::Distributed; } # this used to be in a table, but that was kinda useless @themecoltypes = ( [ 'page_back', 'Page background' ], [ 'page_text', 'Page text' ], [ 'page_link', 'Page link' ], [ 'page_vlink', 'Page visited link' ], [ 'page_alink', 'Page active link' ], [ 'page_text_em', 'Page emphasized text' ], [ 'page_text_title', 'Page title' ], [ 'weak_back', 'Weak accent' ], [ 'weak_text', 'Text on weak accent' ], [ 'strong_back', 'Strong accent' ], [ 'strong_text', 'Text on strong accent' ], [ 'stronger_back', 'Stronger accent' ], [ 'stronger_text', 'Text on stronger accent' ], ); # updated everytime new S1 style cleaning rules are added, # so cached cleaned versions are invalidated. $LJ::S1::CLEANER_VERSION = 5; # PROPERTY Flags: # /a/: # safe in styles as sole attributes, without any cleaning. for # example: is okay, # if we're in # LASTN_TALK_READLINK, because the system generates # %%urlread%%. # by default, if we don't declare things trusted here, # we'll # double-check all attributes at the end for potential XSS # # problems. # # /u/: # is a URL. implies /a/. # # # /d/: # is a number. implies /a/. # # /t/: # tainted! User controls via other some other variable. # # /s/: # some system string... probably safe. but maybe possible to coerce it # alongside something else. my $commonprop = { 'dateformat' => { 'yy' => 'd', 'yyyy' => 'd', 'm' => 'd', 'mm' => 'd', 'd' => 'd', 'dd' => 'd', 'min' => 'd', '12h' => 'd', '12hh' => 'd', '24h' => 'd', '24hh' => 'd', }, 'talklinks' => { 'messagecount' => 'd', 'urlread' => 'u', 'urlpost' => 'u', 'itemid' => 'd', }, 'talkreadlink' => { 'messagecount' => 'd', 'urlread' => 'u', }, 'event' => { 'itemid' => 'd', }, 'pic' => { 'src' => 'u', 'width' => 'd', 'height' => 'd', }, 'newday' => { yy => 'd', yyyy => 'd', m => 'd', mm => 'd', d => 'd', dd => 'd', }, 'skip' => { 'numitems' => 'd', 'url' => 'u', }, }; $LJ::S1::PROPS = { 'CALENDAR_DAY' => { 'd' => 'd', 'eventcount' => 'd', 'dayevent' => 't', 'daynoevent' => 't', }, 'CALENDAR_DAY_EVENT' => { 'eventcount' => 'd', 'dayurl' => 'u', }, 'CALENDAR_DAY_NOEVENT' => { }, 'CALENDAR_EMPTY_DAYS' => { 'numempty' => 'd', }, 'CALENDAR_MONTH' => { 'monlong' => 's', 'monshort' => 's', 'yy' => 'd', 'yyyy' => 'd', 'weeks' => 't', 'urlmonthview' => 'u', }, 'CALENDAR_NEW_YEAR' => { 'yy' => 'd', 'yyyy' => 'd', }, 'CALENDAR_PAGE' => { 'name' => 't', "name-'s" => 's', 'yearlinks' => 't', 'months' => 't', 'username' => 's', 'website' => 't', 'head' => 't', 'urlfriends' => 'u', 'urllastn' => 'u', }, 'CALENDAR_WEBSITE' => { 'url' => 't', 'name' => 't', }, 'CALENDAR_WEEK' => { 'days' => 't', 'emptydays_beg' => 't', 'emptydays_end' => 't', }, 'CALENDAR_YEAR_DISPLAYED' => { 'yyyy' => 'd', 'yy' => 'd', }, 'CALENDAR_YEAR_LINK' => { 'yyyy' => 'd', 'yy' => 'd', 'url' => 'u', }, 'CALENDAR_YEAR_LINKS' => { 'years' => 't', }, # day 'DAY_DATE_FORMAT' => $commonprop->{'dateformat'}, 'DAY_EVENT' => $commonprop->{'event'}, 'DAY_EVENT_PRIVATE' => $commonprop->{'event'}, 'DAY_EVENT_PROTECTED' => $commonprop->{'event'}, 'DAY_PAGE' => { 'prevday_url' => 'u', 'nextday_url' => 'u', 'yy' => 'd', 'yyyy' => 'd', 'm' => 'd', 'mm' => 'd', 'd' => 'd', 'dd' => 'd', 'urllastn' => 'u', 'urlcalendar' => 'u', 'urlfriends' => 'u', }, 'DAY_TALK_LINKS' => $commonprop->{'talklinks'}, 'DAY_TALK_READLINK' => $commonprop->{'talkreadlink'}, # friends 'FRIENDS_DATE_FORMAT' => $commonprop->{'dateformat'}, 'FRIENDS_EVENT' => $commonprop->{'event'}, 'FRIENDS_EVENT_PRIVATE' => $commonprop->{'event'}, 'FRIENDS_EVENT_PROTECTED' => $commonprop->{'event'}, 'FRIENDS_FRIENDPIC' => $commonprop->{'pic'}, 'FRIENDS_NEW_DAY' => $commonprop->{'newday'}, 'FRIENDS_RANGE_HISTORY' => { 'numitems' => 'd', 'skip' => 'd', }, 'FRIENDS_RANGE_MOSTRECENT' => { 'numitems' => 'd', }, 'FRIENDS_SKIP_BACKWARD' => $commonprop->{'skip'}, 'FRIENDS_SKIP_FORWARD' => $commonprop->{'skip'}, 'FRIENDS_TALK_LINKS' => $commonprop->{'talklinks'}, 'FRIENDS_TALK_READLINK' => $commonprop->{'talkreadlink'}, # lastn 'LASTN_ALTPOSTER' => { 'poster' => 's', 'owner' => 's', 'pic' => 't', }, 'LASTN_ALTPOSTER_PIC' => $commonprop->{'pic'}, 'LASTN_CURRENT' => { 'what' => 's', 'value' => 't', }, 'LASTN_CURRENTS' => { 'currents' => 't', }, 'LASTN_DATEFORMAT' => $commonprop->{'dateformat'}, 'LASTN_EVENT' => $commonprop->{'event'}, 'LASTN_EVENT_PRIVATE' => $commonprop->{'event'}, 'LASTN_EVENT_PROTECTED' => $commonprop->{'event'}, 'LASTN_NEW_DAY' => $commonprop->{'newday'}, 'LASTN_PAGE' => { 'urlfriends' => 'u', 'urlcalendar' => 'u', }, 'LASTN_RANGE_HISTORY' => { 'numitems' => 'd', 'skip' => 'd', }, 'LASTN_RANGE_MOSTRECENT' => { 'numitems' => 'd', }, 'LASTN_SKIP_BACKWARD' => $commonprop->{'skip'}, 'LASTN_SKIP_FORWARD' => $commonprop->{'skip'}, 'LASTN_TALK_LINKS' => $commonprop->{'talklinks'}, 'LASTN_TALK_READLINK' => $commonprop->{'talkreadlink'}, 'LASTN_USERPIC' => { 'src' => 'u', 'width' => 'd', 'height' => 'd', }, }; # # name: LJ::S1::get_themeid # des: Loads or returns cached version of given color theme data. # returns: Hashref with color names as keys # args: dbarg?, themeid # des-themeid: S1 themeid. # sub get_themeid { &LJ::nodb; my $themeid = shift; return $LJ::S1::CACHE_THEMEID{$themeid} if $LJ::S1::CACHE_THEMEID{$themeid}; my $dbr = LJ::get_db_reader(); my $ret = {}; my $sth = $dbr->prepare("SELECT coltype, color FROM themedata WHERE themeid=?"); $sth->execute($themeid); $ret->{$_->{'coltype'}} = $_->{'color'} while $_ = $sth->fetchrow_hashref; return $LJ::S1::CACHE_THEMEID{$themeid} = $ret; } # returns: hashref of vars (cleaned) sub load_style { &LJ::nodb; my ($styleid, $viewref) = @_; # first try local cache for this process my $cch = $LJ::S1::CACHE_STYLE{$styleid}; if ($cch && $cch->{'cachetime'} > time() - 300) { $$viewref = $cch->{'type'} if ref $viewref eq "SCALAR"; return $cch->{'style'}; } # try memcache my $memkey = [$styleid, "s1styc:$styleid"]; my $styc = LJ::MemCache::get($memkey); # database handle we'll use if we have to rebuild the cache my $db; # function to return a given a styleid my $find_db = sub { my $sid = shift; # should we work with a global or clustered table? my $userid = LJ::S1::get_style_userid($sid); # if the user's style is clustered, need to get a $u my $u = $userid ? LJ::load_userid($userid) : undef; # return appropriate db handle if ($u && $u->{'dversion'} >= 5) { # users' styles are clustered return LJ::S1::get_s1style_writer($u); } return @LJ::MEMCACHE_SERVERS ? LJ::get_db_writer() : LJ::get_db_reader(); }; # get database stylecache unless ($styc) { $db = $find_db->($styleid); $styc = $db->selectrow_hashref("SELECT * FROM s1stylecache WHERE styleid=?", undef, $styleid); LJ::MemCache::set($memkey, $styc, time()+60*30) if $styc; } # no stylecache in db, built a new one if (! $styc || $styc->{'vars_cleanver'} < $LJ::S1::CLEANER_VERSION) { my $style = LJ::S1::get_style($styleid); return {} unless $style; $db ||= $find_db->($styleid); $styc = { 'type' => $style->{'type'}, 'opt_cache' => $style->{'opt_cache'}, 'vars_stor' => LJ::CleanHTML::clean_s1_style($style->{'formatdata'}), 'vars_cleanver' => $LJ::S1::CLEANER_VERSION, }; # do this query on the db handle we used above $db->do("REPLACE INTO s1stylecache (styleid, cleandate, type, opt_cache, vars_stor, vars_cleanver) ". "VALUES (?,NOW(),?,?,?,?)", undef, $styleid, map { $styc->{$_} } qw(type opt_cache vars_stor vars_cleanver)); } my $ret = Storable::thaw($styc->{'vars_stor'}); $$viewref = $styc->{'type'} if ref $viewref eq "SCALAR"; if ($styc->{'opt_cache'} eq "Y") { $LJ::S1::CACHE_STYLE{$styleid} = { 'style' => $ret, 'cachetime' => time(), 'type' => $styc->{'type'}, }; } return $ret; } # LJ::S1::get_public_styles # # LJ::load_user_props calls LJ::S1::get_public_styles and since # a lot of cron jobs call LJ::load_user_props, we've moved # LJ::S1::get_public_styles to ljlib so that it can be used # without including ljviews.pl sub get_s1style_writer { my $u = shift; return undef unless LJ::isu($u); # special case system, its styles live on # the global master's s1style table alone if ($u->{'user'} eq 'system') { return LJ::get_db_writer(); } return $u->writer; } sub get_s1style_reader { my $u = shift; return undef unless LJ::isu($u); # special case system, its styles live on # the global master's s1style table alone if ($u->{'user'} eq 'system') { return @LJ::MEMCACHE_SERVERS ? LJ::get_db_writer() : LJ::get_db_reader(); } return @LJ::MEMCACHE_SERVERS ? LJ::get_cluster_def_reader($u) : LJ::get_cluster_reader($u); } # takes either $u object or userid sub get_user_styles { my $u = shift; $u = LJ::isu($u) ? $u : LJ::load_user($u); return undef unless $u; my %styles; # all cols *except* formatdata, which is big and unnecessary for most uses. # it'll be loaded by LJ::S1::get_style my $cols = "styleid, styledes, type, is_public, is_embedded, ". "is_colorfree, opt_cache, has_ads, lastupdate"; # new clustered table my ($db, $sth); if ($u->{'dversion'} >= 5) { $db = LJ::S1::get_s1style_reader($u); $sth = $db->prepare("SELECT userid, $cols FROM s1style WHERE userid=?"); $sth->execute($u->{'userid'}); # old global table } else { $db = @LJ::MEMCACHE_SERVERS ? LJ::get_db_writer() : LJ::get_db_reader(); $sth = $db->prepare("SELECT user, $cols FROM style WHERE user=?"); $sth->execute($u->{'user'}); } # build data structure while (my $row = $sth->fetchrow_hashref) { # fix up both userid and user values for consistency $row->{'userid'} = $u->{'userid'}; $row->{'user'} = $u->{'user'}; $styles{$row->{'styleid'}} = $row; next unless @LJ::MEMCACHE_SERVERS; # now update memcache while we have this data? LJ::MemCache::set([$row->{'styleid'}, "s1style:$row->{'styleid'}"], $row); } return \%styles; } # includes formatdata row. sub get_style { my $styleid = shift; return unless $styleid; my $memkey = [$styleid, "s1style_all:$styleid"]; my $style = LJ::MemCache::get($memkey); return $style if $style; # query global mapping table, returns undef if style isn't clustered my $userid = LJ::S1::get_style_userid($styleid); my $u; $u = LJ::load_userid($userid) if $userid; # new clustered table if ($u && $u->{'dversion'} >= 5) { my $db = LJ::S1::get_s1style_reader($u); $style = $db->selectrow_hashref("SELECT * FROM s1style WHERE styleid=?", undef, $styleid); # fill in user since the caller may expect it $style->{'user'} = $u->{'user'}; # old global table } else { my $db = @LJ::MEMCACHE_SERVERS ? LJ::get_db_writer() : LJ::get_db_reader(); $style = $db->selectrow_hashref("SELECT * FROM style WHERE styleid=?", undef, $styleid); # fill in userid since the caller may expect it $style->{'userid'} = LJ::get_userid($style->{'user'}); } return unless $style; LJ::MemCache::set($memkey, $style); return $style; } sub check_dup_style { my ($u, $type, $styledes) = @_; return unless $type && $styledes; $u = LJ::isu($u) ? $u : LJ::load_user($u); # new clustered table if ($u && $u->{'dversion'} >= 5) { # get writer since this function is to check duplicates. as such, # the write action we're checking for probably happened recently my $db = LJ::S1::get_s1style_writer($u); return $db->selectrow_hashref("SELECT * FROM s1style WHERE userid=? AND type=? AND styledes=?", undef, $u->{'userid'}, $type, $styledes); # old global table } else { my $dbh = LJ::get_db_writer(); return $dbh->selectrow_hashref("SELECT * FROM style WHERE user=? AND type=? AND styledes=?", undef, $u->{'user'}, $type, $styledes); } } # returns undef if style isn't clustered sub get_style_userid { my $styleid = shift; # check cache my $userid = $LJ::S1::REQ_CACHE_STYLEMAP{$styleid}; return $userid if $userid; my $memkey = [$styleid, "s1stylemap:$styleid"]; my $style = LJ::MemCache::get($memkey); return $style if $style; # fetch from db my $dbr = LJ::get_db_reader(); $userid = $dbr->selectrow_array("SELECT userid FROM s1stylemap WHERE styleid=?", undef, $styleid); return unless $userid; # set cache $LJ::S1::REQ_CACHE_STYLEMAP{$styleid} = $userid; LJ::MemCache::set($memkey, $userid); return $userid; } sub create_style { my ($u, $opts) = @_; return unless LJ::isu($u) && ref $opts eq 'HASH'; my $dbh = LJ::get_db_writer(); return undef unless $dbh; my $styleid = LJ::alloc_global_counter('S'); return undef unless $styleid; my (@cols, @bind, @vals); foreach (qw(styledes type formatdata is_public is_embedded is_colorfree opt_cache has_ads)) { next unless $opts->{$_}; push @cols, $_; push @bind, "?"; push @vals, $opts->{$_}; } my $cols = join(",", @cols); my $bind = join(",", @bind); return unless @cols; if ($u->{'dversion'} >= 5) { my $db = LJ::S1::get_s1style_writer($u); $db->do("INSERT INTO s1style (styleid,userid,$cols) VALUES (?,?,$bind)", undef, $styleid, $u->{'userid'}, @vals); my $insertid = LJ::User::mysql_insertid($db); die "Couldn't allocate insertid for s1style for userid $u->{userid}" unless $insertid; $dbh->do("INSERT INTO s1stylemap (styleid, userid) VALUES (?,?)", undef, $insertid, $u->{'userid'}); return $insertid; } else { $dbh->do("INSERT INTO style (styleid, user,$cols) VALUES (?,?,$bind)", undef, $styleid, $u->{'user'}, @vals); return $dbh->{'mysql_insertid'}; } } sub update_style { my ($styleid, $opts) = @_; return unless $styleid && ref $opts eq 'HASH'; # query global mapping table, returns undef if style isn't clustered my $userid = LJ::S1::get_style_userid($styleid); my $u; $u = LJ::load_userid($userid) if $userid; my @cols = qw(styledes type formatdata is_public is_embedded is_colorfree opt_cache has_ads lastupdate); # what table to operate on ? my ($db, $table); # clustered table if ($u && $u->{'dversion'} >= 5) { $db = LJ::S1::get_s1style_writer($u); $table = "s1style"; # global table } else { $db = LJ::get_db_writer(); $table = "style"; } my (@sets, @vals); foreach (@cols) { if ($opts->{$_}) { push @sets, "$_=?"; push @vals, $opts->{$_}; } } # update style my $now_lastupdate = $opts->{'lastupdate'} ? ", lastupdate=NOW()" : ''; my $rows = $db->do("UPDATE $table SET " . join(", ", @sets) . "$now_lastupdate WHERE styleid=?", undef, @vals, $styleid); # clear out stylecache $db->do("UPDATE s1stylecache SET vars_stor=NULL, vars_cleanver=0 WHERE styleid=?", undef, $styleid); # update memcache keys LJ::MemCache::delete([$styleid, "s1style:$styleid"]); LJ::MemCache::delete([$styleid, "s1style_all:$styleid"]); LJ::MemCache::delete([$styleid, "s1styc:$styleid"]); return $rows; } sub delete_style { my $styleid = shift; return unless $styleid; # query global mapping table, returns undef if style isn't clustered my $userid = LJ::S1::get_style_userid($styleid); my $u; $u = LJ::load_userid($userid) if $userid; my $dbh = LJ::get_db_writer(); # new clustered table if ($u && $u->{'dversion'} >= 5) { $dbh->do("DELETE FROM s1stylemap WHERE styleid=?", undef, $styleid); my $db = LJ::S1::get_s1style_writer($u); $db->do("DELETE FROM s1style WHERE styleid=?", undef, $styleid); $db->do("DELETE FROM s1stylecache WHERE styleid=?", undef, $styleid); # old global table } else { # they won't have an s1stylemap entry $dbh->do("DELETE FROM style WHERE styleid=?", undef, $styleid); $dbh->do("DELETE FROM s1stylecache WHERE styleid=?", undef, $styleid); } # clear out some memcache space LJ::MemCache::delete([$styleid, "s1style:$styleid"]); LJ::MemCache::delete([$styleid, "s1style_all:$styleid"]); LJ::MemCache::delete([$styleid, "s1stylemap:$styleid"]); LJ::MemCache::delete([$styleid, "s1styc:$styleid"]); return; } sub get_overrides { my $u = shift; return unless LJ::isu($u); # try memcache my $memkey = [$u->{'userid'}, "s1overr:$u->{'userid'}"]; my $overr = LJ::MemCache::get($memkey); return $overr if $overr; # new clustered table if ($u->{'dversion'} >= 5) { my $db = @LJ::MEMCACHE_SERVERS ? LJ::get_cluster_def_reader($u) : LJ::get_cluster_reader($u); $overr = $db->selectrow_array("SELECT override FROM s1overrides WHERE userid=?", undef, $u->{'userid'}); # old global table } else { my $dbh = @LJ::MEMCACHE_SERVERS ? LJ::get_db_writer() : LJ::get_db_reader(); $overr = $dbh->selectrow_array("SELECT override FROM overrides WHERE user=?", undef, $u->{'user'}); } # set in memcache LJ::MemCache::set($memkey, $overr); return $overr; } sub clear_overrides { my $u = shift; return unless LJ::isu($u); my $overr; my $db; # new clustered table if ($u->{'dversion'} >= 5) { $overr = $u->do("DELETE FROM s1overrides WHERE userid=?", undef, $u->{'userid'}); $db = $u; # old global table } else { my $dbh = LJ::get_db_writer(); $overr = $dbh->do("DELETE FROM overrides WHERE user=?", undef, $u->{'user'}); $db = $dbh; } # update s1usercache $db->do("UPDATE s1usercache SET override_stor=NULL WHERE userid=?", undef, $u->{'userid'}); LJ::MemCache::delete([$u->{'userid'}, "s1uc:$u->{'userid'}"]); LJ::MemCache::delete([$u->{'userid'}, "s1overr:$u->{'userid'}"]); return $overr; } sub save_overrides { my ($u, $overr) = @_; return unless LJ::isu($u) && $overr; # new clustered table my $insertid; if ($u->{'dversion'} >= 5) { $u->do("REPLACE INTO s1overrides (userid, override) VALUES (?, ?)", undef, $u->{'userid'}, $overr); $insertid = $u->mysql_insertid; # old global table } else { my $dbh = LJ::get_db_writer(); $dbh->do("REPLACE INTO overrides (user, override) VALUES (?, ?)", undef, $u->{'user'}, $overr); $insertid = $dbh->{'mysql_insertid'}; } # update s1usercache my $override_stor = LJ::CleanHTML::clean_s1_style($overr); $u->do("UPDATE s1usercache SET override_stor=?, override_cleanver=? WHERE userid=?", undef, $override_stor, $LJ::S1::CLEANER_VERSION, $u->{'userid'}); LJ::MemCache::delete([$u->{'userid'}, "s1uc:$u->{'userid'}"]); LJ::MemCache::delete([$u->{'userid'}, "s1overr:$u->{'userid'}"]); return $insertid; } package LJ; # # name: LJ::alldateparts_to_hash # class: s1 # des: Given a date/time format from MySQL, breaks it into a hash. # info: This is used by S1. # args: alldatepart # des-alldatepart: The output of the MySQL function # DATE_FORMAT(sometime, "%a %W %b %M %y %Y %c %m %e %d # %D %p %i %l %h %k %H") # returns: Hash (whole, not reference), with keys: dayshort, daylong, # monshort, monlong, yy, yyyy, m, mm, d, dd, dth, ap, AP, # ampm, AMPM, min, 12h, 12hh, 24h, 24hh # sub alldateparts_to_hash { my $alldatepart = shift; my @dateparts = split(/ /, $alldatepart); return ( 'dayshort' => $dateparts[0], 'daylong' => $dateparts[1], 'monshort' => $dateparts[2], 'monlong' => $dateparts[3], 'yy' => $dateparts[4], 'yyyy' => $dateparts[5], 'm' => $dateparts[6], 'mm' => $dateparts[7], 'd' => $dateparts[8], 'dd' => $dateparts[9], 'dth' => $dateparts[10], 'ap' => substr(lc($dateparts[11]),0,1), 'AP' => substr(uc($dateparts[11]),0,1), 'ampm' => lc($dateparts[11]), 'AMPM' => $dateparts[11], 'min' => $dateparts[12], '12h' => $dateparts[13], '12hh' => $dateparts[14], '24h' => $dateparts[15], '24hh' => $dateparts[16], ); } # # class: s1 # name: LJ::fill_var_props # args: vars, key, hashref # des: S1 utility function to interpolate %%variables%% in a variable. If # a modifier is given like %%foo:var%%, then [func[LJ::fvp_transform]] # is called. # des-vars: hashref with keys being S1 vars # des-key: the variable in the vars hashref we're expanding # des-hashref: hashref of values that could interpolate. # returns: Expanded string. # sub fill_var_props { my ($vars, $key, $hashref) = @_; $_ = $vars->{$key}; s/%%([\w:]+:)?([\w\-\']+)%%/$1 ? LJ::fvp_transform(lc($1), $vars, $hashref, $2) : $hashref->{$2}/eg; return $_; } # # class: s1 # name: LJ::fvp_transform # des: Called from [func[LJ::fill_var_props]] to do trasformations. # args: transform, vars, hashref, attr # des-transform: The transformation type. # des-vars: hashref with keys being S1 vars # des-hashref: hashref of values that could interpolate. (see # [func[LJ::fill_var_props]]) # des-attr: the attribute name that's being interpolated. # returns: Transformed interpolated variable. # sub fvp_transform { my ($transform, $vars, $hashref, $attr) = @_; my $ret = $hashref->{$attr}; while ($transform =~ s/(\w+):$//) { my $trans = $1; if ($trans eq "color") { return $vars->{"color-$attr"}; } elsif ($trans eq "ue") { $ret = LJ::eurl($ret); } elsif ($trans eq "cons") { if ($attr eq "img") { return $LJ::IMGPREFIX; } if ($attr eq "siteroot") { return $LJ::SITEROOT; } if ($attr eq "sitename") { return $LJ::SITENAME; } } elsif ($trans eq "attr") { $ret =~ s/\"/"/g; $ret =~ s/\'/&\#39;/g; $ret =~ s//>/g; $ret =~ s/\]\]//g; # so they can't end the parent's [attr[..]] wrapper } elsif ($trans eq "lc") { $ret = lc($ret); } elsif ($trans eq "uc") { $ret = uc($ret); } elsif ($trans eq "xe") { $ret = LJ::exml($ret); } elsif ($trans eq 'ljuser' or $trans eq 'ljcomm') { my $user = LJ::canonical_username($ret); $ret = LJ::ljuser($user); } elsif ($trans eq 'userurl') { my $u = LJ::load_user($ret); $ret = LJ::journal_base($u) if $u; } } return $ret; } # # class: s1 # name: LJ::parse_vars # des: Parses S1 style data into hashref. # returns: Nothing. Modifies a hashref. # args: dataref, hashref # des-dataref: Reference to scalar with data to parse. Format is # a BML-style full block, as used in the S1 style system. # des-hashref: Hashref to populate with data. # sub parse_vars { my ($dataref, $hashref) = @_; my @data = split(/\n/, $$dataref); my $curitem = ""; foreach (@data) { $_ .= "\n"; s/\r//g; if ($curitem eq "" && /^([A-Z0-9\_]+)=>([^\n\r]*)/) { $hashref->{$1} = $2; } elsif ($curitem eq "" && /^([A-Z0-9\_]+)<=\s*$/) { $curitem = $1; $hashref->{$curitem} = ""; } elsif ($curitem && /^<=$curitem\s*$/) { chop $hashref->{$curitem}; # remove the false newline $curitem = ""; } else { $hashref->{$curitem} .= $_ if ($curitem =~ /\S/); } } } sub current_mood_str { my ($themeid, $moodid, $mood) = @_; # ideal behavior: if there is a moodid, that defines the picture. # if there is a current_mood, that overrides as the mood name, # otherwise show the mood name associated with current_moodid my $moodname; my $moodpic; # favor custom mood over system mood if (my $val = $mood) { LJ::CleanHTML::clean_subject(\$val); $moodname = $val; } if (my $val = $moodid) { $moodname ||= LJ::mood_name($val); my %pic; if (LJ::get_mood_picture($themeid, $val, \%pic)) { $moodpic = " "; } } return "$moodpic$moodname"; } sub current_music_str { my $val = shift; LJ::CleanHTML::clean_subject(\$val); return $val; } # # class: s1 # name: LJ::prepare_currents # des: do all the current music/mood/weather/whatever stuff. only used by ljviews.pl. # args: dbarg, args # des-args: hashref with keys: 'props' (a hashref), 'vars' hashref with # keys being S1 variables. # sub prepare_currents { my $args = shift; my %currents = (); my $val; if ($val = $args->{'props'}->{'current_music'}) { $currents{'Music'} = LJ::current_music_str($val); } $currents{'Mood'} = LJ::current_mood_str($args->{'user'}->{'moodthemeid'}, $args->{'props'}->{'current_moodid'}, $args->{'props'}->{'current_mood'}); delete $currents{'Mood'} unless $currents{'Mood'}; if (%currents) { if ($args->{'vars'}->{$args->{'prefix'}.'_CURRENTS'}) { ### PREFIX_CURRENTS is defined, so use the correct style vars my $fvp = { 'currents' => "" }; foreach (sort keys %currents) { $fvp->{'currents'} .= LJ::fill_var_props($args->{'vars'}, $args->{'prefix'}.'_CURRENT', { 'what' => $_, 'value' => $currents{$_}, }); } $args->{'event'}->{'currents'} = LJ::fill_var_props($args->{'vars'}, $args->{'prefix'}.'_CURRENTS', $fvp); } else { ### PREFIX_CURRENTS is not defined, so just add to %%events%% $args->{'event'}->{'event'} .= "
 "; foreach (sort keys %currents) { $args->{'event'}->{'event'} .= "
Current $_: " . $currents{$_} . "\n"; } } } } package LJ::S1; use strict; require "$ENV{'LJHOME'}/cgi-bin/ljconfig.pl"; require "$ENV{'LJHOME'}/cgi-bin/ljlang.pl"; require "$ENV{'LJHOME'}/cgi-bin/cleanhtml.pl"; # the creator for the 'lastn' view: sub create_view_lastn { my ($ret, $u, $vars, $remote, $opts) = @_; my $user = $u->{'user'}; if ($u->{'journaltype'} eq "R" && $u->{'renamedto'} ne "") { $opts->{'redir'} = LJ::journal_base($u->{'renamedto'}, $opts->{'vhost'}); return 1; } foreach ("name", "url", "urlname", "journaltitle") { LJ::text_out(\$u->{$_}); } my $get = $opts->{'getargs'}; if ($opts->{'pathextra'}) { $opts->{'badargs'} = 1; return 1; } LJ::load_user_props($remote, "opt_ljcut_disable_lastn"); my %lastn_page = (); $lastn_page{'name'} = LJ::ehtml($u->{'name'}); $lastn_page{'name-\'s'} = ($u->{'name'} =~ /s$/i) ? "'" : "'s"; $lastn_page{'username'} = $user; $lastn_page{'title'} = LJ::ehtml($u->{'journaltitle'} || $u->{'name'} . $lastn_page{'name-\'s'} . " Journal"); $lastn_page{'numitems'} = $vars->{'LASTN_OPT_ITEMS'} || 20; my $journalbase = LJ::journal_base($user, $opts->{'vhost'}); $lastn_page{'urlfriends'} = "$journalbase/friends"; $lastn_page{'urlcalendar'} = "$journalbase/calendar"; if ($u->{'url'} =~ m!^https?://!) { $lastn_page{'website'} = LJ::fill_var_props($vars, 'LASTN_WEBSITE', { "url" => LJ::ehtml($u->{'url'}), "name" => LJ::ehtml($u->{'urlname'} || "My Website"), }); } $lastn_page{'events'} = ""; $lastn_page{'head'} = ""; if (LJ::are_hooks('s2_head_content_extra')) { $lastn_page{'head'} .= LJ::run_hook('s2_head_content_extra', $remote, $opts->{r}); } # if user has requested, or a skip back link has been followed, don't index or follow if ($u->{'opt_blockrobots'} || $get->{'skip'}) { $lastn_page{'head'} .= LJ::robot_meta_tags() } if ($LJ::UNICODE) { $lastn_page{'head'} .= '\n"; } # Automatic Discovery of RSS/Atom $lastn_page{'head'} .= qq{\n}; $lastn_page{'head'} .= qq{\n}; $lastn_page{'head'} .= qq{\n}; $lastn_page{'head'} .= qq{\n}; $lastn_page{'head'} .= qq{\n} if LJ::OpenID::server_enabled(); # FOAF autodiscovery my $real_user = 1; foreach (@LJ::PROTECTED_USERNAMES) { $real_user = 0 if ($u->{'user'} =~ $_); } if ($real_user) { my $foafurl = $u->{external_foaf_url} ? LJ::eurl($u->{external_foaf_url}) : "$journalbase/data/foaf"; my $digest = Digest::SHA1::sha1_hex('mailto:' . $u->{email}); $lastn_page{head} .= qq{\n}; $lastn_page{head} .= qq{\n}; } $lastn_page{'head'} .= $vars->{'GLOBAL_HEAD'} . "\n" . $vars->{'LASTN_HEAD'}; my $events = \$lastn_page{'events'}; # to show my $itemshow = $vars->{'LASTN_OPT_ITEMS'} + 0; if ($itemshow < 1) { $itemshow = 20; } if ($itemshow > 50) { $itemshow = 50; } my $skip = $get->{'skip'}+0; my $maxskip = $LJ::MAX_HINTS_LASTN-$itemshow; if ($skip < 0) { $skip = 0; } if ($skip > $maxskip) { $skip = $maxskip; } my $dayskip = $get->{'dayskip'}+0; # do they have the viewall priv? my $viewall = 0; my $viewsome = 0; if ($get->{'viewall'} && LJ::check_priv($remote, "canview")) { LJ::statushistory_add($u->{'userid'}, $remote->{'userid'}, "viewall", "lastn: $user, statusvis: $u->{'statusvis'}"); $viewall = LJ::check_priv($remote, 'canview', '*'); $viewsome = $viewall || LJ::check_priv($remote, 'canview', 'suspended'); } ## load the items my $err; my @items = LJ::get_recent_items({ 'u' => $u, 'clustersource' => 'slave', 'viewall' => $viewall, 'remote' => $remote, 'itemshow' => $itemshow, 'skip' => $skip, 'dayskip' => $dayskip, 'tags' => $opts->{tags}, # 'order' => ($u->{'journaltype'} eq "C" || $u->{'journaltype'} eq "Y") # community or syndicated # ? "logtime" : "", 'order' => ($u->{'journaltype'} eq "Y") # community or syndicated ? "logtime" : "", 'err' => \$err, }); if ($err) { $opts->{'errcode'} = $err; $$ret = ""; return 0; } my $lastday = -1; my $lastmonth = -1; my $lastyear = -1; my $eventnum = 0; my %posteru = (); # map posterids to u objects LJ::load_userids_multiple([map { $_->{'posterid'}, \$posteru{$_->{'posterid'}} } @items], [$u]); # pre load things in a batch (like userpics) to minimize db calls my @userpic_load; push @userpic_load, [ $u, $u->{'defaultpicid'} ] if $u->{'defaultpicid'}; foreach my $item (@items) { next if $item->{'posterid'} == $u->{'userid'}; my $itemid = $item->{'itemid'}; my $pu = $posteru{$item->{'posterid'}}; my $picid = LJ::get_picid_from_keyword($pu, $item->{'props'}->{'picture_keyword'}); $item->{'_picid'} = $picid; push @userpic_load, [ $pu, $picid ] if ($picid && ! grep { $_ eq $picid } @userpic_load); } my %userpics; LJ::load_userpics(\%userpics, \@userpic_load); if (my $picid = $u->{'defaultpicid'}) { $lastn_page{'userpic'} = LJ::fill_var_props($vars, 'LASTN_USERPIC', { "src" => "$LJ::USERPIC_ROOT/$picid/$u->{'userid'}", "width" => $userpics{$picid}->{'width'}, "height" => $userpics{$picid}->{'height'}, }); } # spit out the S1 my $can_manage = LJ::can_manage($remote, $u); ENTRY: foreach my $item (@items) { my ($posterid, $itemid, $security, $alldatepart) = map { $item->{$_} } qw(posterid itemid security alldatepart); my $pu = $posteru{$posterid}; next ENTRY if $pu && $pu->{'statusvis'} eq 'S' && !$viewsome; my $props = $item->{'props'}; my $replycount = $props->{'replycount'}; my $subject = $item->{'text'}->[0]; my $event = $item->{'text'}->[1]; if ($get->{'nohtml'}) { # quote all non-LJ tags $subject =~ s{<(?!/?lj)(.*?)>} {<$1>}gi; $event =~ s{<(?!/?lj)(.*?)>} {<$1>}gi; } my %lastn_date_format = LJ::alldateparts_to_hash($alldatepart); if ($lastday != $lastn_date_format{'d'} || $lastmonth != $lastn_date_format{'m'} || $lastyear != $lastn_date_format{'yyyy'}) { my %lastn_new_day = (); foreach (qw(dayshort daylong monshort monlong m mm yy yyyy d dd dth)) { $lastn_new_day{$_} = $lastn_date_format{$_}; } unless ($lastday==-1) { $$events .= LJ::fill_var_props($vars, 'LASTN_END_DAY', {}); } $$events .= LJ::fill_var_props($vars, 'LASTN_NEW_DAY', \%lastn_new_day); $lastday = $lastn_date_format{'d'}; $lastmonth = $lastn_date_format{'m'}; $lastyear = $lastn_date_format{'yyyy'}; } my %lastn_event = (); $eventnum++; $lastn_event{'eventnum'} = $eventnum; $lastn_event{'itemid'} = $itemid; $lastn_event{'datetime'} = LJ::fill_var_props($vars, 'LASTN_DATE_FORMAT', \%lastn_date_format); if ($subject ne "") { LJ::CleanHTML::clean_subject(\$subject); $lastn_event{'subject'} = LJ::fill_var_props($vars, 'LASTN_SUBJECT', { "subject" => $subject, }); } my $ditemid = $itemid * 256 + $item->{'anum'}; my $itemargs = "journal=$user&itemid=$ditemid"; $lastn_event{'itemargs'} = $itemargs; LJ::CleanHTML::clean_event(\$event, { 'preformatted' => $props->{'opt_preformatted'}, 'cuturl' => LJ::item_link($u, $itemid, $item->{'anum'}), 'ljcut_disable' => $remote->{'opt_ljcut_disable_lastn'}, }); LJ::expand_embedded($u, $ditemid, $remote, \$event); $lastn_event{'event'} = $event; if ($u->{'opt_showtalklinks'} eq "Y" && ! $props->{'opt_nocomments'} || $props->{'syn_link'} ne "") { my $nc; $nc = "nc=$replycount" if $replycount; # && $remote && $remote->{'opt_nctalklinks'}; my $permalink = "$journalbase/$ditemid.html"; my $posturl = LJ::Talk::talkargs($permalink, "mode=reply"); my $readurl = LJ::Talk::talkargs($permalink, $nc); my $dispreadlink = $replycount || ($can_manage && $props->{'hasscreened'}); if ($ljr && $props->{'syn_link'}) { my $rs = LJR::Distributed::match_remote_server($props->{'syn_link'}); if ($rs->{"servertype"} eq "lj") { $posturl = $props->{'syn_link'} . "?mode=reply"; $dispreadlink = 1; $readurl = $props->{'syn_link'}; $replycount = 'Read'; } else { $posturl = $props->{'syn_link'}; $dispreadlink = 0; $replycount = undef; } } $lastn_event{'talklinks'} = LJ::fill_var_props($vars, 'LASTN_TALK_LINKS', { 'itemid' => $ditemid, 'itemargs' => $itemargs, 'urlpost' => $posturl, 'urlread' => $readurl, 'messagecount' => $replycount, 'readlink' => $dispreadlink ? LJ::fill_var_props($vars, 'LASTN_TALK_READLINK', { 'urlread' => $readurl, 'messagecount' => $replycount, 'mc-plural-s' => $replycount == 1 ? "" : "s", 'mc-plural-es' => $replycount == 1 ? "" : "es", 'mc-plural-ies' => $replycount == 1 ? "y" : "ies", }) : "", }); } ## current stuff LJ::prepare_currents({ 'props' => $props, 'vars' => $vars, 'prefix' => "LASTN", 'event' => \%lastn_event, 'user' => $u, }); if ($u->{'userid'} != $posterid) { my %lastn_altposter = (); my $poster = $pu->{'user'}; $lastn_altposter{'poster'} = $poster; $lastn_altposter{'owner'} = $user; if (my $picid = $item->{'_picid'}) { my $pic = $userpics{$picid}; $lastn_altposter{'pic'} = LJ::fill_var_props($vars, 'LASTN_ALTPOSTER_PIC', { "src" => "$LJ::USERPIC_ROOT/$picid/$pic->{'userid'}", "width" => $pic->{'width'}, "height" => $pic->{'height'}, }); } $lastn_event{'altposter'} = LJ::fill_var_props($vars, 'LASTN_ALTPOSTER', \%lastn_altposter); } my $var = 'LASTN_EVENT'; if ($security eq "private" && $vars->{'LASTN_EVENT_PRIVATE'}) { $var = 'LASTN_EVENT_PRIVATE'; } if ($security eq "usemask" && $vars->{'LASTN_EVENT_PROTECTED'}) { $var = 'LASTN_EVENT_PROTECTED'; } $$events .= LJ::fill_var_props($vars, $var, \%lastn_event); } # end huge while loop $$events .= LJ::fill_var_props($vars, 'LASTN_END_DAY', {}); my $item_shown = $eventnum; my $item_total = @items; my $item_hidden = $item_total - $item_shown; if ($skip) { $lastn_page{'range'} = LJ::fill_var_props($vars, 'LASTN_RANGE_HISTORY', { "numitems" => $item_shown, "skip" => $skip, }); } else { $lastn_page{'range'} = LJ::fill_var_props($vars, 'LASTN_RANGE_MOSTRECENT', { "numitems" => $item_shown, }); } #### make the skip links my ($skip_f, $skip_b) = (0, 0); my %skiplinks; ### if we've skipped down, then we can skip back up if ($skip) { $skip_f = 1; my $newskip = $skip - $itemshow; $newskip = 0 if $newskip <= 0; my $url = LJ::make_link("$journalbase/", { skip => ($newskip || ""), dayskip => ($dayskip || "") }); $skiplinks{'skipforward'} = LJ::fill_var_props($vars, 'LASTN_SKIP_FORWARD', { "numitems" => $itemshow, "url" => $url, }); } ## unless we didn't even load as many as we were expecting on this ## page, then there are more (unless there are exactly the number shown ## on the page, but who cares about that) unless ($item_total != $itemshow) { $skip_b = 1; if ($skip==$maxskip) { $skiplinks{'skipbackward'} = LJ::fill_var_props($vars, 'LASTN_SKIP_BACKWARD', { "numitems" => "Day", "url" => "$journalbase/" . sprintf("%04d/%02d/%02d/", $lastyear, $lastmonth, $lastday), }); } else { my $newskip = $skip + $itemshow; my $url = LJ::make_link("$journalbase/", { skip => ($newskip || ""), dayskip => ($dayskip || "") }); $skiplinks{'skipbackward'} = LJ::fill_var_props($vars, 'LASTN_SKIP_BACKWARD', { "numitems" => $itemshow, "url" => $url, }); } } ### if they're both on, show a spacer if ($skip_b && $skip_f) { $skiplinks{'skipspacer'} = $vars->{'LASTN_SKIP_SPACER'}; } ### if either are on, put skiplinks into lastn_page if ($skip_b || $skip_f) { $lastn_page{'skiplinks'} = LJ::fill_var_props($vars, 'LASTN_SKIP_LINKS', \%skiplinks); } $$ret = LJ::fill_var_props($vars, 'LASTN_PAGE', \%lastn_page); return 1; } # the creator for the 'friends' view: sub create_view_friends { my ($ret, $u, $vars, $remote, $opts) = @_; my $sth; my $user = $u->{'user'}; # see how often the remote user can reload this page. # "friendsviewupdate" time determines what granularity time # increments by for checking for new updates my $nowtime = time(); # update delay specified by "friendsviewupdate" my $newinterval = LJ::get_cap_min($remote, "friendsviewupdate") || 1; # when are we going to say page was last modified? back up to the # most recent time in the past where $time % $interval == 0 my $lastmod = $nowtime; $lastmod -= $lastmod % $newinterval; # see if they have a previously cached copy of this page they # might be able to still use. my $ims = $opts->{'r'}->header_in('If-Modified-Since'); if ($ims) { my $theirtime = LJ::http_to_time($ims); # send back a 304 Not Modified if they say they've reloaded this # document in the last $newinterval seconds: unless ($theirtime < $lastmod) { $opts->{'handler_return'} = 304; return 1; } } $opts->{'r'}->header_out('Last-Modified', LJ::time_to_http($lastmod)); $$ret = ""; my $get = $opts->{'getargs'}; my $journalbase = LJ::journal_base($user, $opts->{'vhost'}); if ($get->{'mode'} eq "live") { $$ret .= "${user}'s friends: live!\n"; $$ret .= "\n"; $$ret .= " \n"; $$ret .= " \n"; $$ret .= "\n"; return 1; } if ($u->{'journaltype'} eq "R" && $u->{'renamedto'} ne "") { $opts->{'redir'} = LJ::journal_base($u->{'renamedto'}, $opts->{'vhost'}) . "/friends"; return 1; } foreach ("name", "url", "urlname", "friendspagetitle") { LJ::text_out(\$u->{$_}); } my %friends_page = (); $friends_page{'name'} = LJ::ehtml($u->{'name'}); $friends_page{'name-\'s'} = ($u->{'name'} =~ /s$/i) ? "'" : "'s"; $friends_page{'username'} = $user; $friends_page{'title'} = LJ::ehtml($u->{'friendspagetitle'} || $u->{'name'} . $friends_page{'name-\'s'} . " Friends"); $friends_page{'numitems'} = $vars->{'FRIENDS_OPT_ITEMS'} || 20; $friends_page{'head'} = ""; if (LJ::are_hooks('s2_head_content_extra')) { $friends_page{'head'} .= LJ::run_hook('s2_head_content_extra', $remote, $opts->{r}); } ## never have spiders index friends pages (change too much, and some ## people might not want to be indexed) $friends_page{'head'} .= LJ::robot_meta_tags(); if ($LJ::UNICODE) { $friends_page{'head'} .= ''; } $friends_page{'head'} .= $vars->{'GLOBAL_HEAD'} . "\n" . $vars->{'FRIENDS_HEAD'}; if ($u->{'url'} =~ m!^https?://!) { $friends_page{'website'} = LJ::fill_var_props($vars, 'FRIENDS_WEBSITE', { "url" => LJ::ehtml($u->{'url'}), "name" => LJ::ehtml($u->{'urlname'} || "My Website"), }); } $friends_page{'urlcalendar'} = "$journalbase/calendar"; $friends_page{'urllastn'} = "$journalbase/"; $friends_page{'events'} = ""; my $itemshow = $vars->{'FRIENDS_OPT_ITEMS'} + 0; if ($itemshow < 1) { $itemshow = 20; } if ($itemshow > 50) { $itemshow = 50; } my $skip = $get->{'skip'}+0; my $maxskip = ($LJ::MAX_SCROLLBACK_FRIENDS || 1000) - $itemshow; if ($skip > $maxskip) { $skip = $maxskip; } if ($skip < 0) { $skip = 0; } my $filter; my $group; my $common_filter = 1; if (defined $get->{'filter'} && $remote && $remote->{'user'} eq $user) { $filter = $get->{'filter'}; $common_filter = 0; } else { if ($opts->{'pathextra'}) { $group = $opts->{'pathextra'}; $group =~ s!^/!!; $group =~ s!/$!!; if ($group) { $group = LJ::durl($group); $common_filter = 0;} } my $grp = LJ::get_friend_group($u, { 'name' => $group || "Default View" }); my $bit = $grp ? $grp->{'groupnum'} : 0; my $public = $grp ? $grp->{'is_public'} : 0; if ($bit && ($public || ($remote && $remote->{'user'} eq $user))) { $filter = (1 << $bit); } elsif ($group) { $opts->{'badfriendgroup'} = 1; return 1; } } if ($get->{'mode'} eq "livecond") { ## load the items my @items = LJ::get_friend_items({ 'u' => $u, 'remote' => $remote, 'itemshow' => 1, 'skip' => 0, 'filter' => $filter, 'common_filter' => $common_filter, }); my $first = @items ? $items[0]->{'itemid'} : 0; $$ret .= "time = " . scalar(time()) . "
"; $opts->{'headers'}->{'Refresh'} = "30;URL=$LJ::SITEROOT/users/$user/friends?mode=livecond&lastitemid=$first"; if ($get->{'lastitemid'} == $first) { $$ret .= "nothing new!"; } else { if ($get->{'lastitemid'}) { $$ret .= "New stuff!\n"; $$ret .= "\n"; $opts->{'trusted_html'} = 1; } else { $$ret .= "Friends Live! started."; } } return 1; } ## load the items my %friends; my %friends_row; my @items = LJ::get_friend_items({ 'u' => $u, 'remote' => $remote, 'itemshow' => $itemshow, 'skip' => $skip, 'dayskip' => $get->{'dayskip'}, 'filter' => $filter, 'common_filter' => $common_filter, 'friends_u' => \%friends, 'friends' => \%friends_row, 'showtypes' => $get->{'show'}, 'friendsoffriends' => $opts->{'view'} eq "friendsfriends", }); while ($_ = each %friends) { # we expect fgcolor/bgcolor to be in here later $friends{$_}->{'fgcolor'} = $friends_row{$_}->{'fgcolor'} || '#000000'; $friends{$_}->{'bgcolor'} = $friends_row{$_}->{'bgcolor'} || '#ffffff'; } unless (%friends) { $friends_page{'events'} = LJ::fill_var_props($vars, 'FRIENDS_NOFRIENDS', { "name" => LJ::ehtml($u->{'name'}), "name-\'s" => ($u->{'name'} =~ /s$/i) ? "'" : "'s", "username" => $user, }); $$ret .= "" if ($get->{'mode'} eq "framed"); $$ret .= LJ::fill_var_props($vars, 'FRIENDS_PAGE', \%friends_page); return 1; } my %aposter; # alt-posterid -> u object (if not in friends already) LJ::load_userids_multiple([map { $_->{'posterid'}, \$aposter{$_->{'posterid'}} } grep { $friends{$_->{'ownerid'}} && ! $friends{$_->{'posterid'}} } @items], [ $u, $remote ]); # load the pictures for the user my %userpics; my @picids = map { [$friends{$_}, $friends{$_}->{'defaultpicid'}] } keys %friends; LJ::load_userpics(\%userpics, [ @picids, map { [ $_, $_->{'defaultpicid'} ] } values %aposter ]); # load 'opt_stylemine' prop for $remote. don't need to load opt_nctalklinks # because that was already faked in LJ::make_journal previously LJ::load_user_props($remote, "opt_stylemine", "opt_imagelinks", "opt_ljcut_disable_friends"); # load options for image links my ($maximgwidth, $maximgheight) = (undef, undef); ($maximgwidth, $maximgheight) = ($1, $2) if ($remote && $remote->{'userid'} == $u->{'userid'} && $remote->{'opt_imagelinks'} =~ m/^(\d+)\|(\d+)$/); my %friends_events = (); my $events = \$friends_events{'events'}; my $lastday = -1; my $eventnum = 0; ENTRY: foreach my $item (@items) { my ($friendid, $posterid, $itemid, $security, $alldatepart) = map { $item->{$_} } qw(ownerid posterid itemid security alldatepart); my $pu = $friends{$posterid} || $aposter{$posterid}; next ENTRY if $pu && $pu->{'statusvis'} eq 'S'; # counting excludes skipped entries $eventnum++; my $clusterid = $item->{'clusterid'}+0; my $props = $item->{'props'}; my $replycount = $props->{'replycount'}; my $subject = $item->{'text'}->[0]; my $event = $item->{'text'}->[1]; if ($get->{'nohtml'}) { # quote all non-LJ tags $subject =~ s{<(?!/?lj)(.*?)>} {<$1>}gi; $event =~ s{<(?!/?lj)(.*?)>} {<$1>}gi; } my ($fru, $friend, $poster); $fru = $friends{$friendid}; $friend = $poster = $fru->{'user'}; $poster = $pu->{'user'}; my %friends_date_format = LJ::alldateparts_to_hash($alldatepart); if ($lastday != $friends_date_format{'d'}) { my %friends_new_day = (); foreach (qw(dayshort daylong monshort monlong m mm yy yyyy d dd dth)) { $friends_new_day{$_} = $friends_date_format{$_}; } unless ($lastday==-1) { $$events .= LJ::fill_var_props($vars, 'FRIENDS_END_DAY', {}); } $$events .= LJ::fill_var_props($vars, 'FRIENDS_NEW_DAY', \%friends_new_day); $lastday = $friends_date_format{'d'}; } my %friends_event = (); $friends_event{'itemid'} = $itemid; $friends_event{'datetime'} = LJ::fill_var_props($vars, 'FRIENDS_DATE_FORMAT', \%friends_date_format); if ($subject ne "") { LJ::CleanHTML::clean_subject(\$subject); $friends_event{'subject'} = LJ::fill_var_props($vars, 'FRIENDS_SUBJECT', { "subject" => $subject, }); } else { $friends_event{'subject'} = LJ::fill_var_props($vars, 'FRIENDS_NO_SUBJECT', { "friend" => $friend, "name" => $fru->{'name'}, }); } my $ditemid = $itemid * 256 + $item->{'anum'}; my $itemargs = "journal=$friend&itemid=$ditemid"; $friends_event{'itemargs'} = $itemargs; my $stylemine = ""; $stylemine .= "style=mine" if $remote && $remote->{'opt_stylemine'} && $remote->{'userid'} != $friendid; LJ::CleanHTML::clean_event(\$event, { 'preformatted' => $props->{'opt_preformatted'}, 'cuturl' => LJ::item_link($fru, $itemid, $item->{'anum'}, $stylemine), 'maximgwidth' => $maximgwidth, 'maximgheight' => $maximgheight, 'ljcut_disable' => $remote->{'opt_ljcut_disable_friends'}, }); LJ::expand_embedded($fru, $ditemid, $remote, \$event); $friends_event{'event'} = $event; # do the picture { my $picid = $fru->{'defaultpicid'}; # this could be the shared journal pic my $picuserid = $friendid; if ($friendid != $posterid && ! $u->{'opt_usesharedpic'}) { if ($pu->{'defaultpicid'}) { $picid = $pu->{'defaultpicid'}; $picuserid = $posterid; } } if ($props->{'picture_keyword'} && (! $u->{'opt_usesharedpic'} || ($posterid == $friendid))) { my $alt_picid = LJ::get_picid_from_keyword($posterid, $props->{'picture_keyword'}); if ($alt_picid) { LJ::load_userpics(\%userpics, [ $pu, $alt_picid ]); $picid = $alt_picid; $picuserid = $posterid; } } if ($picid) { $friends_event{'friendpic'} = LJ::fill_var_props($vars, 'FRIENDS_FRIENDPIC', { "src" => "$LJ::USERPIC_ROOT/$picid/$picuserid", "width" => $userpics{$picid}->{'width'}, "height" => $userpics{$picid}->{'height'}, }); } } if ($friend ne $poster) { $friends_event{'altposter'} = LJ::fill_var_props($vars, 'FRIENDS_ALTPOSTER', { "poster" => $poster, "owner" => $friend, "fgcolor" => $fru->{'fgcolor'} || "#000000", "bgcolor" => $fru->{'bgcolor'} || "#ffffff", }); } # friends view specific: $friends_event{'user'} = $friend; $friends_event{'fgcolor'} = $fru->{'fgcolor'} || "#000000"; $friends_event{'bgcolor'} = $fru->{'bgcolor'} || "#ffffff"; if ($fru->{'opt_showtalklinks'} eq "Y" && ! $props->{'opt_nocomments'} || $props->{'syn_link'} ne "") { my $dispreadlink = $replycount || ($remote && LJ::can_manage($remote, $fru) && $props->{'hasscreened'}); my $journalbase = LJ::journal_base($fru); my $nc = ""; $nc .= "nc=$replycount" if $replycount; # && $remote && $remote->{'opt_nctalklinks'}; my $permalink = "$journalbase/$ditemid.html"; my $readurl = LJ::Talk::talkargs($permalink, $nc, $stylemine); my $posturl = LJ::Talk::talkargs($permalink, "mode=reply", $stylemine); if ($ljr && $props->{'syn_link'}) { my $rs = LJR::Distributed::match_remote_server($props->{'syn_link'}); if ($rs->{"servertype"} eq "lj") { $posturl = $props->{'syn_link'} . "?mode=reply"; $dispreadlink = 1; $readurl = $props->{'syn_link'}; $replycount = 'Read'; } else { $posturl = $props->{'syn_link'}; $dispreadlink = 0; $replycount = undef; } } $friends_event{'talklinks'} = LJ::fill_var_props($vars, 'FRIENDS_TALK_LINKS', { 'itemid' => $ditemid, 'itemargs' => $itemargs, 'urlpost' => $posturl, 'urlread' => $readurl, 'messagecount' => $replycount, 'readlink' => $dispreadlink ? LJ::fill_var_props($vars, 'FRIENDS_TALK_READLINK', { 'urlread' => $readurl, 'messagecount' => $replycount, 'mc-plural-s' => $replycount == 1 ? "" : "s", 'mc-plural-es' => $replycount == 1 ? "" : "es", 'mc-plural-ies' => $replycount == 1 ? "y" : "ies", }) : "", }); } ## current stuff LJ::prepare_currents({ 'props' => $props, 'vars' => $vars, 'prefix' => "FRIENDS", 'event' => \%friends_event, 'user' => ($u->{'opt_forcemoodtheme'} eq "Y" ? $u : $fru), }); my $var = 'FRIENDS_EVENT'; if ($security eq "private" && $vars->{'FRIENDS_EVENT_PRIVATE'}) { $var = 'FRIENDS_EVENT_PRIVATE'; } if ($security eq "usemask" && $vars->{'FRIENDS_EVENT_PROTECTED'}) { $var = 'FRIENDS_EVENT_PROTECTED'; } $$events .= LJ::fill_var_props($vars, $var, \%friends_event); } # end while $$events .= LJ::fill_var_props($vars, 'FRIENDS_END_DAY', {}); $friends_page{'events'} = LJ::fill_var_props($vars, 'FRIENDS_EVENTS', \%friends_events); my $item_shown = $eventnum; my $item_total = @items; my $item_hidden = $item_total - $item_shown; ### set the range property (what entries are we looking at) if ($skip) { $friends_page{'range'} = LJ::fill_var_props($vars, 'FRIENDS_RANGE_HISTORY', { "numitems" => $item_shown, "skip" => $skip, }); } else { $friends_page{'range'} = LJ::fill_var_props($vars, 'FRIENDS_RANGE_MOSTRECENT', { "numitems" => $item_shown, }); } my ($skip_f, $skip_b) = (0, 0); my %skiplinks; my $base = "$journalbase/$opts->{'view'}"; if ($group) { $base .= "/" . LJ::eurl($group); } # $linkfilter is distinct from $filter: if user has a default view, # $filter is now set according to it but we don't want it to show in the links. # $incfilter may be true even if $filter is 0: user may use filter=0 to turn # off the default group my $linkfilter = $get->{'filter'} + 0; my $incfilter = defined $get->{'filter'}; # if we've skipped down, then we can skip back up if ($skip) { $skip_f = 1; my %linkvars; $linkvars{'filter'} = $linkfilter if $incfilter; $linkvars{'show'} = $get->{'show'} if $get->{'show'} =~ /^\w+$/; my $newskip = $skip - $itemshow; if ($newskip > 0) { $linkvars{'skip'} = $newskip; } $linkvars{'dayskip'} = $get->{'dayskip'} if $get->{'dayskip'}; $skiplinks{'skipforward'} = LJ::fill_var_props($vars, 'FRIENDS_SKIP_FORWARD', { "numitems" => $itemshow, "url" => LJ::make_link($base, \%linkvars), }); } ## unless we didn't even load as many as we were expecting on this ## page, then there are more (unless there are exactly the number shown ## on the page, but who cares about that) unless ($item_total != $itemshow || $skip == $maxskip) { $skip_b = 1; my %linkvars; $linkvars{'filter'} = $linkfilter if $incfilter; $linkvars{'show'} = $get->{'show'} if $get->{'show'} =~ /^\w+$/; my $newskip = $skip + $itemshow; $linkvars{'skip'} = $newskip; $linkvars{'dayskip'} = $get->{'dayskip'} if $get->{'dayskip'}; $skiplinks{'skipbackward'} = LJ::fill_var_props($vars, 'FRIENDS_SKIP_BACKWARD', { "numitems" => $itemshow, "url" => LJ::make_link($base, \%linkvars), }); } ### if they're both on, show a spacer if ($skip_f && $skip_b) { $skiplinks{'skipspacer'} = $vars->{'FRIENDS_SKIP_SPACER'}; } ### if either are on, put skiplinks into lastn_page if ($skip_b || $skip_f) { $friends_page{'skiplinks'} = LJ::fill_var_props($vars, 'FRIENDS_SKIP_LINKS', \%skiplinks); } $$ret .= "" if ($get->{'mode'} eq "framed"); $$ret .= LJ::fill_var_props($vars, 'FRIENDS_PAGE', \%friends_page); return 1; } # the creator for the 'calendar' view: sub create_view_calendar { my ($ret, $u, $vars, $remote, $opts) = @_; my $user = $u->{'user'}; if ($u->{'journaltype'} eq "R" && $u->{'renamedto'} ne "") { $opts->{'redir'} = LJ::journal_base($u->{'renamedto'}, $opts->{'vhost'}) . "/calendar" . $opts->{'pathextra'}; return 1; } foreach ("name", "url", "urlname", "journaltitle") { LJ::text_out(\$u->{$_}); } my $get = $opts->{'getargs'}; my %calendar_page = (); $calendar_page{'name'} = LJ::ehtml($u->{'name'}); $calendar_page{'name-\'s'} = ($u->{'name'} =~ /s$/i) ? "'" : "'s"; $calendar_page{'username'} = $user; $calendar_page{'title'} = LJ::ehtml($u->{'journaltitle'} || $u->{'name'} . $calendar_page{'name-\'s'} . " Journal"); $calendar_page{'head'} = ""; if (LJ::are_hooks('s2_head_content_extra')) { $calendar_page{'head'} .= LJ::run_hook('s2_head_content_extra', $remote, $opts->{r}); } if ($u->{'opt_blockrobots'}) { $calendar_page{'head'} .= LJ::robot_meta_tags(); } if ($LJ::UNICODE) { $calendar_page{'head'} .= ''; } $calendar_page{'head'} .= $vars->{'GLOBAL_HEAD'} . "\n" . $vars->{'CALENDAR_HEAD'}; $calendar_page{'months'} = ""; if ($u->{'url'} =~ m!^https?://!) { $calendar_page{'website'} = LJ::fill_var_props($vars, 'CALENDAR_WEBSITE', { "url" => LJ::ehtml($u->{'url'}), "name" => LJ::ehtml($u->{'urlname'} || "My Website"), }); } my $journalbase = LJ::journal_base($user, $opts->{'vhost'}); $calendar_page{'urlfriends'} = "$journalbase/friends"; $calendar_page{'urllastn'} = "$journalbase/"; my $months = \$calendar_page{'months'}; my $quserid = int($u->{'userid'}); my $maxyear = 0; my $daycts = LJ::get_daycounts($u, $remote); unless ($daycts) { $opts->{'errcode'} = "nodb"; $$ret = ""; return 0; } my (%count, %dayweek); foreach my $dy (@$daycts) { my ($year, $month, $day, $count) = @$dy; # calculate day of week my $time = eval { Time::Local::timegm(0, 0, 0, $day, $month-1, $year) } || eval { Time::Local::timegm(0, 0, 0, LJ::days_in_month($month, $year), $month-1, $year) } || 0; next unless $time; my $dayweek = (gmtime($time))[6] + 1; $count{$year}->{$month}->{$day} = $count; $dayweek{$year}->{$month}->{$day} = $dayweek; if ($year > $maxyear) { $maxyear = $year; } } my @allyears = sort { $b <=> $a } keys %count; if ($vars->{'CALENDAR_SORT_MODE'} eq "forward") { @allyears = reverse @allyears; } my @years = (); my $dispyear = $get->{'year'}; # old form was /users//calendar?year=1999 # but the new form is purtier: */calendar/2001 # but the NEWER form is purtier: */2001 unless ($dispyear) { if ($opts->{'pathextra'} =~ m!^/(\d\d\d\d)/?\b!) { $dispyear = $1; } } # else... default to the year they last posted. $dispyear ||= $maxyear; # we used to show multiple years. now we only show one at a time: (hence the @years confusion) if ($dispyear) { push @years, $dispyear; } if (scalar(@allyears) > 1) { my $yearlinks = ""; foreach my $year (@allyears) { my $yy = sprintf("%02d", $year % 100); my $url = "$journalbase/$year/"; if ($year != $dispyear) { $yearlinks .= LJ::fill_var_props($vars, 'CALENDAR_YEAR_LINK', { "url" => $url, "yyyy" => $year, "yy" => $yy }); } else { $yearlinks .= LJ::fill_var_props($vars, 'CALENDAR_YEAR_DISPLAYED', { "yyyy" => $year, "yy" => $yy }); } } $calendar_page{'yearlinks'} = LJ::fill_var_props($vars, 'CALENDAR_YEAR_LINKS', { "years" => $yearlinks }); } foreach my $year (@years) { $$months .= LJ::fill_var_props($vars, 'CALENDAR_NEW_YEAR', { 'yyyy' => $year, 'yy' => substr($year, 2, 2), }); my @months = sort { $b <=> $a } keys %{$count{$year}}; if ($vars->{'CALENDAR_SORT_MODE'} eq "forward") { @months = reverse @months; } foreach my $month (@months) { my $daysinmonth = LJ::days_in_month($month, $year); # this picks a random day there were journal entries (thus, we know # the %dayweek from above) from that we go backwards and forwards # to find the rest of the days of week my $firstday = (%{$count{$year}->{$month}})[0]; # go backwards from first day my $dayweek = $dayweek{$year}->{$month}->{$firstday}; for (my $i=$firstday-1; $i>0; $i--) { if (--$dayweek < 1) { $dayweek = 7; } $dayweek{$year}->{$month}->{$i} = $dayweek; } # go forwards from first day $dayweek = $dayweek{$year}->{$month}->{$firstday}; for (my $i=$firstday+1; $i<=$daysinmonth; $i++) { if (++$dayweek > 7) { $dayweek = 1; } $dayweek{$year}->{$month}->{$i} = $dayweek; } my %calendar_month = (); $calendar_month{'monlong'} = LJ::Lang::month_long($month); $calendar_month{'monshort'} = LJ::Lang::month_short($month); $calendar_month{'yyyy'} = $year; $calendar_month{'yy'} = substr($year, 2, 2); $calendar_month{'weeks'} = ""; $calendar_month{'urlmonthview'} = sprintf("$journalbase/%04d/%02d/", $year, $month); my $weeks = \$calendar_month{'weeks'}; my %calendar_week = (); $calendar_week{'emptydays_beg'} = ""; $calendar_week{'emptydays_end'} = ""; $calendar_week{'days'} = ""; # start the first row and check for its empty spaces my $rowopen = 1; if ($dayweek{$year}->{$month}->{1} != 1) { my $spaces = $dayweek{$year}->{$month}->{1} - 1; $calendar_week{'emptydays_beg'} = LJ::fill_var_props($vars, 'CALENDAR_EMPTY_DAYS', { 'numempty' => $spaces }); } # make the days! my $days = \$calendar_week{'days'}; for (my $i=1; $i<=$daysinmonth; $i++) { $count{$year}->{$month}->{$i} += 0; if (! $rowopen) { $rowopen = 1; } my %calendar_day = (); $calendar_day{'d'} = $i; $calendar_day{'eventcount'} = $count{$year}->{$month}->{$i}; if ($count{$year}->{$month}->{$i}) { $calendar_day{'dayevent'} = LJ::fill_var_props($vars, 'CALENDAR_DAY_EVENT', { 'eventcount' => $count{$year}->{$month}->{$i}, 'dayurl' => "$journalbase/" . sprintf("%04d/%02d/%02d/", $year, $month, $i), }); } else { $calendar_day{'daynoevent'} = $vars->{'CALENDAR_DAY_NOEVENT'}; } $$days .= LJ::fill_var_props($vars, 'CALENDAR_DAY', \%calendar_day); if ($dayweek{$year}->{$month}->{$i} == 7) { $$weeks .= LJ::fill_var_props($vars, 'CALENDAR_WEEK', \%calendar_week); $rowopen = 0; $calendar_week{'emptydays_beg'} = ""; $calendar_week{'emptydays_end'} = ""; $calendar_week{'days'} = ""; } } # if rows is still open, we have empty spaces if ($rowopen) { if ($dayweek{$year}->{$month}->{$daysinmonth} != 7) { my $spaces = 7 - $dayweek{$year}->{$month}->{$daysinmonth}; $calendar_week{'emptydays_end'} = LJ::fill_var_props($vars, 'CALENDAR_EMPTY_DAYS', { 'numempty' => $spaces }); } $$weeks .= LJ::fill_var_props($vars, 'CALENDAR_WEEK', \%calendar_week); } $$months .= LJ::fill_var_props($vars, 'CALENDAR_MONTH', \%calendar_month); } # end foreach months } # end foreach years ######## new code $$ret .= LJ::fill_var_props($vars, 'CALENDAR_PAGE', \%calendar_page); return 1; } # the creator for the 'day' view: sub create_view_day { my ($ret, $u, $vars, $remote, $opts) = @_; my $sth; my $user = $u->{'user'}; if ($u->{'journaltype'} eq "R" && $u->{'renamedto'} ne "") { $opts->{'redir'} = LJ::journal_base($u->{'renamedto'}, $opts->{'vhost'}) . "/day" . $opts->{'pathextra'}; return 1; } foreach ("name", "url", "urlname", "journaltitle") { LJ::text_out(\$u->{$_}); } my %day_page = (); $day_page{'username'} = $user; $day_page{'head'} = ""; if (LJ::are_hooks('s2_head_content_extra')) { $day_page{'head'} .= LJ::run_hook('s2_head_content_extra', $remote, $opts->{r}); } if ($u->{'opt_blockrobots'}) { $day_page{'head'} .= LJ::robot_meta_tags(); } if ($LJ::UNICODE) { $day_page{'head'} .= ''; } $day_page{'head'} .= $vars->{'GLOBAL_HEAD'} . "\n" . $vars->{'DAY_HEAD'}; $day_page{'name'} = LJ::ehtml($u->{'name'}); $day_page{'name-\'s'} = ($u->{'name'} =~ /s$/i) ? "'" : "'s"; $day_page{'title'} = LJ::ehtml($u->{'journaltitle'} || $u->{'name'} . $day_page{'name-\'s'} . " Journal"); if ($u->{'url'} =~ m!^https?://!) { $day_page{'website'} = LJ::fill_var_props($vars, 'DAY_WEBSITE', { "url" => LJ::ehtml($u->{'url'}), "name" => LJ::ehtml($u->{'urlname'} || "My Website"), }); } my $journalbase = LJ::journal_base($user, $opts->{'vhost'}); $day_page{'urlfriends'} = "$journalbase/friends"; $day_page{'urlcalendar'} = "$journalbase/calendar"; $day_page{'urllastn'} = "$journalbase/"; my $initpagedates = 0; my $get = $opts->{'getargs'}; my $month = $get->{'month'}; my $day = $get->{'day'}; my $year = $get->{'year'}; my @errors = (); if ($opts->{'pathextra'} =~ m!^(?:/day)?/(\d\d\d\d)/(\d\d)/(\d\d)\b!) { ($month, $day, $year) = ($2, $3, $1); } if ($year !~ /^\d+$/) { push @errors, "Corrupt or non-existant year."; } if ($month !~ /^\d+$/) { push @errors, "Corrupt or non-existant month."; } if ($day !~ /^\d+$/) { push @errors, "Corrupt or non-existant day."; } if ($month < 1 || $month > 12 || int($month) != $month) { push @errors, "Invalid month."; } if ($year < 1970 || $year > 2038 || int($year) != $year) { push @errors, "Invalid year: $year"; } if ($day < 1 || $day > 31 || int($day) != $day) { push @errors, "Invalid day."; } if (scalar(@errors)==0 && $day > LJ::days_in_month($month, $year)) { push @errors, "That month doesn't have that many days."; } if (@errors) { $$ret .= "Errors occurred processing this page:\n\n"; return 0; } my $logdb = LJ::get_cluster_reader($u); unless ($logdb) { $opts->{'errcode'} = "nodb"; $$ret = ""; return 0; } my $optDESC = $vars->{'DAY_SORT_MODE'} eq "reverse" ? "DESC" : ""; my $secwhere = "AND security='public'"; my $viewall = 0; my $viewsome = 0; if ($remote) { # do they have the viewall priv? if ($get->{'viewall'} && LJ::check_priv($remote, "canview")) { LJ::statushistory_add($u->{'userid'}, $remote->{'userid'}, "viewall", "day: $user, statusvis: $u->{'statusvis'}"); $viewall = LJ::check_priv($remote, 'canview', '*'); $viewsome = $viewall || LJ::check_priv($remote, 'canview', 'suspended'); } if ($remote->{'userid'} == $u->{'userid'} || $viewall) { $secwhere = ""; # see everything } elsif ($remote->{'journaltype'} eq 'P' || $remote->{'journaltype'} eq 'I') { my $gmask = LJ::get_groupmask($u, $remote); $secwhere = "AND (security='public' OR (security='usemask' AND allowmask & $gmask))" if $gmask; } } # load the log items my $dateformat = "%a %W %b %M %y %Y %c %m %e %d %D %p %i %l %h %k %H"; $sth = $logdb->prepare("SELECT jitemid AS itemid, posterid, security, ". " DATE_FORMAT(eventtime, \"$dateformat\") AS 'alldatepart', anum " . "FROM log2 " . "WHERE journalid=? AND year=? AND month=? AND day=? $secwhere " . "ORDER BY eventtime $optDESC, logtime $optDESC LIMIT 200"); $sth->execute($u->{'userid'}, $year, $month, $day); my @items; push @items, $_ while $_ = $sth->fetchrow_hashref; my @itemids = map { $_->{'itemid'} } @items; # load 'opt_ljcut_disable_lastn' prop for $remote. LJ::load_user_props($remote, "opt_ljcut_disable_lastn"); ### load the log properties LJ::fill_items_with_text_props(\@items, $u); my %posteru = (); # map posterids to u objects LJ::load_userids_multiple([map { $_->{'posterid'}, \$posteru{$_->{'posterid'}} } @items], [$u]); my $events = ""; ENTRY: foreach my $item (@items) { my ($itemid, $posterid, $security, $alldatepart, $anum) = map { $item->{$_} } qw(itemid posterid security alldatepart anum); next ENTRY if $posteru{$posterid} && $posteru{$posterid}->{'statusvis'} eq 'S' && !$viewsome; my $props = $item->{'props'}; my $replycount = $props->{'replycount'}; my $subject = $item->{'text'}->[0]; my $event = $item->{'text'}->[1]; my %day_date_format = LJ::alldateparts_to_hash($alldatepart); unless ($initpagedates++) { foreach (qw(dayshort daylong monshort monlong yy yyyy m mm d dd dth)) { $day_page{$_} = $day_date_format{$_}; } } my %day_event = (); $day_event{'itemid'} = $itemid; $day_event{'datetime'} = LJ::fill_var_props($vars, 'DAY_DATE_FORMAT', \%day_date_format); if ($subject ne "") { LJ::CleanHTML::clean_subject(\$subject); $day_event{'subject'} = LJ::fill_var_props($vars, 'DAY_SUBJECT', { "subject" => $subject, }); } my $ditemid = $itemid*256 + $anum; my $itemargs = "journal=$user&itemid=$ditemid"; $day_event{'itemargs'} = $itemargs; LJ::CleanHTML::clean_event(\$event, { 'preformatted' => $props->{'opt_preformatted'}, 'cuturl' => LJ::item_link($u, $itemid, $anum), 'ljcut_disable' => $remote->{'opt_ljcut_disable_lastn'}, }); LJ::expand_embedded($u, $ditemid, $remote, \$event); $day_event{'event'} = $event; if ($u->{'opt_showtalklinks'} eq "Y" && ! $props->{'opt_nocomments'} || $props->{'syn_link'} ) { my $nc; $nc = "nc=$replycount" if $replycount; # && $remote && $remote->{'opt_nctalklinks'}; my $permalink = "$journalbase/$ditemid.html"; my $posturl = LJ::Talk::talkargs($permalink, "mode=reply"); my $readurl = LJ::Talk::talkargs($permalink, $nc); my $dispreadlink = $replycount || ($props->{'hasscreened'} && ($remote->{'user'} eq $user || LJ::can_manage($remote, $u))); if ($ljr && $props->{'syn_link'}) { my $rs = LJR::Distributed::match_remote_server($props->{'syn_link'}); if ($rs->{"servertype"} eq "lj") { $posturl = $props->{'syn_link'} . "?mode=reply"; $dispreadlink = 1; $readurl = $props->{'syn_link'}; $replycount = 'Read'; } else { $posturl = $props->{'syn_link'}; $dispreadlink = 0; $replycount = undef; } } $day_event{'talklinks'} = LJ::fill_var_props($vars, 'DAY_TALK_LINKS', { 'itemid' => $ditemid, 'itemargs' => $itemargs, 'urlpost' => $posturl, 'urlread' => $readurl, 'messagecount' => $replycount, 'readlink' => $dispreadlink ? LJ::fill_var_props($vars, 'DAY_TALK_READLINK', { 'urlread' => $readurl, 'messagecount' => $replycount, 'mc-plural-s' => $replycount == 1 ? "" : "s", 'mc-plural-es' => $replycount == 1 ? "" : "es", 'mc-plural-ies' => $replycount == 1 ? "y" : "ies", }) : "", }); } ## current stuff LJ::prepare_currents({ 'props' => $props, 'vars' => $vars, 'prefix' => "DAY", 'event' => \%day_event, 'user' => $u, }); my $var = 'DAY_EVENT'; if ($security eq "private" && $vars->{'DAY_EVENT_PRIVATE'}) { $var = 'DAY_EVENT_PRIVATE'; } if ($security eq "usemask" && $vars->{'DAY_EVENT_PROTECTED'}) { $var = 'DAY_EVENT_PROTECTED'; } $events .= LJ::fill_var_props($vars, $var, \%day_event); } if (! $initpagedates) { # if no entries were on that day, we haven't populated the time shit! # FIXME: don't use the database for this. it can be done in Perl. my $dbr = LJ::get_db_reader(); $sth = $dbr->prepare("SELECT DATE_FORMAT('$year-$month-$day', '%a %W %b %M %y %Y %c %m %e %d %D') AS 'alldatepart'"); $sth->execute; my @dateparts = split(/ /, $sth->fetchrow_arrayref->[0]); foreach (qw(dayshort daylong monshort monlong yy yyyy m mm d dd dth)) { $day_page{$_} = shift @dateparts; } $day_page{'events'} = LJ::fill_var_props($vars, 'DAY_NOEVENTS', {}); } else { $day_page{'events'} = LJ::fill_var_props($vars, 'DAY_EVENTS', { 'events' => $events }); $events = ""; # free some memory maybe } # calculate previous day my $pdyear = $year; my $pdmonth = $month; my $pdday = $day-1; if ($pdday < 1) { if (--$pdmonth < 1) { $pdmonth = 12; $pdyear--; } $pdday = LJ::days_in_month($pdmonth, $pdyear); } # calculate next day my $nxyear = $year; my $nxmonth = $month; my $nxday = $day+1; if ($nxday > LJ::days_in_month($nxmonth, $nxyear)) { $nxday = 1; if (++$nxmonth > 12) { ++$nxyear; $nxmonth=1; } } $day_page{'prevday_url'} = "$journalbase/" . sprintf("%04d/%02d/%02d/", $pdyear, $pdmonth, $pdday); $day_page{'nextday_url'} = "$journalbase/" . sprintf("%04d/%02d/%02d/", $nxyear, $nxmonth, $nxday); $$ret .= LJ::fill_var_props($vars, 'DAY_PAGE', \%day_page); return 1; } 1;