3640 lines
137 KiB
Perl
3640 lines
137 KiB
Perl
|
|
#!/usr/bin/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.
|
||
|
|
|
||
|
|
package LJ;
|
||
|
|
use strict;
|
||
|
|
|
||
|
|
use Carp;
|
||
|
|
use POSIX;
|
||
|
|
use Digest::MD5;
|
||
|
|
use Digest::SHA1;
|
||
|
|
|
||
|
|
use DW::Auth::Challenge;
|
||
|
|
use DW::External::Site;
|
||
|
|
use DW::Request;
|
||
|
|
use DW::Formats;
|
||
|
|
use LJ::Utils qw(rand_chars);
|
||
|
|
use LJ::Global::Constants;
|
||
|
|
use LJ::Event;
|
||
|
|
use LJ::Subscription::Pending;
|
||
|
|
use LJ::Directory::Search;
|
||
|
|
use LJ::Directory::Constraint;
|
||
|
|
use LJ::PageStats;
|
||
|
|
use LJ::JSON;
|
||
|
|
|
||
|
|
# <LJFUNC>
|
||
|
|
# name: LJ::img
|
||
|
|
# des: Returns an HTML <img> or <input> tag to an named image
|
||
|
|
# code, which each site may define with a different image file with
|
||
|
|
# its own dimensions. This prevents hard-coding filenames & sizes
|
||
|
|
# into the source. The real image data is stored in LJ::Img, which
|
||
|
|
# has default values provided in cgi-bin/LJ/Global/Img.pm but can be
|
||
|
|
# overridden in cgi-bin/LJ/Local/Img.pm or etc/config.pl.
|
||
|
|
# args: imagecode, type?, attrs?
|
||
|
|
# des-imagecode: The unique string key to reference the image. Not a filename,
|
||
|
|
# but the purpose or location of the image.
|
||
|
|
# des-type: By default, the tag returned is an <img> tag, but if 'type'
|
||
|
|
# is "input", then an input tag is returned.
|
||
|
|
# des-attrs: Optional hashref of other attributes. If this isn't a hashref,
|
||
|
|
# then it's assumed to be a scalar for the 'name' attribute for
|
||
|
|
# input controls.
|
||
|
|
# </LJFUNC>
|
||
|
|
sub img {
|
||
|
|
my ( $ic, $type, $attr ) = @_;
|
||
|
|
$type //= ''; # Type is either "" or "input"
|
||
|
|
|
||
|
|
my ( $attrs, $alt ) = ( '', '' );
|
||
|
|
if ($attr) {
|
||
|
|
if ( ref $attr eq "HASH" ) {
|
||
|
|
if ( exists $attr->{alt} ) {
|
||
|
|
$alt = LJ::ehtml( $attr->{alt} );
|
||
|
|
delete $attr->{alt};
|
||
|
|
}
|
||
|
|
$attrs .= " $_=\"" . LJ::ehtml( $attr->{$_} || '' ) . "\"" foreach keys %$attr;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
$attrs = " id=\"$attr\"";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
my $i = $LJ::Img::img{$ic};
|
||
|
|
$alt ||= LJ::Lang::string_exists( $i->{alt} ) ? LJ::Lang::ml( $i->{alt} ) : $i->{alt};
|
||
|
|
if ( $type eq "" ) {
|
||
|
|
return
|
||
|
|
"<img src=\"$LJ::IMGPREFIX$i->{src}\" width=\"$i->{width}\" "
|
||
|
|
. "height=\"$i->{height}\" alt=\"$alt\" title=\"$alt\" "
|
||
|
|
. "border='0'$attrs />";
|
||
|
|
}
|
||
|
|
if ( $type eq "input" ) {
|
||
|
|
return
|
||
|
|
"<input type=\"image\" src=\"$LJ::IMGPREFIX$i->{'src'}\" "
|
||
|
|
. "width=\"$i->{'width'}\" height=\"$i->{'height'}\" title=\"$alt\" "
|
||
|
|
. "alt=\"$alt\" border='0'$attrs />";
|
||
|
|
}
|
||
|
|
return "<b>XXX</b>";
|
||
|
|
}
|
||
|
|
|
||
|
|
# <LJFUNC>
|
||
|
|
# name: LJ::date_to_view_links
|
||
|
|
# class: component
|
||
|
|
# des: Returns HTML of date with links to user's journal.
|
||
|
|
# args: u, date
|
||
|
|
# des-date: date in yyyy-mm-dd form.
|
||
|
|
# returns: HTML with yyyy, mm, and dd all links to respective views.
|
||
|
|
# </LJFUNC>
|
||
|
|
sub date_to_view_links {
|
||
|
|
my ( $u, $date ) = @_;
|
||
|
|
return unless $date =~ /^(\d\d\d\d)-(\d\d)-(\d\d)/;
|
||
|
|
|
||
|
|
my ( $y, $m, $d ) = ( $1, $2, $3 );
|
||
|
|
my $base = $u->journal_base;
|
||
|
|
|
||
|
|
my $ret;
|
||
|
|
$ret .= "<a href=\"$base/$y/\">$y</a>-";
|
||
|
|
$ret .= "<a href=\"$base/$y/$m/\">$m</a>-";
|
||
|
|
$ret .= "<a href=\"$base/$y/$m/$d/\">$d</a>";
|
||
|
|
return $ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
# <LJFUNC>
|
||
|
|
# name: LJ::auto_linkify
|
||
|
|
# des: Takes a plain-text string and changes URLs into <a href> tags (auto-linkification).
|
||
|
|
# args: str
|
||
|
|
# des-str: The string to perform auto-linkification on.
|
||
|
|
# returns: The auto-linkified text.
|
||
|
|
# </LJFUNC>
|
||
|
|
sub auto_linkify {
|
||
|
|
my $str = shift;
|
||
|
|
my $match = sub {
|
||
|
|
my $str = shift;
|
||
|
|
if ( $str =~ /^(.*?)(&(#39|quot|lt|gt)(;.*)?)$/ ) {
|
||
|
|
return "<a href='$1'>$1</a>$2";
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
return "<a href='$str'>$str</a>";
|
||
|
|
}
|
||
|
|
};
|
||
|
|
$str =~ s!(https?://[^\s\'\"\<\>]+[a-zA-Z0-9_/&=\-])! $match->( $1 ); !ge
|
||
|
|
if defined $str;
|
||
|
|
|
||
|
|
return $str;
|
||
|
|
}
|
||
|
|
|
||
|
|
# return 1 if URL is a safe stylesheet that S1/S2/etc can pull in.
|
||
|
|
# return 0 to reject the link tag
|
||
|
|
# return a URL to rewrite the stylesheet URL
|
||
|
|
# $href will always be present. $host and $path may not.
|
||
|
|
sub valid_stylesheet_url {
|
||
|
|
my ( $href, $host, $path ) = @_;
|
||
|
|
unless ( $host && $path ) {
|
||
|
|
return 0 unless $href =~ m!^https?://([^/]+?)(/.*)$!;
|
||
|
|
( $host, $path ) = ( $1, $2 );
|
||
|
|
}
|
||
|
|
|
||
|
|
my $cleanit = sub {
|
||
|
|
|
||
|
|
# allow tag, if we're doing no css cleaning
|
||
|
|
return 1 unless LJ::is_enabled('css_cleaner');
|
||
|
|
|
||
|
|
# remove tag, if we have no CSSPROXY configured
|
||
|
|
return 0 unless $LJ::CSSPROXY;
|
||
|
|
|
||
|
|
# rewrite tag for CSS cleaning
|
||
|
|
return "$LJ::CSSPROXY?u=" . LJ::eurl($href);
|
||
|
|
};
|
||
|
|
|
||
|
|
return 1 if $LJ::TRUSTED_CSS_HOST{$host};
|
||
|
|
return $cleanit->() unless $host =~ /\Q$LJ::DOMAIN\E$/i;
|
||
|
|
|
||
|
|
# let users use system stylesheets.
|
||
|
|
return 1
|
||
|
|
if $host eq $LJ::DOMAIN
|
||
|
|
|| $host eq $LJ::DOMAIN_WEB
|
||
|
|
|| $href =~ /^\Q$LJ::STATPREFIX\E/;
|
||
|
|
|
||
|
|
# S2 stylesheets:
|
||
|
|
return 1 if $path =~ m!^(/~\w+|/users/\w+|/\w+)?/res/(\d+)/stylesheet(\?\d+)?$!;
|
||
|
|
|
||
|
|
# unknown, reject.
|
||
|
|
return $cleanit->();
|
||
|
|
}
|
||
|
|
|
||
|
|
# <LJFUNC>
|
||
|
|
# name: LJ::make_authas_select
|
||
|
|
# des: Given a u object and some options, determines which users the given user
|
||
|
|
# can switch to. If the list exists, returns a select list and a submit
|
||
|
|
# button with labels. Otherwise returns a hidden element.
|
||
|
|
# returns: string of HTML elements
|
||
|
|
# args: u, opts?
|
||
|
|
# des-opts: Optional. Valid keys are:
|
||
|
|
# 'authas' - current user, gets selected in drop-down;
|
||
|
|
# 'label' - label to go before form elements;
|
||
|
|
# 'button' - button label for submit button;
|
||
|
|
# 'type' - journaltype (affects label & list filtering)
|
||
|
|
# 'selectonly' - 1 for just menu items, otherwise entire HTML block
|
||
|
|
# 'foundation' - 1 for Foundation HTML, otherwise legacy HTML
|
||
|
|
# others - arguments to pass to $u->get_authas_list.
|
||
|
|
# </LJFUNC>
|
||
|
|
sub make_authas_select {
|
||
|
|
my ( $u, $opts ) = @_; # type, authas, label, button
|
||
|
|
|
||
|
|
my $authas = $opts->{authas} || $u->user;
|
||
|
|
my $button = $opts->{button} || $BML::ML{'web.authas.btn'};
|
||
|
|
my $label = $opts->{label} || $BML::ML{'web.authas.select.label'};
|
||
|
|
|
||
|
|
my $foundation = $opts->{foundation} || 0;
|
||
|
|
|
||
|
|
my @list = $u->get_authas_list($opts);
|
||
|
|
|
||
|
|
# only do most of form if there are options to select from
|
||
|
|
if ( @list > 1 || $list[0] ne $u->user ) {
|
||
|
|
my $menu = LJ::html_select(
|
||
|
|
{
|
||
|
|
name => 'authas',
|
||
|
|
selected => $authas,
|
||
|
|
class => 'hideable',
|
||
|
|
id => 'authas',
|
||
|
|
style => $foundation ? 'width: 100%' : '',
|
||
|
|
},
|
||
|
|
map { $_, $_ } @list
|
||
|
|
);
|
||
|
|
|
||
|
|
my $ret = '';
|
||
|
|
if ( $opts->{selectonly} ) {
|
||
|
|
$ret = $menu;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
$ret = $foundation
|
||
|
|
? q{<div class='row collapse'><div class='columns small-3 medium-1'><label class='inline'>}
|
||
|
|
. $label
|
||
|
|
. q{</label></div>}
|
||
|
|
. q{<div class='columns small-9 medium-11'><div class='row'>}
|
||
|
|
. q{<div class="columns small-8 medium-4">}
|
||
|
|
. $menu
|
||
|
|
. q{</div>}
|
||
|
|
. q{<div class='columns small-4 medium-2 end'>}
|
||
|
|
. LJ::html_submit( undef, $button, { class => "secondary button" } )
|
||
|
|
. q{</div>}
|
||
|
|
. q{</div></div>}
|
||
|
|
. q{</div>}
|
||
|
|
|
||
|
|
# else not foundation
|
||
|
|
: "<br/>"
|
||
|
|
. LJ::Lang::ml( 'web.authas.select',
|
||
|
|
{ menu => $menu, username => LJ::ljuser($authas) } )
|
||
|
|
. " "
|
||
|
|
. LJ::html_submit( undef, $button )
|
||
|
|
. "<br/><br/>\n";
|
||
|
|
}
|
||
|
|
|
||
|
|
return $ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
# no communities to choose from, give the caller a hidden
|
||
|
|
return LJ::html_hidden( authas => $authas );
|
||
|
|
}
|
||
|
|
|
||
|
|
# <LJFUNC>
|
||
|
|
# name: LJ::help_icon
|
||
|
|
# des: Returns BML to show a help link/icon given a help topic, or nothing
|
||
|
|
# if the site hasn't defined a URL for that topic. Optional arguments
|
||
|
|
# include HTML/BML to place before and after the link/icon, should it
|
||
|
|
# be returned.
|
||
|
|
# args: topic, pre?, post?
|
||
|
|
# des-topic: Help topic key.
|
||
|
|
# See etc/config-local.pl, or [special[helpurls]] for examples.
|
||
|
|
# des-pre: HTML/BML to place before the help icon.
|
||
|
|
# des-post: HTML/BML to place after the help icon.
|
||
|
|
# </LJFUNC>
|
||
|
|
sub help_icon {
|
||
|
|
my $topic = shift;
|
||
|
|
my $pre = shift;
|
||
|
|
my $post = shift;
|
||
|
|
return "" unless ( defined $LJ::HELPURL{$topic} );
|
||
|
|
return "$pre<?help $LJ::HELPURL{$topic} help?>$post";
|
||
|
|
}
|
||
|
|
|
||
|
|
# like help_icon, but no BML.
|
||
|
|
sub help_icon_html {
|
||
|
|
my $topic = shift;
|
||
|
|
my $url = $LJ::HELPURL{$topic} or return "";
|
||
|
|
my $pre = shift || "";
|
||
|
|
my $post = shift || "";
|
||
|
|
return
|
||
|
|
"$pre<a href=\"$url\" class=\"helplink\" target=\"_blank\">"
|
||
|
|
. LJ::img( 'help', '' )
|
||
|
|
. "</a>$post";
|
||
|
|
}
|
||
|
|
|
||
|
|
# <LJFUNC>
|
||
|
|
# name: LJ::bad_input
|
||
|
|
# des: Returns common BML for reporting form validation errors in
|
||
|
|
# a bulleted list.
|
||
|
|
# returns: BML showing errors.
|
||
|
|
# args: error*
|
||
|
|
# des-error: A list of errors
|
||
|
|
# </LJFUNC>
|
||
|
|
sub bad_input {
|
||
|
|
my @errors = @_;
|
||
|
|
my $ret = "";
|
||
|
|
$ret .= "<?badcontent?>\n<ul>\n";
|
||
|
|
foreach my $ei (@errors) {
|
||
|
|
my $err = LJ::errobj($ei) or next;
|
||
|
|
$ret .= $err->as_bullets;
|
||
|
|
}
|
||
|
|
$ret .= "</ul>\n";
|
||
|
|
return $ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
# <LJFUNC>
|
||
|
|
# name: LJ::error_list
|
||
|
|
# des: Returns an error bar with bulleted list of errors.
|
||
|
|
# returns: BML showing errors.
|
||
|
|
# args: error*
|
||
|
|
# des-error: A list of errors
|
||
|
|
# </LJFUNC>
|
||
|
|
sub error_list {
|
||
|
|
|
||
|
|
# FIXME: retrofit like bad_input above? merge? make aliases for each other?
|
||
|
|
my @errors = @_;
|
||
|
|
my $ret;
|
||
|
|
$ret .= "<?errorbar ";
|
||
|
|
$ret .= "<strong>";
|
||
|
|
$ret .= BML::ml('error.procrequest');
|
||
|
|
$ret .= "</strong><ul>";
|
||
|
|
|
||
|
|
foreach my $ei (@errors) {
|
||
|
|
my $err = LJ::errobj($ei) or next;
|
||
|
|
$ret .= $err->as_bullets;
|
||
|
|
}
|
||
|
|
$ret .= " </ul> errorbar?>";
|
||
|
|
return $ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
# <LJFUNC>
|
||
|
|
# name: LJ::error_noremote
|
||
|
|
# des: Returns an error telling the user to log in.
|
||
|
|
# returns: Translation string "error.notloggedin"
|
||
|
|
# </LJFUNC>
|
||
|
|
sub error_noremote {
|
||
|
|
return "<?needlogin?>";
|
||
|
|
}
|
||
|
|
|
||
|
|
# <LJFUNC>
|
||
|
|
# name: LJ::warning_list
|
||
|
|
# des: Returns a warning bar with bulleted list of warnings.
|
||
|
|
# returns: BML showing warnings
|
||
|
|
# args: warnings*
|
||
|
|
# des-warnings: A list of warnings
|
||
|
|
# </LJFUNC>
|
||
|
|
sub warning_list {
|
||
|
|
my @warnings = @_;
|
||
|
|
my $ret;
|
||
|
|
|
||
|
|
$ret .= "<?warningbar ";
|
||
|
|
$ret .= "<strong>";
|
||
|
|
$ret .= BML::ml('label.warning');
|
||
|
|
$ret .= "</strong><ul>";
|
||
|
|
|
||
|
|
foreach (@warnings) {
|
||
|
|
$ret .= "<li>$_</li>";
|
||
|
|
}
|
||
|
|
$ret .= " </ul> warningbar?>";
|
||
|
|
return $ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
# <LJFUNC>
|
||
|
|
# name: LJ::did_post
|
||
|
|
# des: Cookies should only show pages which make no action.
|
||
|
|
# When an action is being made, check the request coming
|
||
|
|
# from the remote user is a POST request.
|
||
|
|
# info: When web pages are using cookie authentication, you can't just trust that
|
||
|
|
# the remote user wants to do the action they're requesting. It's way too
|
||
|
|
# easy for people to force other people into making GET requests to
|
||
|
|
# a server. What if a user requested http://server/delete_all_journal.bml,
|
||
|
|
# and that URL checked the remote user and immediately deleted the whole
|
||
|
|
# journal? Now anybody has to do is embed that address in an image
|
||
|
|
# tag and a lot of people's journals will be deleted without them knowing.
|
||
|
|
# Cookies should only show pages which make no action. When an action is
|
||
|
|
# being made, check that it's a POST request.
|
||
|
|
# returns: true if REQUEST_METHOD == "POST"
|
||
|
|
# </LJFUNC>
|
||
|
|
sub did_post {
|
||
|
|
return ( BML::get_method() eq "POST" );
|
||
|
|
}
|
||
|
|
|
||
|
|
# <LJFUNC>
|
||
|
|
# name: LJ::robot_meta_tags
|
||
|
|
# des: Returns meta tags to instruct a robot/crawler to not index or follow links.
|
||
|
|
# returns: A string with appropriate meta tags
|
||
|
|
# </LJFUNC>
|
||
|
|
sub robot_meta_tags {
|
||
|
|
return "<meta name=\"robots\" content=\"noindex, nofollow, noarchive\" />\n"
|
||
|
|
. "<meta name=\"googlebot\" content=\"noindex, nofollow, noarchive, nosnippet\" />\n";
|
||
|
|
}
|
||
|
|
|
||
|
|
sub paging_bar {
|
||
|
|
my ( $page, $pages, $opts ) = @_;
|
||
|
|
|
||
|
|
my $self_link = $opts->{self_link}
|
||
|
|
|| sub { LJ::page_change_getargs( page => $_[0] ) };
|
||
|
|
|
||
|
|
my $href_opts = $opts->{href_opts} || sub { '' };
|
||
|
|
|
||
|
|
my $nav = '';
|
||
|
|
return $nav unless $pages && $pages > 1;
|
||
|
|
|
||
|
|
$nav .= "<p style='font-weight: bolder; margin: 0 0 .5em 0'>";
|
||
|
|
$nav .= LJ::Lang::ml( 'ljlib.pageofpages', { page => $page, total => $pages } );
|
||
|
|
$nav .= "</p>\n";
|
||
|
|
|
||
|
|
my $linkify = sub {
|
||
|
|
"<a href='" . $self_link->( $_[0] ) . "'" . $href_opts->( $_[0] ) . ">$_[1]</a>\n";
|
||
|
|
};
|
||
|
|
|
||
|
|
my ( $left, $right ) = ( "<b><<</b>", "<b>>></b>" );
|
||
|
|
$left = $linkify->( $page - 1, $left ) if $page > 1;
|
||
|
|
$right = $linkify->( $page + 1, $right ) if $page < $pages;
|
||
|
|
|
||
|
|
my @pagelinks;
|
||
|
|
|
||
|
|
for ( my $i = 1 ; $i <= $pages ; $i++ ) {
|
||
|
|
my $link = "[$i]";
|
||
|
|
$link = ( $i != $page ) ? $linkify->( $i, $link ) : "<b>$link</b>";
|
||
|
|
push @pagelinks, "<br />" if $i > 10 && ( $i == 11 || $i % 10 == 0 );
|
||
|
|
push @pagelinks, $link;
|
||
|
|
}
|
||
|
|
|
||
|
|
$nav .= "$left ";
|
||
|
|
$nav .= "<span style='text-align: center'>";
|
||
|
|
$nav .= join ' ', @pagelinks;
|
||
|
|
$nav .= "</span>";
|
||
|
|
$nav .= " $right";
|
||
|
|
|
||
|
|
return
|
||
|
|
"<div class='action-box'><div class='inner'>$nav</div></div><div class='clear-floats'></div>\n";
|
||
|
|
}
|
||
|
|
|
||
|
|
=head2 C<< LJ::page_change_getargs( %args ) >>
|
||
|
|
Returns the current URL with a modified list of GET arguments.
|
||
|
|
=cut
|
||
|
|
|
||
|
|
sub page_change_getargs {
|
||
|
|
my %args = @_;
|
||
|
|
my %cu_opts = ( keep_args => 1, no_blank => 1 );
|
||
|
|
|
||
|
|
# specified args will override keep_args
|
||
|
|
return LJ::create_url( undef, args => \%args, %cu_opts );
|
||
|
|
}
|
||
|
|
|
||
|
|
=head2 C<< LJ::paging( $listref, $page, $pagesize ) >>
|
||
|
|
Drop-in replacement for BML::paging in non-BML context.
|
||
|
|
=cut
|
||
|
|
|
||
|
|
sub paging {
|
||
|
|
my ( $listref, $page, $pagesize ) = @_;
|
||
|
|
$page = 1 unless $page && $page == int $page;
|
||
|
|
return unless $pagesize; # let's not divide by zero
|
||
|
|
my @items = @{$listref};
|
||
|
|
my %self;
|
||
|
|
|
||
|
|
my $newurl = sub {
|
||
|
|
|
||
|
|
# replaces BML::page_newurl
|
||
|
|
return LJ::page_change_getargs( page => $_[0] );
|
||
|
|
};
|
||
|
|
|
||
|
|
$self{itemcount} = scalar @items;
|
||
|
|
|
||
|
|
$self{pages} = $self{itemcount} / $pagesize;
|
||
|
|
$self{pages} = int( $self{pages} ) + 1
|
||
|
|
if $self{pages} != int( $self{pages} ); # round up any fraction
|
||
|
|
|
||
|
|
$page = 1 if $page < 1;
|
||
|
|
$page = $self{pages} if $page > $self{pages};
|
||
|
|
$self{page} = $page;
|
||
|
|
|
||
|
|
$self{itemfirst} = $pagesize * ( $page - 1 ) + 1;
|
||
|
|
$self{itemlast} = $pagesize * $page;
|
||
|
|
$self{itemlast} = $self{itemcount} if $self{pages} == $page;
|
||
|
|
|
||
|
|
my @range = ( $self{itemfirst} - 1 ) .. ( $self{itemlast} - 1 );
|
||
|
|
$self{items} = [ @items[@range] ];
|
||
|
|
|
||
|
|
my ( $prev, $next ) = ( $newurl->( $page - 1 ), $newurl->( $page + 1 ) );
|
||
|
|
$self{backlink} = "<a href=\"$prev\"><<<</a>" unless $page == 1;
|
||
|
|
$self{nextlink} = "<a href=\"$next\">>>></a>" unless $page == $self{pages};
|
||
|
|
|
||
|
|
return %self;
|
||
|
|
}
|
||
|
|
|
||
|
|
# <LJFUNC>
|
||
|
|
# class: web
|
||
|
|
# name: LJ::make_cookie
|
||
|
|
# des: Prepares cookie header lines.
|
||
|
|
# returns: An array of cookie lines.
|
||
|
|
# args: name, value, expires, path?, domain?
|
||
|
|
# des-name: The name of the cookie.
|
||
|
|
# des-value: The value to set the cookie to.
|
||
|
|
# des-expires: The time (in seconds) when the cookie is supposed to expire.
|
||
|
|
# Set this to 0 to expire when the browser closes. Set it to
|
||
|
|
# undef to delete the cookie.
|
||
|
|
# des-path: The directory path to bind the cookie to.
|
||
|
|
# des-domain: The domain (or domains) to bind the cookie to.
|
||
|
|
# </LJFUNC>
|
||
|
|
sub make_cookie {
|
||
|
|
my ( $name, $value, $expires, $path, $domain ) = @_;
|
||
|
|
my $cookie = "";
|
||
|
|
my @cookies = ();
|
||
|
|
|
||
|
|
# let the domain argument be an array ref, so callers can set
|
||
|
|
# cookies in both .foo.com and foo.com, for some broken old browsers.
|
||
|
|
if ( $domain && ref $domain eq "ARRAY" ) {
|
||
|
|
foreach (@$domain) {
|
||
|
|
push( @cookies, LJ::make_cookie( $name, $value, $expires, $path, $_ ) );
|
||
|
|
}
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = gmtime($expires);
|
||
|
|
$year += 1900;
|
||
|
|
|
||
|
|
my @day = qw{Sunday Monday Tuesday Wednesday Thursday Friday Saturday};
|
||
|
|
my @month = qw{Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec};
|
||
|
|
|
||
|
|
$cookie = sprintf "%s=%s", LJ::eurl($name), LJ::eurl($value);
|
||
|
|
|
||
|
|
# this logic is confusing potentially
|
||
|
|
unless ( defined $expires && $expires == 0 ) {
|
||
|
|
$cookie .= sprintf "; expires=$day[$wday], %02d-$month[$mon]-%04d %02d:%02d:%02d GMT",
|
||
|
|
$mday, $year, $hour, $min, $sec;
|
||
|
|
}
|
||
|
|
|
||
|
|
$cookie .= "; path=$path" if $path;
|
||
|
|
$cookie .= "; domain=$domain" if $domain;
|
||
|
|
push( @cookies, $cookie );
|
||
|
|
return @cookies;
|
||
|
|
}
|
||
|
|
|
||
|
|
# <LJFUNC>
|
||
|
|
# name: LJ::check_referer
|
||
|
|
# class: web
|
||
|
|
# des: Checks if the user is coming from a given URI.
|
||
|
|
# args: uri?, referer?
|
||
|
|
# des-uri: string; the URI we want the user to come from.
|
||
|
|
# des-referer: string; the location the user is posting from.
|
||
|
|
# If not supplied, will be retrieved with BML::get_client_header.
|
||
|
|
# In general, you don't want to pass this yourself unless
|
||
|
|
# you already have it or know we can't get it from BML.
|
||
|
|
# returns: 1 if they're coming from that URI, else undef
|
||
|
|
# </LJFUNC>
|
||
|
|
sub check_referer {
|
||
|
|
my $uri = shift(@_) || '';
|
||
|
|
my $referer = shift(@_) || BML::get_client_header('Referer');
|
||
|
|
|
||
|
|
# get referer and check
|
||
|
|
return 1 unless $referer;
|
||
|
|
|
||
|
|
my ( $origuri, $origreferer ) = ( $uri, $referer );
|
||
|
|
|
||
|
|
# escape any regex characters, like the '.' in '.bml'
|
||
|
|
$uri = quotemeta($uri);
|
||
|
|
|
||
|
|
# check that the end of the uri matches exactly (no extra characters or dir levels)
|
||
|
|
# or else that the uri is followed immediately by additional parameters
|
||
|
|
my $checkend = '(?:$|\\?)';
|
||
|
|
|
||
|
|
# allow us to properly check URIs without .bml extensions
|
||
|
|
if ( $origuri =~ /\.bml($|\?)/ ) {
|
||
|
|
$checkend = '' if $1 eq '?';
|
||
|
|
$uri =~ s/\\.bml($|\\\?)/$1$checkend/;
|
||
|
|
$referer =~ s/\.bml($|\?)/$1/;
|
||
|
|
}
|
||
|
|
elsif ($uri) {
|
||
|
|
$uri .= $checkend;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
$uri = '(/|$)';
|
||
|
|
}
|
||
|
|
|
||
|
|
return 1 if $LJ::SITEROOT && $referer =~ m!^\Q$LJ::SITEROOT\E$uri!;
|
||
|
|
return 1 if $LJ::DOMAIN && $referer =~ m!^https?://\Q$LJ::DOMAIN\E$uri!;
|
||
|
|
return 1 if $LJ::DOMAIN_WEB && $referer =~ m!^https?://\Q$LJ::DOMAIN_WEB\E$uri!;
|
||
|
|
return 1
|
||
|
|
if $referer =~ m!^https?://([A-Za-z0-9_\-]{1,25})\.\Q$LJ::DOMAIN\E$uri!;
|
||
|
|
return 1 if $origuri =~ m!^https?://! && $origreferer eq $origuri;
|
||
|
|
|
||
|
|
# Dev container: $LJ::DOMAIN is empty, so match referer host against request host
|
||
|
|
if ( $LJ::IS_DEV_SERVER && !$LJ::DOMAIN ) {
|
||
|
|
my $r = eval { DW::Request->get };
|
||
|
|
if ($r) {
|
||
|
|
my $host = $r->host;
|
||
|
|
return 1 if $referer =~ m!^https?://\Q$host\E$uri!;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return undef;
|
||
|
|
}
|
||
|
|
|
||
|
|
# <LJFUNC>
|
||
|
|
# name: LJ::form_auth
|
||
|
|
# class: web
|
||
|
|
# des: Creates an authentication token to be used later to verify that a form
|
||
|
|
# submission came from a particular user.
|
||
|
|
# args: raw?
|
||
|
|
# des-raw: boolean; If true, returns only the token (no HTML).
|
||
|
|
# returns: HTML hidden field to be inserted into the output of a page.
|
||
|
|
# </LJFUNC>
|
||
|
|
sub form_auth {
|
||
|
|
my $raw = shift;
|
||
|
|
my $chal = $LJ::REQ_GLOBAL{form_auth_chal};
|
||
|
|
|
||
|
|
unless ($chal) {
|
||
|
|
my $remote = LJ::get_remote();
|
||
|
|
my $id = $remote ? $remote->id : 0;
|
||
|
|
my $sess =
|
||
|
|
$remote && $remote->session ? $remote->session->id : LJ::UniqCookie->current_uniq;
|
||
|
|
|
||
|
|
my $auth = join( '-', LJ::rand_chars(10), $id, $sess );
|
||
|
|
$chal = DW::Auth::Challenge->generate( 86400, $auth );
|
||
|
|
$LJ::REQ_GLOBAL{form_auth_chal} = $chal;
|
||
|
|
}
|
||
|
|
|
||
|
|
return $raw ? $chal : LJ::html_hidden( "lj_form_auth", $chal );
|
||
|
|
}
|
||
|
|
|
||
|
|
# <LJFUNC>
|
||
|
|
# name: LJ::check_form_auth
|
||
|
|
# class: web
|
||
|
|
# des: Verifies form authentication created with [func[LJ::form_auth]].
|
||
|
|
# returns: Boolean; true if the current data in %POST is a valid form, submitted
|
||
|
|
# by the user in $remote using the current session,
|
||
|
|
# or false if the user has changed, the challenge has expired,
|
||
|
|
# or the user has changed session (logged out and in again, or something).
|
||
|
|
# </LJFUNC>
|
||
|
|
sub check_form_auth {
|
||
|
|
my $formauth = shift || $BMLCodeBlock::POST{'lj_form_auth'};
|
||
|
|
return 0 unless $formauth;
|
||
|
|
|
||
|
|
my $remote = LJ::get_remote();
|
||
|
|
my $id = $remote ? $remote->id : 0;
|
||
|
|
my $sess = $remote && $remote->session ? $remote->session->id : LJ::UniqCookie->current_uniq;
|
||
|
|
|
||
|
|
# check the attributes are as they should be
|
||
|
|
my $attr = DW::Auth::Challenge->get_attributes($formauth);
|
||
|
|
my ( $randchars, $chal_id, $chal_sess ) = split( /\-/, $attr );
|
||
|
|
|
||
|
|
return 0 unless $id == $chal_id;
|
||
|
|
return 0 unless $sess eq $chal_sess;
|
||
|
|
|
||
|
|
# check the signature is good and not expired
|
||
|
|
my $opts = { dont_check_count => 1 }; # in/out
|
||
|
|
DW::Auth::Challenge->check( $formauth, $opts );
|
||
|
|
return $opts->{valid} && !$opts->{expired};
|
||
|
|
}
|
||
|
|
|
||
|
|
# <LJFUNC>
|
||
|
|
# name: LJ::create_qr_div
|
||
|
|
# class: web
|
||
|
|
# des: Creates the hidden div that stores the QuickReply form.
|
||
|
|
# returns: undef upon failure or HTML for the div upon success
|
||
|
|
# args: user, remote, ditemid, style args, userpic, viewing thread
|
||
|
|
# des-u: user object or userid for journal reply in.
|
||
|
|
# des-ditemid: ditemid for this comment.
|
||
|
|
# des-style_opts: the viewing style arguments on this page, as a hashref.
|
||
|
|
# des-userpic: alternate default userpic.
|
||
|
|
# </LJFUNC>
|
||
|
|
sub create_qr_div {
|
||
|
|
|
||
|
|
my ( $user, $ditemid, %opts ) = @_;
|
||
|
|
my $u = LJ::want_user($user);
|
||
|
|
my $remote = LJ::get_remote();
|
||
|
|
return undef unless $u && $remote && $ditemid;
|
||
|
|
|
||
|
|
my $style_opts = $opts{style_opts} || {};
|
||
|
|
my $userpic_kw = $opts{userpic};
|
||
|
|
my $viewing_thread = $opts{thread};
|
||
|
|
|
||
|
|
return undef if $remote->prop("opt_no_quickreply");
|
||
|
|
|
||
|
|
my $e = LJ::Entry->new( $u, ditemid => $ditemid );
|
||
|
|
my $separator = %$style_opts ? "&" : "?";
|
||
|
|
my $basepath = $e->url( style_opts => LJ::viewing_style_opts(%$style_opts) ) . $separator;
|
||
|
|
|
||
|
|
my $usertype =
|
||
|
|
( $remote->openid_identity && $remote->is_validated ) ? 'openid_cookie' : 'cookieuser';
|
||
|
|
my $hidden_form_elements .= LJ::html_hidden(
|
||
|
|
{ 'name' => 'replyto', 'id' => 'replyto', 'value' => '' },
|
||
|
|
{ 'name' => 'parenttalkid', 'id' => 'parenttalkid', 'value' => '' },
|
||
|
|
|
||
|
|
# ^ these two inputs are duplicates, but oh well.
|
||
|
|
{ 'name' => 'journal', 'id' => 'journal', 'value' => $u->{'user'} },
|
||
|
|
{ 'name' => 'itemid', 'id' => 'itemid', 'value' => $ditemid },
|
||
|
|
{ 'name' => 'usertype', 'id' => 'usertype', 'value' => $usertype },
|
||
|
|
{ 'name' => 'qr', 'id' => 'qr', 'value' => '1' },
|
||
|
|
{ 'name' => 'cookieuser', 'id' => 'cookieuser', 'value' => $remote->{'user'} },
|
||
|
|
{ 'name' => 'dtid', 'id' => 'dtid', 'value' => '' },
|
||
|
|
|
||
|
|
# ^ the "display" version of replyto/parenttalkid. Starts empty, set by
|
||
|
|
# JS when QR is summoned, then used to build the "more options" URL.
|
||
|
|
# Nothing uses this after the form is submitted, it's just for JS.
|
||
|
|
{ 'name' => 'basepath', 'id' => 'basepath', 'value' => $basepath },
|
||
|
|
{ 'name' => 'viewing_thread', 'id' => 'viewing_thread', 'value' => $viewing_thread },
|
||
|
|
);
|
||
|
|
|
||
|
|
while ( my ( $key, $value ) = each %$style_opts ) {
|
||
|
|
$hidden_form_elements .= LJ::html_hidden( { name => $key, id => $key, value => $value } );
|
||
|
|
}
|
||
|
|
|
||
|
|
# rate limiting challenge
|
||
|
|
{
|
||
|
|
my ( $time, $secret ) = LJ::get_secret();
|
||
|
|
my $rchars = LJ::rand_chars(20);
|
||
|
|
my $chal = $ditemid . "-$u->{userid}-$time-$rchars";
|
||
|
|
my $res = Digest::MD5::md5_hex( $secret . $chal );
|
||
|
|
$hidden_form_elements .= LJ::html_hidden( "chrp1", "$chal-$res" );
|
||
|
|
}
|
||
|
|
|
||
|
|
# hashref with "selected" and "items" keys
|
||
|
|
my $editors = DW::Formats::select_items( preferred => $remote->prop('comment_editor'), );
|
||
|
|
|
||
|
|
# FIXME: This is incoherent on reading/network pages. (But it's only
|
||
|
|
# noticeable when viewing the reading/network page of someone who wouldn't
|
||
|
|
# allow you to comment.) -NF
|
||
|
|
my $post_disabled = $u->does_not_allow_comments_from($remote);
|
||
|
|
|
||
|
|
return DW::Template->template_string(
|
||
|
|
'journal/quickreply.tt',
|
||
|
|
{
|
||
|
|
form_url => LJ::create_url( '/talkpost_do', host => $LJ::DOMAIN_WEB ),
|
||
|
|
hidden_form_elements => $hidden_form_elements,
|
||
|
|
post_disabled => $post_disabled,
|
||
|
|
post_button_class => $post_disabled ? 'ui-state-disabled' : '',
|
||
|
|
|
||
|
|
# Currently unused, but might come back.
|
||
|
|
minimal => $opts{minimal} ? 1 : 0,
|
||
|
|
|
||
|
|
current_icon_kw => $userpic_kw,
|
||
|
|
current_icon => LJ::Userpic->new_from_keyword( $remote, $userpic_kw ),
|
||
|
|
|
||
|
|
editors => $editors,
|
||
|
|
|
||
|
|
foundation_beta => !LJ::BetaFeatures->user_in_beta( $remote => "nos2foundation" ),
|
||
|
|
|
||
|
|
remote => $remote,
|
||
|
|
|
||
|
|
journal => {
|
||
|
|
is_iplogging => $u->opt_logcommentips eq 'A',
|
||
|
|
is_linkstripped => !$remote
|
||
|
|
|| ( $remote && $remote->is_identity && !$u->trusts_or_has_member($remote) ),
|
||
|
|
},
|
||
|
|
|
||
|
|
help => {
|
||
|
|
icon => LJ::help_icon_html( "userpics", " " ),
|
||
|
|
iplogging => LJ::help_icon_html( "iplogging", " " ),
|
||
|
|
},
|
||
|
|
}
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
# <LJFUNC>
|
||
|
|
# name: LJ::get_lastcomment
|
||
|
|
# class: web
|
||
|
|
# des: Looks up the last talkid and journal the remote user posted in.
|
||
|
|
# returns: talkid, jid
|
||
|
|
# args:
|
||
|
|
# </LJFUNC>
|
||
|
|
sub get_lastcomment {
|
||
|
|
my $remote = LJ::get_remote();
|
||
|
|
my ( $talkid, $jid );
|
||
|
|
|
||
|
|
# Figure out their last post
|
||
|
|
if ($remote) {
|
||
|
|
my $memkey = [ $remote->{'userid'}, "lastcomm:$remote->{'userid'}" ];
|
||
|
|
my $memval = LJ::MemCache::get($memkey);
|
||
|
|
( $jid, $talkid ) = split( /:/, $memval ) if $memval;
|
||
|
|
}
|
||
|
|
|
||
|
|
return ( $talkid, $jid );
|
||
|
|
}
|
||
|
|
|
||
|
|
# <LJFUNC>
|
||
|
|
# name: LJ::make_qr_target
|
||
|
|
# class: web
|
||
|
|
# des: Returns a div usable for QuickReply boxes.
|
||
|
|
# returns: HTML for the div
|
||
|
|
# args:
|
||
|
|
# </LJFUNC>
|
||
|
|
sub make_qr_target {
|
||
|
|
my $name = shift;
|
||
|
|
|
||
|
|
return "<div id='ljqrt$name' name='ljqrt$name'></div>";
|
||
|
|
}
|
||
|
|
|
||
|
|
# <LJFUNC>
|
||
|
|
# name: LJ::set_lastcomment
|
||
|
|
# class: web
|
||
|
|
# des: Sets the lastcomm memcached key for this user's last comment.
|
||
|
|
# returns: undef on failure
|
||
|
|
# args: u, remote, dtalkid, life?
|
||
|
|
# des-u: Journal they just posted in, either u or userid
|
||
|
|
# des-remote: Remote user
|
||
|
|
# des-dtalkid: Talkid for the comment they just posted
|
||
|
|
# des-life: How long, in seconds, the memcached key should live.
|
||
|
|
# </LJFUNC>
|
||
|
|
sub set_lastcomment {
|
||
|
|
my ( $u, $remote, $dtalkid, $life ) = @_;
|
||
|
|
|
||
|
|
my $userid = LJ::want_userid($u);
|
||
|
|
return undef unless $userid && $remote && $dtalkid;
|
||
|
|
|
||
|
|
# By default, this key lasts for 10 seconds.
|
||
|
|
$life ||= 10;
|
||
|
|
|
||
|
|
# Set memcache key for highlighting the comment
|
||
|
|
my $memkey = [ $remote->{'userid'}, "lastcomm:$remote->{'userid'}" ];
|
||
|
|
LJ::MemCache::set( $memkey, "$userid:$dtalkid", time() + $life );
|
||
|
|
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
sub deemp {
|
||
|
|
"<span class='de'>$_[0]</span>";
|
||
|
|
}
|
||
|
|
|
||
|
|
=head2 C<< LJ::determine_viewing_style( $args, $view, $u ) >>
|
||
|
|
Takes a hashref of get args, and the current view, and an optional user.
|
||
|
|
Returns "original", "mine", "site", or "light" as the style.
|
||
|
|
=cut
|
||
|
|
|
||
|
|
sub determine_viewing_style {
|
||
|
|
my ( $args, $view, $u ) = @_;
|
||
|
|
|
||
|
|
my $style = 'original';
|
||
|
|
|
||
|
|
# incorporate any user preferences
|
||
|
|
$style = $u->viewing_style($view) if $u;
|
||
|
|
|
||
|
|
# incorporate any style arguments
|
||
|
|
my %style_getargs = %{ LJ::viewing_style_opts(%$args) };
|
||
|
|
$style = $style_getargs{'style'} if $style_getargs{'style'};
|
||
|
|
|
||
|
|
# keep format=light for backwards compatibility -- override and
|
||
|
|
# assume that somebody using it really wants the light version
|
||
|
|
$style = $style_getargs{'format'} if $style_getargs{'format'};
|
||
|
|
|
||
|
|
return $style;
|
||
|
|
}
|
||
|
|
|
||
|
|
=head2 C<< LJ::viewing_style_args( %arguments ) >>
|
||
|
|
Takes a list of viewing styles arguments from a list, makes sure they are valid values,
|
||
|
|
and returns them as a string that can be appended to the URL. Looks for "s2id", "format", "style"
|
||
|
|
=cut
|
||
|
|
|
||
|
|
sub viewing_style_args {
|
||
|
|
|
||
|
|
#fixme - this should be modernised to take a hashref rather than a hash
|
||
|
|
my (%args) = @_;
|
||
|
|
|
||
|
|
%args = %{ LJ::viewing_style_opts(%args) };
|
||
|
|
|
||
|
|
my @valid_args;
|
||
|
|
while ( my ( $key, $value ) = each %args ) {
|
||
|
|
push @valid_args, "$key=$value";
|
||
|
|
}
|
||
|
|
|
||
|
|
return join "&", @valid_args;
|
||
|
|
}
|
||
|
|
|
||
|
|
=head2 C<< LJ::viewing_style_opts( %arguments ) >>
|
||
|
|
Takes a list of viewing styles arguments from a list, and returns a hashref of valid values
|
||
|
|
=cut
|
||
|
|
|
||
|
|
sub viewing_style_opts {
|
||
|
|
|
||
|
|
#fixme - this should be modernised to take a hashref rather than a hash
|
||
|
|
my (%args) = @_;
|
||
|
|
return {} unless %args;
|
||
|
|
|
||
|
|
my $valid_style_args = {
|
||
|
|
style => { light => 1, site => 1, mine => 1, original => 1 },
|
||
|
|
format => { light => 1 },
|
||
|
|
fallback => { s2 => 1, bml => 1 },
|
||
|
|
};
|
||
|
|
|
||
|
|
my %ret;
|
||
|
|
|
||
|
|
# only accept purely numerical s2ids
|
||
|
|
$ret{s2id} = $args{s2id} if $args{s2id} && $args{s2id} =~ /^\d+$/;
|
||
|
|
|
||
|
|
foreach my $key ( keys %{$valid_style_args} ) {
|
||
|
|
$ret{$key} = $args{$key}
|
||
|
|
if $args{$key} && $valid_style_args->{$key}->{ $args{$key} };
|
||
|
|
}
|
||
|
|
|
||
|
|
return \%ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
=head2 C<< LJ::create_url($path,%opts) >>
|
||
|
|
If specified, path must begin with a /
|
||
|
|
|
||
|
|
args being a list of arguments to create.
|
||
|
|
opts can contain:
|
||
|
|
proto -- specify a protocol
|
||
|
|
host -- link to different domains
|
||
|
|
args -- get arguments to add
|
||
|
|
fragment -- add fragment identifier
|
||
|
|
cur_args -- hashref of current GET arguments to the page
|
||
|
|
keep_args -- arguments to keep
|
||
|
|
keep_query_string -- keep the raw query string (ignores keep_args)
|
||
|
|
no_blank -- remove keys with null values from GET args
|
||
|
|
viewing_style -- include viewing style args
|
||
|
|
=cut
|
||
|
|
|
||
|
|
sub create_url {
|
||
|
|
my ( $path, %opts ) = @_;
|
||
|
|
|
||
|
|
my $r = DW::Request->get;
|
||
|
|
my %out_args = %{ $opts{args} || {} };
|
||
|
|
|
||
|
|
my $host = lc( $opts{host} || $r->host );
|
||
|
|
$path ||= $r->uri;
|
||
|
|
|
||
|
|
my $proto = $opts{proto} // $LJ::PROTOCOL;
|
||
|
|
my $url = $proto . "://$host$path";
|
||
|
|
|
||
|
|
# TWO PATHS: if keep_query_string is used, we simply preserve that
|
||
|
|
# with no further logic. If not, however, we perform arguments logic.
|
||
|
|
my $args;
|
||
|
|
if ( $opts{keep_query_string} ) {
|
||
|
|
$args = $r->query_string;
|
||
|
|
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
my $orig_args = $opts{cur_args} || $r->get_args( preserve_case => 1 );
|
||
|
|
|
||
|
|
# Move over viewing style arguments
|
||
|
|
if ( $opts{viewing_style} ) {
|
||
|
|
my $vs_args = LJ::viewing_style_opts(%$orig_args);
|
||
|
|
foreach my $k ( keys %$vs_args ) {
|
||
|
|
$out_args{$k} = $vs_args->{$k} unless exists $out_args{$k};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
$opts{keep_args} = [ keys %$orig_args ]
|
||
|
|
if defined $opts{keep_args} and $opts{keep_args} == 1;
|
||
|
|
$opts{keep_args} = [] if ref $opts{keep_args} ne 'ARRAY';
|
||
|
|
|
||
|
|
# Move over arguments that we need to keep
|
||
|
|
foreach my $k ( @{ $opts{keep_args} } ) {
|
||
|
|
$out_args{$k} = $orig_args->{$k}
|
||
|
|
if exists $orig_args->{$k} && !exists $out_args{$k};
|
||
|
|
}
|
||
|
|
|
||
|
|
foreach my $k ( keys %out_args ) {
|
||
|
|
if ( !defined $out_args{$k} ) {
|
||
|
|
delete $out_args{$k};
|
||
|
|
}
|
||
|
|
elsif ( !length $out_args{$k} ) {
|
||
|
|
delete $out_args{$k} if $opts{no_blank};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
$args = LJ::encode_url_string( \%out_args, [ sort keys %out_args ] );
|
||
|
|
}
|
||
|
|
|
||
|
|
$url .= "?$args" if $args;
|
||
|
|
$url .= "#" . $opts{fragment} if $opts{fragment};
|
||
|
|
|
||
|
|
return $url;
|
||
|
|
}
|
||
|
|
|
||
|
|
# <LJFUNC>
|
||
|
|
# name: LJ::entry_form
|
||
|
|
# class: web
|
||
|
|
# des: Returns a properly formatted form for creating/editing entries.
|
||
|
|
# args: head, onload, opts
|
||
|
|
# des-head: string reference for the <head> section (JavaScript previews, etc).
|
||
|
|
# des-onload: string reference for JavaScript functions to be called on page load
|
||
|
|
# des-opts: hashref of keys/values:
|
||
|
|
# mode: either "update" or "edit", depending on context;
|
||
|
|
# datetime: date and time, formatted yyyy-mm-dd hh:mm;
|
||
|
|
# remote: remote u object;
|
||
|
|
# subject: entry subject;
|
||
|
|
# event: entry text;
|
||
|
|
# richtext: allow rich text formatting;
|
||
|
|
# auth_as_remote: bool option to authenticate as remote user, pre-filling pic/friend groups/etc.
|
||
|
|
# return: form to include in BML pages.
|
||
|
|
# </LJFUNC>
|
||
|
|
sub entry_form {
|
||
|
|
my ( $opts, $head, $onload, $errors ) = @_;
|
||
|
|
|
||
|
|
my $out = "";
|
||
|
|
my $remote = $opts->{remote};
|
||
|
|
my $altlogin = $opts->{altlogin};
|
||
|
|
my ( $moodlist, $moodpics );
|
||
|
|
|
||
|
|
# usejournal has no point if you're trying to use the account you're logged in as,
|
||
|
|
# so disregard it so we can assume that if it exists, we're trying to post to an
|
||
|
|
# account that isn't us
|
||
|
|
if ( $remote && $opts->{usejournal} && $remote->{user} eq $opts->{usejournal} ) {
|
||
|
|
delete $opts->{usejournal};
|
||
|
|
}
|
||
|
|
|
||
|
|
# Temp fix for FF 2.0.0.17
|
||
|
|
my $rte_is_supported = LJ::is_enabled( 'rte_support', BML::get_client_header("User-Agent") );
|
||
|
|
$opts->{'richtext_default'} = 0 unless $rte_is_supported;
|
||
|
|
|
||
|
|
$opts->{'richtext'} = $opts->{'richtext_default'};
|
||
|
|
my $tabnum = 10; #make allowance for username and password
|
||
|
|
# Leave gaps for interpolated fields eg date/time
|
||
|
|
my $tabindex = sub { return ( $tabnum += 10 ) - 10; };
|
||
|
|
$opts->{'event'} = LJ::durl( $opts->{'event'} ) if $opts->{'mode'} eq "edit";
|
||
|
|
|
||
|
|
$out .= "\n\n<div id='entry-form-wrapper'>\n";
|
||
|
|
$out .= LJ::error_list( $errors->{entry} ) if $errors->{entry};
|
||
|
|
|
||
|
|
### Icon Selection
|
||
|
|
|
||
|
|
my $pic = ''; # displays chosen/default pic
|
||
|
|
my $picform = ''; # displays form drop-down
|
||
|
|
|
||
|
|
LJ::Widget::UserpicSelector->render(
|
||
|
|
picargs => [ $remote, \$$head, \$pic, \$picform ],
|
||
|
|
prop_picture_keyword => $opts->{prop_picture_keyword},
|
||
|
|
no_auth => !$opts->{auth_as_remote},
|
||
|
|
onload => $onload,
|
||
|
|
altlogin => $altlogin,
|
||
|
|
entry_js => 1
|
||
|
|
);
|
||
|
|
|
||
|
|
# libs for userpicselect
|
||
|
|
LJ::Talk::init_iconbrowser_js()
|
||
|
|
if !$altlogin && $remote && $remote->can_use_userpic_select;
|
||
|
|
|
||
|
|
$out .= $pic;
|
||
|
|
|
||
|
|
### Meta Information Column 1
|
||
|
|
{
|
||
|
|
# do a login action to get usejournals, but only if using remote
|
||
|
|
my $res;
|
||
|
|
$res = LJ::Protocol::do_request(
|
||
|
|
"login",
|
||
|
|
{
|
||
|
|
ver => $LJ::PROTOCOL_VER,
|
||
|
|
username => $remote->user,
|
||
|
|
},
|
||
|
|
undef,
|
||
|
|
{
|
||
|
|
noauth => 1,
|
||
|
|
u => $remote,
|
||
|
|
}
|
||
|
|
) if $opts->{auth_as_remote};
|
||
|
|
|
||
|
|
$out .= "<div id='metainfo'>\n\n";
|
||
|
|
|
||
|
|
# login info
|
||
|
|
$out .= $opts->{'auth'};
|
||
|
|
if ( $opts->{'mode'} eq "update" ) {
|
||
|
|
|
||
|
|
# communities the user can post in
|
||
|
|
my $usejournal = $opts->{'usejournal'};
|
||
|
|
if ($usejournal) {
|
||
|
|
$out .= "<p id='usejournal_single' class='pkg'>\n";
|
||
|
|
$out .=
|
||
|
|
"<label for='usejournal' class='left'>"
|
||
|
|
. BML::ml('entryform.postto')
|
||
|
|
. "</label>\n";
|
||
|
|
$out .= LJ::ljuser($usejournal);
|
||
|
|
$out .= LJ::html_hidden(
|
||
|
|
{ name => 'usejournal', value => $usejournal, id => 'usejournal_username' } );
|
||
|
|
$out .= LJ::html_hidden( usejournal_set => 'true' );
|
||
|
|
$out .= "</p>\n";
|
||
|
|
}
|
||
|
|
elsif ( $res && ref $res->{'usejournals'} eq 'ARRAY' ) {
|
||
|
|
my $submitprefix = BML::ml('entryform.update3');
|
||
|
|
$out .= "<p id='usejournal_list' class='pkg'>\n";
|
||
|
|
$out .=
|
||
|
|
"<label for='usejournal' class='left'>"
|
||
|
|
. BML::ml('entryform.postto')
|
||
|
|
. "</label>\n";
|
||
|
|
$out .= LJ::html_select(
|
||
|
|
{
|
||
|
|
'name' => 'usejournal',
|
||
|
|
'id' => 'usejournal',
|
||
|
|
'selected' => $usejournal,
|
||
|
|
'tabindex' => $tabindex->(),
|
||
|
|
'class' => 'select',
|
||
|
|
"onchange" => "changeSubmit('"
|
||
|
|
. $submitprefix . "','"
|
||
|
|
. $remote->{'user'}
|
||
|
|
. "'); getUserTags('$remote->{user}'); changeSecurityOptions('$remote->{user}'); XPostAccount.updateXpostFromJournal('$remote->{user}');"
|
||
|
|
},
|
||
|
|
"",
|
||
|
|
$remote->{'user'},
|
||
|
|
map { $_, $_ } @{ $res->{'usejournals'} }
|
||
|
|
) . "\n";
|
||
|
|
$out .= "</p>\n";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Authentication box
|
||
|
|
$out .= "<p class='update-errors'><?inerr $errors->{'auth'} inerr?></p>\n"
|
||
|
|
if $errors->{'auth'};
|
||
|
|
|
||
|
|
# Date / Time
|
||
|
|
{
|
||
|
|
my ( $year, $mon, $mday, $hour, $min ) = split( /\D/, $opts->{'datetime'} );
|
||
|
|
my $monthlong = LJ::Lang::month_long($mon);
|
||
|
|
|
||
|
|
# date entry boxes / formatting note
|
||
|
|
my $datetime = LJ::html_datetime(
|
||
|
|
{
|
||
|
|
name => 'date_ymd',
|
||
|
|
notime => 1,
|
||
|
|
default => "$year-$mon-$mday",
|
||
|
|
tabindex => $tabindex->(),
|
||
|
|
disabled => $opts->{'disabled_save'}
|
||
|
|
}
|
||
|
|
);
|
||
|
|
$datetime .= "<span class='float-left'> </span>";
|
||
|
|
$datetime .= LJ::html_text(
|
||
|
|
{
|
||
|
|
size => 2,
|
||
|
|
class => 'text',
|
||
|
|
maxlength => 2,
|
||
|
|
value => $hour,
|
||
|
|
name => "hour",
|
||
|
|
tabindex => $tabindex->(),
|
||
|
|
disabled => $opts->{'disabled_save'}
|
||
|
|
}
|
||
|
|
) . "<span class='float-left'>:</span>";
|
||
|
|
$datetime .= LJ::html_text(
|
||
|
|
{
|
||
|
|
size => 2,
|
||
|
|
class => 'text',
|
||
|
|
maxlength => 2,
|
||
|
|
value => $min,
|
||
|
|
name => "min",
|
||
|
|
tabindex => $tabindex->(),
|
||
|
|
disabled => $opts->{'disabled_save'}
|
||
|
|
}
|
||
|
|
);
|
||
|
|
|
||
|
|
# JavaScript sets this value, so we know that the time we get is correct
|
||
|
|
# but always trust the time if we've been through the form already
|
||
|
|
my $date_diff = ( $opts->{'mode'} eq "edit" || $opts->{'spellcheck_html'} ) ? 1 : 0;
|
||
|
|
$datetime .= LJ::html_hidden( "date_diff", $date_diff );
|
||
|
|
|
||
|
|
# but if we don't have JS, give a signal to trust the given time
|
||
|
|
$datetime .= "<noscript>" . LJ::html_hidden( "date_diff_nojs", "1" ) . "</noscript>";
|
||
|
|
|
||
|
|
$out .= "<p class='pkg'>\n";
|
||
|
|
$out .=
|
||
|
|
"<label for='modifydate' class='left'>" . BML::ml('entryform.date') . "</label>\n";
|
||
|
|
$out .=
|
||
|
|
"<span id='currentdate' class='float-left'><span id='currentdate-date'>$monthlong $mday, $year, $hour"
|
||
|
|
. ":"
|
||
|
|
. "$min</span> <a href='javascript:void(0)' onclick='editdate();' id='currentdate-edit'>"
|
||
|
|
. BML::ml('entryform.date.edit')
|
||
|
|
. "</a></span>\n";
|
||
|
|
$out .=
|
||
|
|
"<span id='modifydate'>$datetime <?de "
|
||
|
|
. BML::ml('entryform.date.24hournote')
|
||
|
|
. " de?><br />\n";
|
||
|
|
$out .= LJ::html_check(
|
||
|
|
{
|
||
|
|
'type' => "check",
|
||
|
|
'id' => "prop_opt_backdated",
|
||
|
|
'name' => "prop_opt_backdated",
|
||
|
|
"value" => 1,
|
||
|
|
'selected' => $opts->{'prop_opt_backdated'},
|
||
|
|
'tabindex' => $tabindex->()
|
||
|
|
}
|
||
|
|
);
|
||
|
|
$out .=
|
||
|
|
"<label for='prop_opt_backdated' class='right'>"
|
||
|
|
. BML::ml('entryform.backdated4')
|
||
|
|
. "</label>\n";
|
||
|
|
$out .= LJ::help_icon_html( "backdate", "", "" ) . "\n";
|
||
|
|
$out .= "</span><!-- end #modifydate -->\n";
|
||
|
|
$out .= "</p>\n";
|
||
|
|
$out .=
|
||
|
|
"<noscript><p id='time-correct' class='small'>"
|
||
|
|
. BML::ml('entryform.nojstime.note')
|
||
|
|
. "</p></noscript>\n";
|
||
|
|
$$onload .= " defaultDate();";
|
||
|
|
}
|
||
|
|
|
||
|
|
# User Picture
|
||
|
|
{
|
||
|
|
my $tab = $tabindex->();
|
||
|
|
$picform =~ s/~~TABINDEX~~/$tab/;
|
||
|
|
$out .= $picform;
|
||
|
|
}
|
||
|
|
|
||
|
|
$out .= "</div><!-- end #metainfo -->\n\n";
|
||
|
|
|
||
|
|
### Other Posting Options
|
||
|
|
{
|
||
|
|
$out .= "<div id='infobox'>\n";
|
||
|
|
$out .=
|
||
|
|
LJ::Hooks::run_hook( 'entryforminfo', $opts->{'usejournal'}, $opts->{'remote'} );
|
||
|
|
$out .= "</div><!-- end #infobox -->\n\n";
|
||
|
|
}
|
||
|
|
|
||
|
|
### Subject
|
||
|
|
$out .= "<div id='compose-entry' class='pkg'>\n";
|
||
|
|
|
||
|
|
$out .= "<label class='left' for='subject'>" . BML::ml('entryform.subject') . "</label>\n";
|
||
|
|
$out .= LJ::html_text(
|
||
|
|
{
|
||
|
|
'name' => 'subject',
|
||
|
|
'value' => $opts->{'subject'},
|
||
|
|
'class' => 'text',
|
||
|
|
'id' => 'subject',
|
||
|
|
'size' => '43',
|
||
|
|
'maxlength' => '100',
|
||
|
|
'tabindex' => $tabindex->(),
|
||
|
|
'disabled' => $opts->{'disabled_save'}
|
||
|
|
}
|
||
|
|
) . "\n";
|
||
|
|
$out .= "<ul id='entry-tabs' style='display: none;'>\n";
|
||
|
|
$out .= "<li id='jrich'>"
|
||
|
|
. BML::ml(
|
||
|
|
"entryform.htmlokay.rich4",
|
||
|
|
{
|
||
|
|
'opts' => 'href="javascript:void(0);" onclick="return useRichText(\'draft\', \''
|
||
|
|
. $LJ::WSTATPREFIX . '\');"'
|
||
|
|
}
|
||
|
|
)
|
||
|
|
. "</li>\n"
|
||
|
|
if $rte_is_supported;
|
||
|
|
$out .= "<li id='jplain' class='on'>"
|
||
|
|
. BML::ml( "entryform.plainswitch2",
|
||
|
|
{ 'aopts' => 'href="javascript:void(0);" onclick="return usePlainText(\'draft\');"' } )
|
||
|
|
. "</li>\n";
|
||
|
|
$out .= "</ul>";
|
||
|
|
$out .= "</div><!-- end #entry -->\n\n";
|
||
|
|
$$onload .= " showEntryTabs();";
|
||
|
|
}
|
||
|
|
|
||
|
|
### Display Spell Check Results:
|
||
|
|
$out .=
|
||
|
|
"<div id='spellcheck-results'><strong>"
|
||
|
|
. BML::ml('entryform.spellchecked')
|
||
|
|
. "</strong><br />$opts->{'spellcheck_html'}</div>\n"
|
||
|
|
if $opts->{'spellcheck_html'};
|
||
|
|
|
||
|
|
### Insert Object Toolbar:
|
||
|
|
LJ::need_res(
|
||
|
|
qw(
|
||
|
|
js/6alib/core.js
|
||
|
|
js/6alib/dom.js
|
||
|
|
js/6alib/ippu.js
|
||
|
|
js/lj_ippu.js
|
||
|
|
)
|
||
|
|
);
|
||
|
|
$out .= "<div id='htmltools' class='pkg'>\n";
|
||
|
|
$out .= "<ul class='pkg'>\n";
|
||
|
|
$out .=
|
||
|
|
"<li class='image'><a href='javascript:void(0);' onclick='InOb.handleInsertImage();' title='"
|
||
|
|
. BML::ml('fckland.ljimage') . "'>"
|
||
|
|
. BML::ml('entryform.insert.image2')
|
||
|
|
. "</a></li>\n";
|
||
|
|
$out .=
|
||
|
|
"<li class='media'><a href='javascript:void(0);' onclick='InOb.handleInsertEmbed();' title='"
|
||
|
|
. BML::ml('fcklang.ljvideo2') . "'>"
|
||
|
|
. BML::ml('fcklang.ljvideo2')
|
||
|
|
. "</a></li>\n"
|
||
|
|
if LJ::is_enabled('embed_module');
|
||
|
|
$out .= "</ul>\n";
|
||
|
|
my $format_selected =
|
||
|
|
( $opts->{mode} eq "update" && $remote && $remote->disable_auto_formatting )
|
||
|
|
|| $opts->{'prop_opt_preformatted'}
|
||
|
|
|| $opts->{'event_format'} ? "checked='checked'" : "";
|
||
|
|
$out .=
|
||
|
|
"<span id='linebreaks'><input type='checkbox' class='check' value='preformatted' name='event_format' id='event_format' $format_selected />
|
||
|
|
<label for='event_format'>"
|
||
|
|
. BML::ml('entryform.format3')
|
||
|
|
. "</label>"
|
||
|
|
. LJ::help_icon_html( "noautoformat", "", " " )
|
||
|
|
. "</span>\n";
|
||
|
|
$out .= "</div>\n\n";
|
||
|
|
|
||
|
|
### Draft Status Area
|
||
|
|
$out .= "<div id='draft-container' class='pkg'>\n";
|
||
|
|
$out .= LJ::html_textarea(
|
||
|
|
{
|
||
|
|
'name' => 'event',
|
||
|
|
'value' => $opts->{'event'},
|
||
|
|
'rows' => '20',
|
||
|
|
'cols' => '50',
|
||
|
|
'style' => '',
|
||
|
|
'tabindex' => $tabindex->(),
|
||
|
|
'wrap' => 'soft',
|
||
|
|
'disabled' => $opts->{'disabled_save'},
|
||
|
|
'id' => 'draft'
|
||
|
|
}
|
||
|
|
) . "\n";
|
||
|
|
$out .= "</div><!-- end #draft-container -->\n\n";
|
||
|
|
$out .= "<input type='text' disabled='disabled' name='draftstatus' id='draftstatus' />\n\n";
|
||
|
|
LJ::need_res( 'stc/fck/fckeditor.js', 'js/rte.js', 'stc/display_none.css' );
|
||
|
|
if ( !$opts->{'did_spellcheck'} ) {
|
||
|
|
|
||
|
|
my $jnorich = LJ::ejs( LJ::deemp( BML::ml('entryform.htmlokay.norich2') ) );
|
||
|
|
|
||
|
|
$out .= <<RTE;
|
||
|
|
<script language='JavaScript' type='text/javascript'>
|
||
|
|
<!--
|
||
|
|
|
||
|
|
// Check if this browser supports FCKeditor
|
||
|
|
var rte = new FCKeditor();
|
||
|
|
var t = rte._IsCompatibleBrowser();
|
||
|
|
if (t) {
|
||
|
|
RTE
|
||
|
|
|
||
|
|
my @sites = DW::External::Site->get_sites;
|
||
|
|
my @sitevalues;
|
||
|
|
foreach my $site ( sort { $a->{sitename} cmp $b->{sitename} } @sites ) {
|
||
|
|
push @sitevalues, { domain => $site->{domain}, sitename => $site->{sitename} };
|
||
|
|
}
|
||
|
|
|
||
|
|
$out .= "var FCKLang;\n";
|
||
|
|
$out .= "if (!FCKLang) FCKLang = {};\n";
|
||
|
|
$out .= "FCKLang.UserPrompt = \"" . LJ::ejs( BML::ml('fcklang.userprompt') ) . "\";\n";
|
||
|
|
$out .= "FCKLang.UserPrompt_User = \""
|
||
|
|
. LJ::ejs( BML::ml('fcklang.userprompt.user') ) . "\";\n";
|
||
|
|
$out .= "FCKLang.UserPrompt_Site = \""
|
||
|
|
. LJ::ejs( BML::ml('fcklang.userprompt.site') ) . "\";\n";
|
||
|
|
$out .= "FCKLang.UserPrompt_SiteList =" . to_json( \@sitevalues ) . ";\n";
|
||
|
|
$out .= "FCKLang.InvalidChars = \"" . LJ::ejs( BML::ml('fcklang.invalidchars') ) . "\";\n";
|
||
|
|
$out .= "FCKLang.LJUser = \"" . LJ::ejs( BML::ml('fcklang.ljuser') ) . "\";\n";
|
||
|
|
$out .= "FCKLang.LJVideo = \"" . LJ::ejs( BML::ml('fcklang.ljvideo2') ) . "\";\n";
|
||
|
|
$out .=
|
||
|
|
"FCKLang.EmbedContents = \"" . LJ::ejs( BML::ml('fcklang.embedcontents') ) . "\";\n";
|
||
|
|
$out .= "FCKLang.EmbedPrompt = \"" . LJ::ejs( BML::ml('fcklang.embedprompt') ) . "\";\n";
|
||
|
|
$out .= "FCKLang.CutPrompt = \"" . LJ::ejs( BML::ml('fcklang.cutprompt') ) . "\";\n";
|
||
|
|
$out .= "FCKLang.ReadMore = \"" . LJ::ejs( BML::ml('fcklang.readmore') ) . "\";\n";
|
||
|
|
$out .= "FCKLang.CutContents = \"" . LJ::ejs( BML::ml('fcklang.cutcontents') ) . "\";\n";
|
||
|
|
$out .= "FCKLang.LJCut = \"" . LJ::ejs( BML::ml('fcklang.ljcut') ) . "\";\n";
|
||
|
|
|
||
|
|
if ( $opts->{'richtext_default'} ) {
|
||
|
|
$$onload .= 'useRichText("draft", "' . LJ::ejs($LJ::WSTATPREFIX) . '");';
|
||
|
|
}
|
||
|
|
|
||
|
|
{
|
||
|
|
my $jrich = LJ::ejs(
|
||
|
|
LJ::deemp(
|
||
|
|
BML::ml(
|
||
|
|
"entryform.htmlokay.rich2",
|
||
|
|
{
|
||
|
|
'opts' =>
|
||
|
|
'href="javascript:void(0);" onclick="return useRichText(\'draft\', \''
|
||
|
|
. LJ::ejs($LJ::WSTATPREFIX) . '\');"'
|
||
|
|
}
|
||
|
|
)
|
||
|
|
)
|
||
|
|
);
|
||
|
|
|
||
|
|
my $jplain = LJ::ejs(
|
||
|
|
LJ::deemp(
|
||
|
|
BML::ml(
|
||
|
|
"entryform.plainswitch",
|
||
|
|
{
|
||
|
|
'aopts' =>
|
||
|
|
'href="javascript:void(0);" onclick="return usePlainText(\'draft\');"'
|
||
|
|
}
|
||
|
|
)
|
||
|
|
)
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
$out .= <<RTE;
|
||
|
|
} else {
|
||
|
|
document.getElementById('entry-tabs').style.visibility = 'hidden';
|
||
|
|
document.getElementById('htmltools').style.display = 'block';
|
||
|
|
document.write("$jnorich");
|
||
|
|
usePlainText('draft');
|
||
|
|
}
|
||
|
|
//-->
|
||
|
|
</script>
|
||
|
|
RTE
|
||
|
|
|
||
|
|
$out .=
|
||
|
|
'<noscript><?de ' . BML::ml('entryform.htmlokay.norich2') . ' de?><br /></noscript>';
|
||
|
|
}
|
||
|
|
$out .= LJ::html_hidden( { name => 'switched_rte_on', id => 'switched_rte_on', value => '0' } );
|
||
|
|
|
||
|
|
$out .= "<div id='options' class='pkg'>";
|
||
|
|
if ( !$opts->{'disabled_save'} ) {
|
||
|
|
### Options
|
||
|
|
|
||
|
|
# Tag labeling
|
||
|
|
if ( LJ::is_enabled('tags') ) {
|
||
|
|
$out .= "<p class='pkg'>";
|
||
|
|
$out .=
|
||
|
|
"<label for='prop_taglist' class='left options'>"
|
||
|
|
. BML::ml('entryform.tags')
|
||
|
|
. "</label>";
|
||
|
|
$out .= LJ::html_text(
|
||
|
|
{
|
||
|
|
'name' => 'prop_taglist',
|
||
|
|
'id' => 'prop_taglist',
|
||
|
|
'class' => 'text',
|
||
|
|
'size' => '35',
|
||
|
|
'value' => $opts->{'prop_taglist'},
|
||
|
|
'tabindex' => $tabindex->(),
|
||
|
|
'raw' => "autocomplete='off'",
|
||
|
|
}
|
||
|
|
);
|
||
|
|
$out .= LJ::help_icon_html('addtags');
|
||
|
|
$out .= "</p>";
|
||
|
|
}
|
||
|
|
|
||
|
|
$out .= "<p class='pkg'>\n";
|
||
|
|
$out .= "<span id='prop_mood_wrapper' class='inputgroup-left'>\n";
|
||
|
|
$out .=
|
||
|
|
"<label for='prop_current_moodid' class='left options'>"
|
||
|
|
. BML::ml('entryform.mood')
|
||
|
|
. "</label>";
|
||
|
|
|
||
|
|
# Current Mood
|
||
|
|
{
|
||
|
|
my @moodlist = ( '', BML::ml('entryform.mood.noneother') );
|
||
|
|
my $sel;
|
||
|
|
|
||
|
|
my $moods = DW::Mood->get_moods;
|
||
|
|
|
||
|
|
foreach ( sort { $moods->{$a}->{'name'} cmp $moods->{$b}->{'name'} } keys %$moods ) {
|
||
|
|
push @moodlist, ( $_, $moods->{$_}->{'name'} );
|
||
|
|
|
||
|
|
if ( $opts->{prop_current_mood}
|
||
|
|
&& $opts->{prop_current_mood} eq $moods->{$_}->{name}
|
||
|
|
|| $opts->{prop_current_moodid} && $opts->{prop_current_moodid} == $_ )
|
||
|
|
{
|
||
|
|
$sel = $_;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($remote) {
|
||
|
|
my $r_theme = DW::Mood->new( $remote->{'moodthemeid'} );
|
||
|
|
foreach my $mood ( keys %$moods ) {
|
||
|
|
my $moodid = $moods->{$mood}->{id};
|
||
|
|
if ( $r_theme && $r_theme->get_picture( $moodid, \my %pic ) ) {
|
||
|
|
$moodlist .= " moods[" . $moodid;
|
||
|
|
$moodlist .= "] = \"";
|
||
|
|
$moodlist .= $moods->{$mood}->{name} . "\";\n";
|
||
|
|
$moodpics .= " moodpics[" . $moodid;
|
||
|
|
$moodpics .= "] = \"";
|
||
|
|
$moodpics .= $pic{pic} . "\";\n";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
$$onload .= " mood_preview();";
|
||
|
|
$$head .= <<MOODS;
|
||
|
|
<script type="text/javascript" language="JavaScript"><!--
|
||
|
|
if (document.getElementById) {
|
||
|
|
var moodpics = new Array();
|
||
|
|
$moodpics
|
||
|
|
var moods = new Array();
|
||
|
|
$moodlist
|
||
|
|
}
|
||
|
|
//--></script>
|
||
|
|
MOODS
|
||
|
|
}
|
||
|
|
my $moodpreviewoc;
|
||
|
|
$moodpreviewoc = 'mood_preview()' if $remote;
|
||
|
|
$out .= LJ::html_select(
|
||
|
|
{
|
||
|
|
'name' => 'prop_current_moodid',
|
||
|
|
'id' => 'prop_current_moodid',
|
||
|
|
'selected' => $sel,
|
||
|
|
'onchange' => $moodpreviewoc,
|
||
|
|
'class' => 'select',
|
||
|
|
'tabindex' => $tabindex->()
|
||
|
|
},
|
||
|
|
@moodlist
|
||
|
|
);
|
||
|
|
$out .= " "
|
||
|
|
. LJ::html_text(
|
||
|
|
{
|
||
|
|
'name' => 'prop_current_mood',
|
||
|
|
'id' => 'prop_current_mood',
|
||
|
|
'class' => 'text',
|
||
|
|
'value' => $opts->{'prop_current_mood'},
|
||
|
|
'onchange' => $moodpreviewoc,
|
||
|
|
'size' => '15',
|
||
|
|
'maxlength' => '30',
|
||
|
|
'tabindex' => $tabindex->()
|
||
|
|
}
|
||
|
|
);
|
||
|
|
}
|
||
|
|
$out .= "<span id='mood_preview'></span>";
|
||
|
|
$out .= "</span>\n";
|
||
|
|
$out .= "<span class='inputgroup-right'>\n";
|
||
|
|
$out .=
|
||
|
|
"<label for='comment_settings' class='left options'>"
|
||
|
|
. BML::ml('entryform.comment.settings2')
|
||
|
|
. "</label>\n";
|
||
|
|
|
||
|
|
# Comment Settings
|
||
|
|
my $comment_settings_selected = sub {
|
||
|
|
return "noemail" if $opts->{'prop_opt_noemail'};
|
||
|
|
return "nocomments"
|
||
|
|
if $opts->{prop_opt_nocomments} || $opts->{prop_opt_nocomments_maintainer};
|
||
|
|
return $opts->{'comment_settings'};
|
||
|
|
};
|
||
|
|
|
||
|
|
my $comment_settings_journaldefault = sub {
|
||
|
|
return "Disabled"
|
||
|
|
if $opts->{prop_opt_default_nocomments}
|
||
|
|
&& $opts->{prop_opt_default_nocomments} eq 'N';
|
||
|
|
return "No Email"
|
||
|
|
if $opts->{prop_opt_default_noemail} && $opts->{prop_opt_default_noemail} eq 'N';
|
||
|
|
return "Enabled";
|
||
|
|
};
|
||
|
|
|
||
|
|
my $nocomments_display =
|
||
|
|
$opts->{prop_opt_nocomments_maintainer}
|
||
|
|
? 'entryform.comment.settings.nocomments.admin'
|
||
|
|
: 'entryform.comment.settings.nocomments';
|
||
|
|
|
||
|
|
my $comment_settings_default = BML::ml( 'entryform.comment.settings.default5',
|
||
|
|
{ 'aopts' => $comment_settings_journaldefault->() } );
|
||
|
|
$out .= LJ::html_select(
|
||
|
|
{
|
||
|
|
'name' => "comment_settings",
|
||
|
|
'id' => 'comment_settings',
|
||
|
|
'class' => 'select',
|
||
|
|
'selected' => $comment_settings_selected->(),
|
||
|
|
'tabindex' => $tabindex->()
|
||
|
|
},
|
||
|
|
"",
|
||
|
|
$comment_settings_default,
|
||
|
|
"nocomments",
|
||
|
|
BML::ml( $nocomments_display, "noemail" ),
|
||
|
|
"noemail",
|
||
|
|
BML::ml('entryform.comment.settings.noemail')
|
||
|
|
);
|
||
|
|
$out .= LJ::help_icon_html( "comment", "", " " );
|
||
|
|
$out .= "\n";
|
||
|
|
$out .= "</span>\n";
|
||
|
|
$out .= "</p>\n";
|
||
|
|
|
||
|
|
# Current Location
|
||
|
|
$out .= "<p class='pkg'>";
|
||
|
|
if ( LJ::is_enabled('web_current_location') ) {
|
||
|
|
$out .= "<span class='inputgroup-left'>";
|
||
|
|
$out .=
|
||
|
|
"<label for='prop_current_location' class='left options'>"
|
||
|
|
. BML::ml('entryform.location')
|
||
|
|
. "</label>";
|
||
|
|
$out .= LJ::html_text(
|
||
|
|
{
|
||
|
|
name => 'prop_current_location',
|
||
|
|
value => $opts->{prop_current_location},
|
||
|
|
id => 'prop_current_location',
|
||
|
|
class => 'text',
|
||
|
|
size => '35',
|
||
|
|
maxlength => LJ::std_max_length(),
|
||
|
|
tabindex => $tabindex->()
|
||
|
|
}
|
||
|
|
) . "\n";
|
||
|
|
$out .= "</span>";
|
||
|
|
}
|
||
|
|
|
||
|
|
# Comment Screening settings
|
||
|
|
$out .= "<span class='inputgroup-right'>\n";
|
||
|
|
$out .=
|
||
|
|
"<label for='prop_opt_screening' class='left options'>"
|
||
|
|
. BML::ml('entryform.comment.screening2')
|
||
|
|
. "</label>\n";
|
||
|
|
my $opt_default_screen = $opts->{prop_opt_default_screening} || '';
|
||
|
|
my $screening_levels_default =
|
||
|
|
$opt_default_screen eq 'N' ? BML::ml('label.screening.none2')
|
||
|
|
: $opt_default_screen eq 'R' ? BML::ml('label.screening.anonymous2')
|
||
|
|
: $opt_default_screen eq 'F' ? BML::ml('label.screening.nonfriends2')
|
||
|
|
: $opt_default_screen eq 'A' ? BML::ml('label.screening.all2')
|
||
|
|
: BML::ml('label.screening.none2');
|
||
|
|
my @levels = (
|
||
|
|
'', BML::ml( 'label.screening.default4', { 'aopts' => $screening_levels_default } ),
|
||
|
|
'N', BML::ml('label.screening.none2'),
|
||
|
|
'R', BML::ml('label.screening.anonymous2'),
|
||
|
|
'F', BML::ml('label.screening.nonfriends2'),
|
||
|
|
'A', BML::ml('label.screening.all2')
|
||
|
|
);
|
||
|
|
$out .= LJ::html_select(
|
||
|
|
{
|
||
|
|
'name' => 'prop_opt_screening',
|
||
|
|
'id' => 'prop_opt_screening',
|
||
|
|
'class' => 'select',
|
||
|
|
'selected' => $opts->{'prop_opt_screening'},
|
||
|
|
'tabindex' => $tabindex->()
|
||
|
|
},
|
||
|
|
@levels
|
||
|
|
);
|
||
|
|
$out .= LJ::help_icon_html( "screening", "", " " );
|
||
|
|
$out .= "</span>\n";
|
||
|
|
$out .= "</p>\n";
|
||
|
|
|
||
|
|
# Current Music
|
||
|
|
$out .= "<p class='pkg'>\n";
|
||
|
|
$out .= "<span class='inputgroup-left'>\n";
|
||
|
|
$out .=
|
||
|
|
"<label for='prop_current_music' class='left options'>"
|
||
|
|
. BML::ml('entryform.music')
|
||
|
|
. "</label>\n";
|
||
|
|
|
||
|
|
# BML::ml('entryform.music')
|
||
|
|
$out .= LJ::html_text(
|
||
|
|
{
|
||
|
|
name => 'prop_current_music',
|
||
|
|
value => $opts->{prop_current_music},
|
||
|
|
id => 'prop_current_music',
|
||
|
|
class => 'text',
|
||
|
|
size => '35',
|
||
|
|
maxlength => LJ::std_max_length(),
|
||
|
|
tabindex => $tabindex->()
|
||
|
|
}
|
||
|
|
) . "\n";
|
||
|
|
$out .= "</span>\n";
|
||
|
|
$out .= "<span class='inputgroup-right'>";
|
||
|
|
|
||
|
|
# Content Flag
|
||
|
|
if ( LJ::is_enabled('adult_content') ) {
|
||
|
|
my @adult_content_menu = (
|
||
|
|
"" => BML::ml('entryform.adultcontent.default'),
|
||
|
|
none => BML::ml('entryform.adultcontent.none'),
|
||
|
|
concepts => BML::ml('entryform.adultcontent.concepts'),
|
||
|
|
explicit => BML::ml('entryform.adultcontent.explicit'),
|
||
|
|
);
|
||
|
|
|
||
|
|
$out .=
|
||
|
|
"<label for='prop_adult_content' class='left options'>"
|
||
|
|
. BML::ml('entryform.adultcontent')
|
||
|
|
. "</label>\n";
|
||
|
|
$out .= LJ::html_select(
|
||
|
|
{
|
||
|
|
name => 'prop_adult_content',
|
||
|
|
id => 'prop_adult_content',
|
||
|
|
class => 'select',
|
||
|
|
selected => $opts->{prop_adult_content} || "",
|
||
|
|
tabindex => $tabindex->(),
|
||
|
|
},
|
||
|
|
@adult_content_menu
|
||
|
|
);
|
||
|
|
$out .= LJ::help_icon_html( "adult_content", "", " " );
|
||
|
|
}
|
||
|
|
$out .= "</span>\n";
|
||
|
|
$out .= "</p>\n";
|
||
|
|
|
||
|
|
if ( LJ::is_enabled('adult_content') ) {
|
||
|
|
$out .= "<p class='pkg'>";
|
||
|
|
$out .=
|
||
|
|
"<label for='prop_adult_content_reason' class='left options'>"
|
||
|
|
. BML::ml('entryform.adultcontentreason')
|
||
|
|
. "</label>";
|
||
|
|
$out .= LJ::html_text(
|
||
|
|
{
|
||
|
|
'name' => 'prop_adult_content_reason',
|
||
|
|
'id' => 'prop_adult_content_reason',
|
||
|
|
'class' => 'text',
|
||
|
|
'size' => '35',
|
||
|
|
'maxlength' => '255',
|
||
|
|
'value' => $opts->{'prop_adult_content_reason'},
|
||
|
|
'tabindex' => $tabindex->(),
|
||
|
|
}
|
||
|
|
);
|
||
|
|
$out .= LJ::help_icon_html('adult_content_reason');
|
||
|
|
$out .= "</p>";
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( $remote && !$altlogin ) {
|
||
|
|
|
||
|
|
# crosspost
|
||
|
|
my @accounts = DW::External::Account->get_external_accounts($remote);
|
||
|
|
|
||
|
|
# populate the per-account html first, so that we only have to
|
||
|
|
# go through them once.
|
||
|
|
my $accthtml = "";
|
||
|
|
my $xpostbydefault = 0;
|
||
|
|
my $xpost_tabindex = $tabindex->();
|
||
|
|
my $did_spellcheck = $opts->{spellcheck_html} ? 1 : 0;
|
||
|
|
if ( scalar @accounts ) {
|
||
|
|
my $xpoststring = $opts->{prop_xpost};
|
||
|
|
my $xpost_selected = DW::External::Account->xpost_string_to_hash($xpoststring);
|
||
|
|
foreach my $acct (@accounts) {
|
||
|
|
|
||
|
|
# print the checkbox for each account
|
||
|
|
my $acctid = $acct->acctid;
|
||
|
|
my $acctname = $acct->displayname;
|
||
|
|
my $selected;
|
||
|
|
if ( $opts->{mode} eq 'edit' ) {
|
||
|
|
$selected = $xpost_selected->{ $acct->acctid } ? "1" : "0";
|
||
|
|
}
|
||
|
|
elsif ($did_spellcheck) {
|
||
|
|
$selected = $opts->{"prop_xpost_$acctid"};
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
$selected = $acct->xpostbydefault;
|
||
|
|
}
|
||
|
|
$accthtml .=
|
||
|
|
"<tr><td><label for='prop_xpost_$acctid' class='left options'>$acctname</label></td>\n";
|
||
|
|
$accthtml .= "<td>"
|
||
|
|
. LJ::html_check(
|
||
|
|
{
|
||
|
|
'type' => 'checkbox',
|
||
|
|
'name' => "prop_xpost_$acctid",
|
||
|
|
'id' => "prop_xpost_$acctid",
|
||
|
|
'class' => 'check xpost_acct_checkbox',
|
||
|
|
'value' => '1',
|
||
|
|
'selected' => $selected,
|
||
|
|
'tabindex' => $tabindex->(),
|
||
|
|
'onchange' => 'XPostAccount.xpostAcctUpdated();',
|
||
|
|
}
|
||
|
|
) . "</td>\n";
|
||
|
|
$xpostbydefault = 1 if $selected;
|
||
|
|
|
||
|
|
$accthtml .= "<td>";
|
||
|
|
unless ( $acct->password ) {
|
||
|
|
|
||
|
|
# password field if no password
|
||
|
|
$accthtml .= "<span id='prop_xpost_pwspan_$acctid'>";
|
||
|
|
$accthtml .=
|
||
|
|
"<label for='prop_xpost_password_$acctid'>"
|
||
|
|
. BML::ml('xpost.password')
|
||
|
|
. "</label>";
|
||
|
|
$accthtml .= LJ::html_text(
|
||
|
|
{
|
||
|
|
'name' => "prop_xpost_password_$acctid",
|
||
|
|
'id' => "prop_xpost_password_$acctid",
|
||
|
|
'value' => "",
|
||
|
|
'disabled' => 0,
|
||
|
|
'size' => 40,
|
||
|
|
'maxlength' => 80,
|
||
|
|
'type' => 'password',
|
||
|
|
'class' => 'xpost_pw'
|
||
|
|
}
|
||
|
|
);
|
||
|
|
$accthtml .=
|
||
|
|
"<span class='xpost_pwstatus' id='prop_xpost_pwstatus_$acctid'></span>";
|
||
|
|
$accthtml .=
|
||
|
|
"<input type='hidden' name='prop_xpost_chal_$acctid' id='prop_xpost_chal_$acctid' class='xpost_chal' />";
|
||
|
|
$accthtml .=
|
||
|
|
"<input type='hidden' name='prop_xpost_resp_$acctid' id='prop_xpost_resp_$acctid'/>";
|
||
|
|
$accthtml .= "</span>";
|
||
|
|
}
|
||
|
|
$accthtml .= "</td>\n";
|
||
|
|
|
||
|
|
$accthtml .= "</tr>\n";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
$out .= qq [
|
||
|
|
<script type="text/javascript" language="JavaScript">
|
||
|
|
// xpost messages
|
||
|
|
var xpostUser = '$remote->{user}';
|
||
|
|
];
|
||
|
|
$out .= "var xpostCheckingMessage = '" . BML::ml('xpost.nopw.checking') . "';\n";
|
||
|
|
$out .= "var xpostCancelLabel = '" . BML::ml('xpost.nopw.cancel') . "';\n";
|
||
|
|
$out .= "var xpostPwRequired = '" . BML::ml('xpost.nopw.required') . "';\n";
|
||
|
|
$out .= "</script>\n";
|
||
|
|
$out .= "<div id='xpostdiv'>\n";
|
||
|
|
$out .=
|
||
|
|
"<p><label for='prop_xpost_check' class='left options'>"
|
||
|
|
. BML::ml('entryform.xpost')
|
||
|
|
. "</label>";
|
||
|
|
$out .= LJ::html_check(
|
||
|
|
{
|
||
|
|
'type' => 'checkbox',
|
||
|
|
'name' => 'prop_xpost_check',
|
||
|
|
'id' => 'prop_xpost_check',
|
||
|
|
'class' => 'check',
|
||
|
|
'value' => '1',
|
||
|
|
'selected' => $xpostbydefault,
|
||
|
|
'disabled' => ( scalar @accounts ) ? '0' : '1',
|
||
|
|
'tabindex' => $xpost_tabindex,
|
||
|
|
'onchange' => 'XPostAccount.xpostButtonUpdated();',
|
||
|
|
}
|
||
|
|
);
|
||
|
|
$out .= LJ::help_icon_html('prop_xpost_check');
|
||
|
|
$out .= "<a href = '/manage/settings/?cat=othersites'>"
|
||
|
|
. BML::ml('entryform.xpost.manage') . "</a>";
|
||
|
|
$out .= "</p>\n<table summary=''>";
|
||
|
|
$out .= $accthtml;
|
||
|
|
$out .= "</table>\n";
|
||
|
|
|
||
|
|
$out .= "</div>\n";
|
||
|
|
$out .= qq [
|
||
|
|
<p class='pkg'>
|
||
|
|
<span class='inputgroup-left'></span>
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
### Other Posting Options
|
||
|
|
$out .=
|
||
|
|
LJ::Hooks::run_hook( 'add_extra_entryform_fields',
|
||
|
|
{ opts => $opts, tabindex => $tabindex } )
|
||
|
|
|| '';
|
||
|
|
|
||
|
|
$out .= "<span class='inputgroup-right'>";
|
||
|
|
|
||
|
|
# extra submit button so make sure it posts the form when person presses enter key
|
||
|
|
if ( $opts->{'mode'} eq "edit" ) {
|
||
|
|
$out .= "<input type='submit' name='action:save' class='hidden_submit xpost_submit' />";
|
||
|
|
}
|
||
|
|
if ( $opts->{'mode'} eq "update" ) {
|
||
|
|
$out .=
|
||
|
|
"<input type='submit' name='action:update' class='hidden_submit xpost_submit' />";
|
||
|
|
}
|
||
|
|
|
||
|
|
# submit_value field to emulate the submit button selected if we
|
||
|
|
# have to submit with javascript
|
||
|
|
$out .= "<input type='hidden' name='submit_value' />";
|
||
|
|
|
||
|
|
my $preview;
|
||
|
|
$preview =
|
||
|
|
"<input type='button' value='"
|
||
|
|
. BML::ml('entryform.preview')
|
||
|
|
. "' onclick='entryPreview(this.form)' tabindex='"
|
||
|
|
. $tabindex->() . "' />";
|
||
|
|
if ( !$opts->{'disabled_save'} ) {
|
||
|
|
$out .= <<PREVIEW;
|
||
|
|
<script type="text/javascript" language="JavaScript">
|
||
|
|
<!--
|
||
|
|
if (document.getElementById) {
|
||
|
|
document.write("$preview ");
|
||
|
|
}
|
||
|
|
//-->
|
||
|
|
</script>
|
||
|
|
PREVIEW
|
||
|
|
}
|
||
|
|
if ( $LJ::SPELLER && !$opts->{'disabled_save'} ) {
|
||
|
|
$out .= LJ::html_submit(
|
||
|
|
'action:spellcheck',
|
||
|
|
BML::ml('entryform.spellcheck'),
|
||
|
|
{ onclick => 'XPostAccount.doSpellcheck()', tabindex => $tabindex->() }
|
||
|
|
) . " ";
|
||
|
|
}
|
||
|
|
|
||
|
|
# Update posting date/time
|
||
|
|
$out .=
|
||
|
|
"<input type='button' value='"
|
||
|
|
. BML::ml('entryform.updatedate')
|
||
|
|
. "' onclick='settime(\""
|
||
|
|
. LJ::ejs( BML::ml('entryform.dateupdated') )
|
||
|
|
. "\", this);' tabindex='"
|
||
|
|
. $tabindex->() . "' />";
|
||
|
|
$out .= "</span>\n";
|
||
|
|
$out .= "</p>\n";
|
||
|
|
}
|
||
|
|
|
||
|
|
### Community maintainer bar
|
||
|
|
|
||
|
|
if ( $opts->{'maintainer_mode'} ) {
|
||
|
|
$out .= "<p class='pkg'>\n";
|
||
|
|
$out .= "<em>" . BML::ml('entryform.maintainer') . "</em>\n";
|
||
|
|
$out .= "</p>\n";
|
||
|
|
|
||
|
|
# adult content settings
|
||
|
|
if ( LJ::is_enabled('adult_content') ) {
|
||
|
|
$out .= "<p class='pkg'>\n";
|
||
|
|
my %poster_adult_content_menu = (
|
||
|
|
"" => BML::ml('entryform.adultcontent.default'),
|
||
|
|
none => BML::ml('entryform.adultcontent.none'),
|
||
|
|
concepts => BML::ml('entryform.adultcontent.concepts'),
|
||
|
|
explicit => BML::ml('entryform.adultcontent.explicit'),
|
||
|
|
);
|
||
|
|
|
||
|
|
my @adult_content_menu = (
|
||
|
|
"" => BML::ml(
|
||
|
|
'entryform.adultcontent.poster',
|
||
|
|
{ setting => $poster_adult_content_menu{ $opts->{prop_adult_content} } }
|
||
|
|
),
|
||
|
|
none => BML::ml('entryform.adultcontent.none'),
|
||
|
|
concepts => BML::ml('entryform.adultcontent.concepts'),
|
||
|
|
explicit => BML::ml('entryform.adultcontent.explicit'),
|
||
|
|
);
|
||
|
|
|
||
|
|
$out .=
|
||
|
|
"<label for='prop_adult_content_maintainer' class='left options'>"
|
||
|
|
. BML::ml('entryform.adultcontent.maintainer')
|
||
|
|
. "</label>\n";
|
||
|
|
$out .= LJ::html_select(
|
||
|
|
{
|
||
|
|
name => 'prop_adult_content_maintainer',
|
||
|
|
id => 'prop_adult_content_maintainer',
|
||
|
|
class => 'select',
|
||
|
|
selected => $opts->{prop_adult_content_maintainer} || "",
|
||
|
|
tabindex => $tabindex->(),
|
||
|
|
},
|
||
|
|
@adult_content_menu
|
||
|
|
);
|
||
|
|
$out .= LJ::help_icon_html( "adult_content", "", " " );
|
||
|
|
$out .= "</p>\n";
|
||
|
|
|
||
|
|
$out .= "<p class='pkg'>";
|
||
|
|
$out .=
|
||
|
|
"<label for='prop_adult_content_maintainer_reason' class='left options'>"
|
||
|
|
. BML::ml('entryform.adultcontentreason.maintainer')
|
||
|
|
. "</label>";
|
||
|
|
$out .= LJ::html_text(
|
||
|
|
{
|
||
|
|
'name' => 'prop_adult_content_maintainer_reason',
|
||
|
|
'id' => 'prop_adult_content_maintainer_reason',
|
||
|
|
'class' => 'text',
|
||
|
|
'size' => '35',
|
||
|
|
'maxlength' => '255',
|
||
|
|
'value' => $opts->{'prop_adult_content_maintainer_reason'},
|
||
|
|
'tabindex' => $tabindex->(),
|
||
|
|
}
|
||
|
|
);
|
||
|
|
$out .= LJ::help_icon_html('adult_content_reason');
|
||
|
|
$out .= "</p>";
|
||
|
|
}
|
||
|
|
|
||
|
|
# comment disabling/enabling
|
||
|
|
# only possible if comments weren't disabled by poster
|
||
|
|
unless ( $opts->{prop_opt_nocomments} ) {
|
||
|
|
$out .= "<p class='pkg'>";
|
||
|
|
$out .=
|
||
|
|
"<label for='prop_opt_nocomments_maintainer' class='left options'>"
|
||
|
|
. BML::ml('entryform.comment.disable')
|
||
|
|
. "</label>";
|
||
|
|
|
||
|
|
# comment disabling is done via a checkbox as it has only two settings
|
||
|
|
# if we got this far, this is always set to the maintainer setting
|
||
|
|
my $selected = $opts->{prop_opt_nocomments_maintainer};
|
||
|
|
$out .= LJ::html_check(
|
||
|
|
{
|
||
|
|
type => 'checkbox',
|
||
|
|
name => "prop_opt_nocomments_maintainer",
|
||
|
|
id => "prop_opt_nocomments_maintainer",
|
||
|
|
class => 'check',
|
||
|
|
value => '1',
|
||
|
|
selected => $selected,
|
||
|
|
tabindex => $tabindex->(),
|
||
|
|
}
|
||
|
|
);
|
||
|
|
$out .= "</p>";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
$out .= "</div><!-- end #options -->\n\n";
|
||
|
|
|
||
|
|
### Submit Bar
|
||
|
|
{
|
||
|
|
$out .= "<div id='submitbar' class='pkg'>\n\n";
|
||
|
|
|
||
|
|
# Security
|
||
|
|
my $secbar = 0;
|
||
|
|
if ( $opts->{'mode'} eq "update" || !$opts->{'disabled_save'} ) {
|
||
|
|
my $usejournalu = LJ::load_user( $opts->{usejournal} );
|
||
|
|
my $is_comm = $usejournalu && $usejournalu->is_comm ? 1 : 0;
|
||
|
|
|
||
|
|
my $string_public = LJ::ejs( BML::ml('label.security.public2') );
|
||
|
|
my $string_friends = LJ::ejs( BML::ml('label.security.accesslist') );
|
||
|
|
my $string_friends_comm = LJ::ejs( BML::ml('label.security.members') );
|
||
|
|
my $string_private = LJ::ejs( BML::ml('label.security.private2') );
|
||
|
|
my $string_admin = LJ::ejs( BML::ml('label.security.maintainers') );
|
||
|
|
my $string_custom = LJ::ejs( BML::ml('label.security.custom') );
|
||
|
|
|
||
|
|
$out .= qq{
|
||
|
|
<script>var UpdateFormStrings = new Object();
|
||
|
|
UpdateFormStrings.public = "$string_public";
|
||
|
|
UpdateFormStrings.friends = "$string_friends";
|
||
|
|
UpdateFormStrings.friends_comm = "$string_friends_comm";
|
||
|
|
UpdateFormStrings.private = "$string_private";
|
||
|
|
UpdateFormStrings.custom = "$string_custom";
|
||
|
|
UpdateFormStrings.admin = "$string_admin";</script>
|
||
|
|
};
|
||
|
|
|
||
|
|
$$onload .= " setColumns();" if $remote;
|
||
|
|
my @secs = (
|
||
|
|
"public", $string_public, "friends",
|
||
|
|
$is_comm ? $string_friends_comm : $string_friends
|
||
|
|
);
|
||
|
|
push @secs, ( "private", $string_private ) unless $is_comm;
|
||
|
|
push @secs, ( "private", $string_admin )
|
||
|
|
if $is_comm && $remote && $remote->can_manage($usejournalu);
|
||
|
|
|
||
|
|
my ( @secopts, @trust_groups );
|
||
|
|
@trust_groups = $remote->trust_groups if $remote;
|
||
|
|
if ( scalar @trust_groups && !$is_comm ) {
|
||
|
|
push @secs, ( "custom", $string_custom );
|
||
|
|
push @secopts, ( "onchange" => "customboxes()" );
|
||
|
|
}
|
||
|
|
|
||
|
|
if (@secs) {
|
||
|
|
$secbar = 1;
|
||
|
|
$out .= "<div id='security_container'>\n";
|
||
|
|
$out .= "<label for='security'>" . BML::ml('entryform.security2') . " </label>\n";
|
||
|
|
}
|
||
|
|
|
||
|
|
$out .= LJ::html_select(
|
||
|
|
{
|
||
|
|
'id' => "security",
|
||
|
|
'name' => 'security',
|
||
|
|
'include_ids' => 1,
|
||
|
|
'class' => 'select',
|
||
|
|
'selected' => $opts->{'security'},
|
||
|
|
'tabindex' => $tabindex->(),
|
||
|
|
@secopts
|
||
|
|
},
|
||
|
|
@secs
|
||
|
|
) . "\n";
|
||
|
|
|
||
|
|
# if custom security groups available, show them in a hideable div
|
||
|
|
if ( scalar @trust_groups ) {
|
||
|
|
my $display = $opts->{security} && $opts->{security} eq "custom" ? "block" : "none";
|
||
|
|
$out .= LJ::help_icon( "security", "<span id='security-help'>\n", "\n</span>\n" );
|
||
|
|
$out .= "<div id='custom_boxes' class='pkg' style='display: $display;'>\n";
|
||
|
|
$out .= "<ul id='custom_boxes_list'>";
|
||
|
|
foreach my $group (@trust_groups) {
|
||
|
|
my $fg = $group->{groupnum};
|
||
|
|
$out .= "<li>";
|
||
|
|
$out .= LJ::html_check(
|
||
|
|
{
|
||
|
|
'name' => "custom_bit_$fg",
|
||
|
|
'id' => "custom_bit_$fg",
|
||
|
|
'selected' => $opts->{"custom_bit_$fg"}
|
||
|
|
|| ( $opts->{security_mask} ? $opts->{security_mask} + 0 : 0 ) &
|
||
|
|
1 << $fg
|
||
|
|
}
|
||
|
|
) . " ";
|
||
|
|
$out .=
|
||
|
|
"<label for='custom_bit_$fg'>"
|
||
|
|
. LJ::ehtml( $group->{groupname} )
|
||
|
|
. "</label>\n";
|
||
|
|
$out .= "</li>";
|
||
|
|
}
|
||
|
|
$out .= "</ul>";
|
||
|
|
$out .= "</div><!-- end #custom_boxes -->\n";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( $opts->{'mode'} eq "update" ) {
|
||
|
|
my $onclick = "";
|
||
|
|
|
||
|
|
my $defaultjournal;
|
||
|
|
my $not_a_journal = 0;
|
||
|
|
if ( $opts->{'usejournal'} ) {
|
||
|
|
$defaultjournal = $opts->{'usejournal'};
|
||
|
|
}
|
||
|
|
elsif ( $remote && $opts->{auth_as_remote} ) {
|
||
|
|
$defaultjournal = $remote->user;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
$defaultjournal = "Journal";
|
||
|
|
$not_a_journal = 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
$$onload .= " changeSubmit('" . BML::ml('entryform.update3') . "', '$defaultjournal');";
|
||
|
|
$$onload .= " getUserTags('$defaultjournal');" unless $not_a_journal;
|
||
|
|
$$onload .= " changeSecurityOptions('$defaultjournal');" unless $opts->{'security'};
|
||
|
|
|
||
|
|
$out .= LJ::html_submit(
|
||
|
|
'action:update',
|
||
|
|
BML::ml('entryform.update4'),
|
||
|
|
{
|
||
|
|
'onclick' => $onclick,
|
||
|
|
'class' => 'update_submit xpost_submit',
|
||
|
|
'id' => 'formsubmit',
|
||
|
|
'tabindex' => $tabindex->()
|
||
|
|
}
|
||
|
|
) . " \n";
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( $opts->{'mode'} eq "edit" ) {
|
||
|
|
my $onclick = "";
|
||
|
|
|
||
|
|
if ( !$opts->{'disabled_save'} ) {
|
||
|
|
$out .= LJ::html_submit(
|
||
|
|
'action:save',
|
||
|
|
BML::ml('entryform.save'),
|
||
|
|
{
|
||
|
|
'onclick' => $onclick,
|
||
|
|
'disabled' => $opts->{'disabled_save'},
|
||
|
|
'class' => 'xpost_submit',
|
||
|
|
'tabindex' => $tabindex->()
|
||
|
|
}
|
||
|
|
) . " \n";
|
||
|
|
}
|
||
|
|
elsif ( $opts->{maintainer_mode} ) {
|
||
|
|
$out .= LJ::html_submit(
|
||
|
|
'action:savemaintainer',
|
||
|
|
BML::ml('entryform.save.maintainer'),
|
||
|
|
{
|
||
|
|
'onclick' => $onclick,
|
||
|
|
'disabled' => !$opts->{'maintainer_mode'},
|
||
|
|
'class' => 'xpost_submit',
|
||
|
|
'tabindex' => $tabindex->()
|
||
|
|
}
|
||
|
|
) . " \n";
|
||
|
|
}
|
||
|
|
|
||
|
|
# do a double-confirm on delete if we have crossposts that
|
||
|
|
# would also get removed
|
||
|
|
my $delete_onclick =
|
||
|
|
"return XPostAccount.confirmDelete('"
|
||
|
|
. LJ::ejs( BML::ml('entryform.delete.confirm') ) . "', '"
|
||
|
|
. LJ::ejs( BML::ml('entryform.delete.xposts.confirm') ) . "')";
|
||
|
|
$out .= LJ::html_submit(
|
||
|
|
'action:delete',
|
||
|
|
BML::ml('entryform.delete'),
|
||
|
|
{
|
||
|
|
'disabled' => $opts->{'disabled_delete'},
|
||
|
|
'class' => 'xpost_submit',
|
||
|
|
'tabindex' => $tabindex->(),
|
||
|
|
'onclick' => $delete_onclick
|
||
|
|
}
|
||
|
|
) . " \n";
|
||
|
|
|
||
|
|
if ( !$opts->{'disabled_spamdelete'} ) {
|
||
|
|
$out .= LJ::html_submit(
|
||
|
|
'action:deletespam',
|
||
|
|
BML::ml('entryform.deletespam'),
|
||
|
|
{
|
||
|
|
'onclick' => "return confirm('"
|
||
|
|
. LJ::ejs( BML::ml('entryform.deletespam.confirm') ) . "')",
|
||
|
|
'class' => 'xpost_submit',
|
||
|
|
'tabindex' => $tabindex->()
|
||
|
|
}
|
||
|
|
) . "\n";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
$out .= "</div><!-- end #security_container -->\n\n" if $secbar;
|
||
|
|
$out .= "</div><!-- end #submitbar -->\n\n";
|
||
|
|
$out .= "</div><!-- end #entry-form-wrapper -->\n\n";
|
||
|
|
|
||
|
|
$out .= "<script type='text/javascript'>\n";
|
||
|
|
$out .= "// <![CDATA[ \n ";
|
||
|
|
$out .= "init_update_bml() \n";
|
||
|
|
$out .= "// ]]>\n";
|
||
|
|
$out .= "</script>\n";
|
||
|
|
}
|
||
|
|
return $out;
|
||
|
|
}
|
||
|
|
|
||
|
|
# <LJFUNC>
|
||
|
|
# name: LJ::entry_form_decode
|
||
|
|
# class: web
|
||
|
|
# des: Decodes an entry_form into a protocol-compatible hash.
|
||
|
|
# info: Generate form with [func[LJ::entry_form]].
|
||
|
|
# args: req, post
|
||
|
|
# des-req: protocol request hash to build.
|
||
|
|
# des-post: entry_form POST contents.
|
||
|
|
# returns: req
|
||
|
|
# </LJFUNC>
|
||
|
|
sub entry_form_decode {
|
||
|
|
my ( $req, $POST ) = @_;
|
||
|
|
|
||
|
|
# find security
|
||
|
|
my $sec = "public";
|
||
|
|
my $amask = 0;
|
||
|
|
if ( $POST->{'security'} eq "private" ) {
|
||
|
|
$sec = "private";
|
||
|
|
}
|
||
|
|
elsif ( $POST->{'security'} eq "friends" ) {
|
||
|
|
$sec = "usemask";
|
||
|
|
$amask = 1;
|
||
|
|
}
|
||
|
|
elsif ( $POST->{'security'} eq "custom" ) {
|
||
|
|
$sec = "usemask";
|
||
|
|
foreach my $bit ( 1 .. 60 ) {
|
||
|
|
next unless $POST->{"custom_bit_$bit"};
|
||
|
|
$amask |= ( 1 << $bit );
|
||
|
|
}
|
||
|
|
}
|
||
|
|
$req->{'security'} = $sec;
|
||
|
|
$req->{'allowmask'} = $amask;
|
||
|
|
|
||
|
|
# date/time
|
||
|
|
my $date = LJ::html_datetime_decode( { 'name' => "date_ymd", }, $POST );
|
||
|
|
my ( $year, $mon, $day ) = split( /\D/, $date );
|
||
|
|
my ( $hour, $min ) = ( $POST->{'hour'}, $POST->{'min'} );
|
||
|
|
|
||
|
|
# TEMP: ease golive by using older way of determining differences
|
||
|
|
my $date_old = LJ::html_datetime_decode( { 'name' => "date_ymd_old", }, $POST );
|
||
|
|
my ( $year_old, $mon_old, $day_old ) = split( /\D/, $date_old );
|
||
|
|
my ( $hour_old, $min_old ) = ( $POST->{'hour_old'}, $POST->{'min_old'} );
|
||
|
|
|
||
|
|
my $different = $POST->{'min_old'}
|
||
|
|
&& ( ( $year ne $year_old )
|
||
|
|
|| ( $mon ne $mon_old )
|
||
|
|
|| ( $day ne $day_old )
|
||
|
|
|| ( $hour ne $hour_old )
|
||
|
|
|| ( $min ne $min_old ) );
|
||
|
|
|
||
|
|
# this value is set when the JS runs, which means that the user-provided
|
||
|
|
# time is sync'd with their computer clock. otherwise, the JS didn't run,
|
||
|
|
# so let's guess at their timezone.
|
||
|
|
if ( $POST->{'date_diff'} || $POST->{'date_diff_nojs'} || $different ) {
|
||
|
|
delete $req->{'tz'};
|
||
|
|
$req->{'year'} = $year;
|
||
|
|
$req->{'mon'} = $mon;
|
||
|
|
$req->{'day'} = $day;
|
||
|
|
$req->{'hour'} = $hour;
|
||
|
|
$req->{'min'} = $min;
|
||
|
|
}
|
||
|
|
|
||
|
|
# copy some things from %POST
|
||
|
|
foreach (
|
||
|
|
qw(subject
|
||
|
|
prop_picture_keyword prop_current_moodid
|
||
|
|
prop_current_mood prop_current_music
|
||
|
|
prop_opt_screening prop_opt_noemail
|
||
|
|
prop_opt_preformatted prop_opt_nocomments
|
||
|
|
prop_current_location prop_current_coords
|
||
|
|
prop_taglist )
|
||
|
|
)
|
||
|
|
{
|
||
|
|
$req->{$_} = $POST->{$_};
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( $POST->{"subject"} && ( $POST->{"subject"} eq BML::ml('entryform.subject.hint2') ) ) {
|
||
|
|
$req->{"subject"} = "";
|
||
|
|
}
|
||
|
|
|
||
|
|
$req->{"prop_opt_preformatted"} ||=
|
||
|
|
$POST->{'switched_rte_on'} ? 1
|
||
|
|
: $POST->{event_format} && $POST->{event_format} eq "preformatted" ? 1
|
||
|
|
: 0;
|
||
|
|
$req->{"prop_opt_nocomments"} ||=
|
||
|
|
$POST->{comment_settings} && $POST->{comment_settings} eq "nocomments" ? 1 : 0;
|
||
|
|
$req->{"prop_opt_noemail"} ||=
|
||
|
|
$POST->{comment_settings} && $POST->{comment_settings} eq "noemail" ? 1 : 0;
|
||
|
|
$req->{'prop_opt_backdated'} = $POST->{'prop_opt_backdated'} ? 1 : 0;
|
||
|
|
|
||
|
|
if ( LJ::is_enabled('adult_content') ) {
|
||
|
|
$req->{prop_adult_content} = $POST->{prop_adult_content} || '';
|
||
|
|
$req->{prop_adult_content} = ""
|
||
|
|
unless $req->{prop_adult_content} eq "none"
|
||
|
|
|| $req->{prop_adult_content} eq "concepts"
|
||
|
|
|| $req->{prop_adult_content} eq "explicit";
|
||
|
|
|
||
|
|
$req->{prop_adult_content_reason} = $POST->{prop_adult_content_reason} || "";
|
||
|
|
}
|
||
|
|
|
||
|
|
# nuke taglists that are just blank
|
||
|
|
$req->{'prop_taglist'} = "" unless $req->{'prop_taglist'} && $req->{'prop_taglist'} =~ /\S/;
|
||
|
|
|
||
|
|
# Convert the rich text editor output back to parsable lj tags.
|
||
|
|
my $event = $POST->{'event'};
|
||
|
|
if ( $POST->{'switched_rte_on'} ) {
|
||
|
|
$req->{"prop_used_rte"} = 1;
|
||
|
|
|
||
|
|
# We want to see if we can hit the fast path for cleaning
|
||
|
|
# if they did nothing but add line breaks.
|
||
|
|
my $attempt = $event;
|
||
|
|
$attempt =~ s!<br />!\n!g;
|
||
|
|
|
||
|
|
if ( $attempt !~ /<\w/ ) {
|
||
|
|
$event = $attempt;
|
||
|
|
|
||
|
|
# Make sure they actually typed something, and not just hit
|
||
|
|
# enter a lot
|
||
|
|
$attempt =~ s!(?:<p>(?: |\s)+</p>| )\s*?!!gm;
|
||
|
|
$event = '' unless $attempt =~ /\S/;
|
||
|
|
|
||
|
|
$req->{'prop_opt_preformatted'} = 0;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
# Old methods, left in for compatibility during code push
|
||
|
|
$event =~ s!<lj-cut class="ljcut">!<lj-cut>!gi;
|
||
|
|
|
||
|
|
$event =~ s!<lj-raw class="ljraw">!<lj-raw>!gi;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
$req->{"prop_used_rte"} = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
$req->{'event'} = $event;
|
||
|
|
|
||
|
|
## see if an "other" mood they typed in has an equivalent moodid
|
||
|
|
if ( $POST->{'prop_current_mood'} ) {
|
||
|
|
if ( my $id = DW::Mood->mood_id( $POST->{'prop_current_mood'} ) ) {
|
||
|
|
$req->{'prop_current_moodid'} = $id;
|
||
|
|
delete $req->{'prop_current_mood'};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# process site-specific options
|
||
|
|
LJ::Hooks::run_hooks( 'decode_entry_form', $POST, $req );
|
||
|
|
|
||
|
|
return $req;
|
||
|
|
}
|
||
|
|
|
||
|
|
{
|
||
|
|
my %stat_cache = (); # key -> {lastcheck, modtime}
|
||
|
|
|
||
|
|
sub _file_modtime {
|
||
|
|
my ( $key, $now ) = @_;
|
||
|
|
if ( my $ci = $stat_cache{$key} ) {
|
||
|
|
if ( $ci->{lastcheck} > $now - 10 ) {
|
||
|
|
return $ci->{modtime};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
my $set = sub {
|
||
|
|
my $mtime = shift;
|
||
|
|
$stat_cache{$key} = { lastcheck => $now, modtime => $mtime };
|
||
|
|
return $mtime;
|
||
|
|
};
|
||
|
|
|
||
|
|
my $file;
|
||
|
|
|
||
|
|
# Prefer the compiled version, but fall back to source
|
||
|
|
if ( defined $LJ::STATDOCS ) {
|
||
|
|
$file = $LJ::STATDOCS . '/' . $key;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
$file = LJ::resolve_file("htdocs/$key");
|
||
|
|
}
|
||
|
|
|
||
|
|
my $mtime = defined $file ? ( stat($file) )[9] : undef;
|
||
|
|
return $set->($mtime);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# INTERLUDE: What's up w/ need_res, res_includes, and set_active_resource_group?
|
||
|
|
#
|
||
|
|
# GOOD QUESTION, WONDER-CAT. This is a system for lazy resolution and
|
||
|
|
# deduplication of required CSS/JS files. Any component can declare its own
|
||
|
|
# dependencies without caring where it's being called from, which theoretically
|
||
|
|
# makes things slightly more self-contained and maintainable.
|
||
|
|
# Later (I think?), someone extended it to enable incremental code
|
||
|
|
# modernization. A component can declare multiple implementations of a frontend
|
||
|
|
# feature, specifying a "resource group" for each implementation. When it's
|
||
|
|
# finally time to build a page, the controller sets its preferred resource group
|
||
|
|
# and pulls in only frontend code that's compatible with that group.
|
||
|
|
# As for how to use these, well, hmm. They follow a classic LJ design
|
||
|
|
# pattern that I like to call "launch some garbage into the void / reach
|
||
|
|
# into the void and grab some garbage." Infinite flexibility, zero handrails or
|
||
|
|
# validation. So here's the conventions I've been able to puzzle out.
|
||
|
|
#
|
||
|
|
# * `need_res`: Declares one or more JS/CSS files that need to be loaded for
|
||
|
|
# the current page. need_res([<opts hashref>], <path>, [<path>...])
|
||
|
|
# FILE PATHS: Use paths like "js/jquery.replyforms.js" or
|
||
|
|
# "stc/components/quick-reply.css", relative to the top-level "htdocs"
|
||
|
|
# directory. Paths can ONLY go into the "js" (javascript) or "stc" (CSS)
|
||
|
|
# subdirectories. Note that on a live instance of the app, "stc" also
|
||
|
|
# has the compiled contents of the "scss" directory (minus any files
|
||
|
|
# whose names begin with an underscore [_]).
|
||
|
|
# PRIORITY: The opts hashref can have a "priority" key, whose value is
|
||
|
|
# an integer. This affects the order in which files are added to the
|
||
|
|
# page; otherwise the order is effectively random. Files with bigger
|
||
|
|
# priority values get included first. Existing code only seems to
|
||
|
|
# use two values: 0 (normal) and 3 (prerequisites). 0 is never
|
||
|
|
# referenced explicitly, just defaulted to. 3 is always referenced
|
||
|
|
# indirectly, via the $LJ::LIB_RES_PRIORITY variable.
|
||
|
|
# RESOURCE GROUP: The opts hashref can have a "group" key, whose value
|
||
|
|
# is a string. See "KNOWN RESOURCE GROUPS" below for more details. If
|
||
|
|
# you don't specify a group, need_res puts CSS files in the "all" group
|
||
|
|
# and JS files in the "default" (actually legacy) group.
|
||
|
|
#
|
||
|
|
# * `set_active_resource_group`: Sets the value of a global variable called
|
||
|
|
# $LJ::ACTIVE_RES_GROUP, which then affects the behavior of res_includes. It's
|
||
|
|
# supposed to be called pretty late in the process of building a page. You can
|
||
|
|
# check that variable to see what will _probably_ be happening, but be careful;
|
||
|
|
# there aren't a lot of assurances about how accurate it is at any given time.
|
||
|
|
#
|
||
|
|
# * `res_includes`: Builds the actual HTML tags for the JS/CSS in the current
|
||
|
|
# resource group (plus the special "all" group), and returns them as a string.
|
||
|
|
# Whatever calls res_includes is responsible for putting the result somewhere
|
||
|
|
# useful.
|
||
|
|
# Conceptually, res_includes is only meant to be called once per page, but
|
||
|
|
# since Foundation expects JS to go at the bottom of the <body>, it's more like
|
||
|
|
# "call half of it twice," via the wrapper functions `res_includes_head`
|
||
|
|
# (for CSS and really basic inline JS that everything else relies on) and
|
||
|
|
# `res_includes_body` (JS loaded from files).
|
||
|
|
#
|
||
|
|
# KNOWN RESOURCE GROUPS: There isn't any validation on these, but these are the
|
||
|
|
# values that actually get used by something.
|
||
|
|
# - "foundation" - newer pages.
|
||
|
|
# - "jquery" - pages whose JS was modernized in the early '10s.
|
||
|
|
# - "default" - legacy LJ pages that have basically never been modernized.
|
||
|
|
# - "all" - always included, regardless of the current resource group.
|
||
|
|
# - "fragment" - no idea, tbh. Something involving the template system.
|
||
|
|
#
|
||
|
|
# LIMITATIONS: Lazy resolution doesn't work reliably with non-"siteviews" S2
|
||
|
|
# pages (they can sometimes do JS, but not CSS), so they should know all of
|
||
|
|
# their CSS/JS files BEFORE we start rendering markup. In particular, watch out
|
||
|
|
# for this when using .tt components that call need_res.
|
||
|
|
# Why? To lazy resolve, you need to call res_includes AFTER the inner
|
||
|
|
# content is fully built, which means building the outer frame of the page last.
|
||
|
|
# That's easy for site pages, but since journal styles can override the entire
|
||
|
|
# HTML document, there's not really any protected "outer" part of the page that
|
||
|
|
# core2 can defer.
|
||
|
|
# -NF
|
||
|
|
|
||
|
|
# optional first argument: hashref with options
|
||
|
|
# other arguments: resources to include
|
||
|
|
sub need_res {
|
||
|
|
my %opts;
|
||
|
|
if ( ref $_[0] eq 'HASH' ) {
|
||
|
|
%opts = %{ shift() };
|
||
|
|
}
|
||
|
|
|
||
|
|
my $group = $opts{group};
|
||
|
|
|
||
|
|
# higher priority means it comes first in the ordering
|
||
|
|
my $priority = $opts{priority} || 0;
|
||
|
|
|
||
|
|
foreach my $reskey (@_) {
|
||
|
|
die "Bogus reskey $reskey" unless $reskey =~ m!^(js|stc)/!;
|
||
|
|
|
||
|
|
# we put javascript in the 'default' group and CSS in the 'all' group
|
||
|
|
# since we need CSS everywhere and we are switching JS groups
|
||
|
|
my $lgroup = $group || ( $reskey =~ /^js/ ? 'default' : 'all' );
|
||
|
|
unless ( $LJ::NEEDED_RES{"$lgroup-$reskey"}++ ) {
|
||
|
|
$LJ::NEEDED_RES[$priority] ||= [];
|
||
|
|
|
||
|
|
push @{ $LJ::NEEDED_RES[$priority] }, [ $lgroup, $reskey ];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
sub res_includes {
|
||
|
|
my (%opts) = @_;
|
||
|
|
|
||
|
|
my $include_js = !$opts{nojs};
|
||
|
|
my $include_libs = !$opts{nolib};
|
||
|
|
my $include_stylesheets = !$opts{no_stylesheets};
|
||
|
|
my $include_script_tags = !$opts{no_scripttags};
|
||
|
|
my $include_links = $include_stylesheets || $include_script_tags;
|
||
|
|
|
||
|
|
# TODO: automatic dependencies from external map and/or content of files,
|
||
|
|
# currently it's limited to dependencies on the order you call LJ::need_res();
|
||
|
|
my $ret = "";
|
||
|
|
|
||
|
|
# use correct root and prefixes for SSL pages
|
||
|
|
my ( $siteroot, $imgprefix, $statprefix, $jsprefix, $wstatprefix, $iconprefix );
|
||
|
|
$siteroot = $LJ::SITEROOT;
|
||
|
|
$imgprefix = $LJ::IMGPREFIX;
|
||
|
|
$statprefix = $LJ::STATPREFIX;
|
||
|
|
$jsprefix = $LJ::JSPREFIX;
|
||
|
|
$wstatprefix = $LJ::WSTATPREFIX;
|
||
|
|
$iconprefix = $LJ::USERPIC_ROOT;
|
||
|
|
|
||
|
|
if ($include_js) {
|
||
|
|
|
||
|
|
# find current journal
|
||
|
|
my $r = DW::Request->get;
|
||
|
|
my $journal_base = '';
|
||
|
|
my $journal = '';
|
||
|
|
if ($r) {
|
||
|
|
my $journalid = $r->note('journalid');
|
||
|
|
|
||
|
|
my $ju;
|
||
|
|
$ju = LJ::load_userid($journalid) if $journalid;
|
||
|
|
|
||
|
|
if ($ju) {
|
||
|
|
$journal_base = $ju->journal_base;
|
||
|
|
$journal = $ju->{user};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
my $remote = LJ::get_remote();
|
||
|
|
my $hasremote = $remote ? 1 : 0;
|
||
|
|
|
||
|
|
# ctxpopup prop
|
||
|
|
my $ctxpopup_icons = 1;
|
||
|
|
my $ctxpopup_userhead = 1;
|
||
|
|
$ctxpopup_icons = 0 if $remote && !$remote->opt_ctxpopup_icons;
|
||
|
|
$ctxpopup_userhead = 0 if $remote && !$remote->opt_ctxpopup_userhead;
|
||
|
|
|
||
|
|
# poll for esn inbox updates?
|
||
|
|
my $inbox_update_poll = LJ::is_enabled('inbox_update_poll');
|
||
|
|
|
||
|
|
# are media embeds enabled?
|
||
|
|
my $embeds_enabled = LJ::is_enabled('embed_module');
|
||
|
|
|
||
|
|
# esn ajax enabled?
|
||
|
|
my $esn_async = LJ::is_enabled('esn_ajax');
|
||
|
|
|
||
|
|
my %site = (
|
||
|
|
imgprefix => "$imgprefix",
|
||
|
|
siteroot => "$siteroot",
|
||
|
|
statprefix => "$statprefix",
|
||
|
|
iconprefix => "$iconprefix",
|
||
|
|
currentJournalBase => "$journal_base",
|
||
|
|
currentJournal => "$journal",
|
||
|
|
has_remote => $hasremote,
|
||
|
|
ctx_popup => ( $ctxpopup_icons || $ctxpopup_userhead ),
|
||
|
|
ctx_popup_icons => $ctxpopup_icons,
|
||
|
|
ctx_popup_userhead => $ctxpopup_userhead,
|
||
|
|
inbox_update_poll => $inbox_update_poll,
|
||
|
|
media_embed_enabled => $embeds_enabled,
|
||
|
|
esn_async => $esn_async,
|
||
|
|
user_domain => $LJ::USER_DOMAIN,
|
||
|
|
cmax_comment => LJ::CMAX_COMMENT,
|
||
|
|
);
|
||
|
|
|
||
|
|
my $site_params = to_json( \%site );
|
||
|
|
|
||
|
|
# include standard JS info
|
||
|
|
$ret .= qq {
|
||
|
|
<script type="text/javascript">
|
||
|
|
var Site;
|
||
|
|
if (!Site)
|
||
|
|
Site = {};
|
||
|
|
|
||
|
|
Site = Object.assign(Site, $site_params);
|
||
|
|
</script>
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($include_links) {
|
||
|
|
my $now = time();
|
||
|
|
my %list; # type -> [];
|
||
|
|
my %oldest; # type -> $oldest
|
||
|
|
my %included = ();
|
||
|
|
my $add = sub {
|
||
|
|
my ( $type, $what, $modtime, $order ) = @_;
|
||
|
|
|
||
|
|
# the same file may have been included twice
|
||
|
|
# if it was in two different groups and not JS
|
||
|
|
# so add another check here
|
||
|
|
return if $included{$what};
|
||
|
|
$included{$what} = 1;
|
||
|
|
|
||
|
|
# in the concat-res case, we don't directly append the URL w/
|
||
|
|
# the modtime, but rather do one global max modtime at the
|
||
|
|
# end, which is done later in the tags function.
|
||
|
|
$modtime = '' unless defined $modtime;
|
||
|
|
|
||
|
|
$list{$type} ||= [];
|
||
|
|
push @{ $list{$type}[$order] ||= [] }, $what;
|
||
|
|
$oldest{$type} ||= [];
|
||
|
|
$oldest{$type}[$order] = $modtime
|
||
|
|
if $modtime && $modtime > ( $oldest{$type}[$order] || 0 );
|
||
|
|
};
|
||
|
|
|
||
|
|
# we may not want to pull in the libraries again, say if we're pulling in elements via an ajax load
|
||
|
|
delete $LJ::NEEDED_RES[$LJ::LIB_RES_PRIORITY] unless $include_libs;
|
||
|
|
|
||
|
|
my $order = 0;
|
||
|
|
foreach my $by_priority ( reverse @LJ::NEEDED_RES ) {
|
||
|
|
next unless $by_priority;
|
||
|
|
$order++;
|
||
|
|
|
||
|
|
foreach my $resrow (@$by_priority) {
|
||
|
|
|
||
|
|
# determine if this resource is part of the resource group that is active;
|
||
|
|
# or 'default' if no group explicitly active
|
||
|
|
my ( $group, $key ) = @$resrow;
|
||
|
|
next
|
||
|
|
if $group ne 'all'
|
||
|
|
&& ( ( defined $LJ::ACTIVE_RES_GROUP && $group ne $LJ::ACTIVE_RES_GROUP )
|
||
|
|
|| ( !defined $LJ::ACTIVE_RES_GROUP && $group ne 'default' ) );
|
||
|
|
|
||
|
|
my $path;
|
||
|
|
my $mtime = _file_modtime( $key, $now );
|
||
|
|
if ( $key =~ m!^stc/fck/! || $LJ::FORCE_WSTAT{$key} ) {
|
||
|
|
$path = "w$key"; # wstc/ instead of stc/
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
$path = $key;
|
||
|
|
}
|
||
|
|
|
||
|
|
# if we want to also include a local version of this file, include that too
|
||
|
|
if (@LJ::USE_LOCAL_RES) {
|
||
|
|
if ( grep { lc $_ eq lc $key } @LJ::USE_LOCAL_RES ) {
|
||
|
|
my $inc = $key;
|
||
|
|
$inc =~ s/(\w+)\.(\w+)$/$1-local.$2/;
|
||
|
|
LJ::need_res($inc);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( $path =~ m!^js/(.+)! ) {
|
||
|
|
$add->( "js", $1, $mtime, $order );
|
||
|
|
}
|
||
|
|
elsif ( $path =~ /\.css$/ && $path =~ m!^(w?)stc/(.+)! ) {
|
||
|
|
$add->( "${1}stccss", $2, $mtime, $order );
|
||
|
|
}
|
||
|
|
elsif ( $path =~ /\.js$/ && $path =~ m!^(w?)stc/(.+)! ) {
|
||
|
|
$add->( "${1}stcjs", $2, $mtime, $order );
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
my $tags = sub {
|
||
|
|
my ( $type, $template ) = @_;
|
||
|
|
for my $o ( 0 ... $order ) {
|
||
|
|
my $list;
|
||
|
|
my $template_order = $template;
|
||
|
|
next unless $list = $list{$type}[$o];
|
||
|
|
|
||
|
|
my $csep = join( ',', @$list );
|
||
|
|
$csep .= "?v=" . $oldest{$type}[$o];
|
||
|
|
$template_order =~ s/__+/??$csep/;
|
||
|
|
$ret .= $template_order;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
if ($include_stylesheets) {
|
||
|
|
$tags->(
|
||
|
|
"stccss", "<link rel=\"stylesheet\" type=\"text/css\" href=\"$statprefix/___\" />\n"
|
||
|
|
);
|
||
|
|
$tags->(
|
||
|
|
"wstccss",
|
||
|
|
"<link rel=\"stylesheet\" type=\"text/css\" href=\"$wstatprefix/___\" />\n"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($include_script_tags) {
|
||
|
|
$tags->( "js", "<script type=\"text/javascript\" src=\"$jsprefix/___\"></script>\n" );
|
||
|
|
$tags->(
|
||
|
|
"stcjs", "<script type=\"text/javascript\" src=\"$statprefix/___\"></script>\n"
|
||
|
|
);
|
||
|
|
$tags->(
|
||
|
|
"wstcjs", "<script type=\"text/javascript\" src=\"$wstatprefix/___\"></script>\n"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return $ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
sub res_includes_head {
|
||
|
|
return LJ::res_includes( no_scripttags => 1 );
|
||
|
|
}
|
||
|
|
|
||
|
|
sub res_includes_body {
|
||
|
|
return LJ::res_includes( nojs => 1, no_stylesheets => 1 );
|
||
|
|
}
|
||
|
|
|
||
|
|
# called to set the active resource group
|
||
|
|
sub set_active_resource_group {
|
||
|
|
$LJ::ACTIVE_RES_GROUP = $_[0];
|
||
|
|
}
|
||
|
|
|
||
|
|
# Returns HTML of a dynamic tag could given passed in data
|
||
|
|
# Requires hash-ref of tag => { url => url, value => value }
|
||
|
|
sub tag_cloud {
|
||
|
|
my ( $tags, $opts ) = @_;
|
||
|
|
|
||
|
|
# find sizes of tags, sorted
|
||
|
|
my @sizes = sort { $a <=> $b } map { $tags->{$_}->{'value'} } keys %$tags;
|
||
|
|
|
||
|
|
# remove duplicates:
|
||
|
|
my %sizes = map { $_, 1 } @sizes;
|
||
|
|
@sizes = sort { $a <=> $b } keys %sizes;
|
||
|
|
|
||
|
|
my @tag_names = sort keys %$tags;
|
||
|
|
|
||
|
|
my $percentile = sub {
|
||
|
|
my $n = shift;
|
||
|
|
my $total = scalar @sizes;
|
||
|
|
for ( my $i = 0 ; $i < $total ; $i++ ) {
|
||
|
|
next if $n > $sizes[$i];
|
||
|
|
return $i / $total;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
my $base_font_size = 8;
|
||
|
|
my $font_size_range = $opts->{font_size_range} || 25;
|
||
|
|
my $ret .= "<div id='tagcloud' class='tagcloud'>";
|
||
|
|
my %tagdata = ();
|
||
|
|
foreach my $tag (@tag_names) {
|
||
|
|
my $tagurl = $tags->{$tag}->{'url'};
|
||
|
|
my $ct = $tags->{$tag}->{'value'};
|
||
|
|
my $pt = int( $base_font_size + $percentile->($ct) * $font_size_range );
|
||
|
|
$ret .= "<a ";
|
||
|
|
$ret .= "id='taglink_$tag' " unless $opts->{ignore_ids};
|
||
|
|
$ret .=
|
||
|
|
"href='" . LJ::ehtml($tagurl) . "' style='font-size: ${pt}pt; text-decoration: none'>";
|
||
|
|
$ret .= LJ::ehtml($tag) . "</a>\n";
|
||
|
|
|
||
|
|
# build hash of tagname => final point size for refresh
|
||
|
|
$tagdata{$tag} = $pt;
|
||
|
|
}
|
||
|
|
$ret .= "</div>";
|
||
|
|
|
||
|
|
return $ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
sub control_strip {
|
||
|
|
my %opts = @_;
|
||
|
|
my $user = delete $opts{user};
|
||
|
|
|
||
|
|
my $journal = LJ::load_user($user);
|
||
|
|
my $show_strip = 1;
|
||
|
|
$show_strip = LJ::Hooks::run_hook("show_control_strip")
|
||
|
|
if ( LJ::Hooks::are_hooks("show_control_strip") );
|
||
|
|
|
||
|
|
return "" unless $show_strip;
|
||
|
|
|
||
|
|
my $remote = LJ::get_remote();
|
||
|
|
|
||
|
|
my $r = DW::Request->get;
|
||
|
|
my $passed_in_location = $opts{host} && $opts{uri} ? 1 : 0;
|
||
|
|
my $host = delete $opts{host} || $r->host;
|
||
|
|
my $uri = delete $opts{uri} || $r->uri;
|
||
|
|
|
||
|
|
my $args;
|
||
|
|
my $argshash = {};
|
||
|
|
|
||
|
|
# we need to pass in location explicitly when creating a control strip using JS
|
||
|
|
if ($passed_in_location) {
|
||
|
|
$args = delete $opts{args};
|
||
|
|
LJ::decode_url_string( $args, $argshash );
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
$args = $r->query_string;
|
||
|
|
$argshash = $r->get_args;
|
||
|
|
}
|
||
|
|
|
||
|
|
my $view = delete $opts{view} || $r->note('view');
|
||
|
|
my $view_is = sub { defined $view && $view eq $_[0] };
|
||
|
|
|
||
|
|
my $baseuri = "$LJ::PROTOCOL://$host$uri";
|
||
|
|
|
||
|
|
$baseuri .= $args ? "?$args" : "";
|
||
|
|
my $euri = LJ::eurl($baseuri);
|
||
|
|
my $create_link = LJ::Hooks::run_hook( "override_create_link_on_navstrip", $journal )
|
||
|
|
|| "<a href='$LJ::SITEROOT/create'>"
|
||
|
|
. BML::ml( 'web.controlstrip.links.create', { 'sitename' => $LJ::SITENAMESHORT } ) . "</a>";
|
||
|
|
|
||
|
|
# Build up some common links
|
||
|
|
my %links = (
|
||
|
|
'login' =>
|
||
|
|
"<a href='$LJ::SITEROOT/?returnto=$euri'>$BML::ML{'web.controlstrip.links.login'}</a>",
|
||
|
|
'post_journal' =>
|
||
|
|
"<a href='$LJ::SITEROOT/update'>$BML::ML{'web.controlstrip.links.post2'}</a>",
|
||
|
|
'home' => "<a href='$LJ::SITEROOT/'>" . $BML::ML{'web.controlstrip.links.home'} . "</a>",
|
||
|
|
'recent_comments' =>
|
||
|
|
"<a href='$LJ::SITEROOT/comments/recent'>$BML::ML{'web.controlstrip.links.recentcomments'}</a>",
|
||
|
|
'manage_friends' =>
|
||
|
|
"<a href='$LJ::SITEROOT/manage/circle/'>$BML::ML{'web.controlstrip.links.managecircle'}</a>",
|
||
|
|
'manage_entries' =>
|
||
|
|
"<a href='$LJ::SITEROOT/editjournal'>$BML::ML{'web.controlstrip.links.manageentries'}</a>",
|
||
|
|
'invite_friends' =>
|
||
|
|
"<a href='$LJ::SITEROOT/manage/circle/invite'>$BML::ML{'web.controlstrip.links.invitefriends'}</a>",
|
||
|
|
'create_account' => $create_link,
|
||
|
|
'syndicated_list' =>
|
||
|
|
"<a href='$LJ::SITEROOT/feeds/list'>$BML::ML{'web.controlstrip.links.popfeeds'}</a>",
|
||
|
|
'learn_more' => LJ::Hooks::run_hook('control_strip_learnmore_link')
|
||
|
|
|| "<a href='$LJ::SITEROOT/'>$BML::ML{'web.controlstrip.links.learnmore'}</a>",
|
||
|
|
'explore' => "<a href='$LJ::SITEROOT/explore/'>"
|
||
|
|
. BML::ml( 'web.controlstrip.links.explore', { sitenameabbrev => $LJ::SITENAMEABBREV } )
|
||
|
|
. "</a>",
|
||
|
|
'confirm' =>
|
||
|
|
"<a href='$LJ::SITEROOT/register'>$BML::ML{'web.controlstrip.links.confirm'}</a>",
|
||
|
|
);
|
||
|
|
|
||
|
|
if ($remote) {
|
||
|
|
my $unread = $remote->notification_inbox->unread_count;
|
||
|
|
$links{inbox} .= "<a href='$LJ::SITEROOT/inbox/'>$BML::ML{'web.controlstrip.links.inbox'}";
|
||
|
|
$links{inbox} .= " ($unread)" if $unread;
|
||
|
|
$links{inbox} .= "</a>";
|
||
|
|
|
||
|
|
$links{settings} =
|
||
|
|
"<a href='$LJ::SITEROOT/manage/settings/'>$BML::ML{'web.controlstrip.links.settings'}</a>";
|
||
|
|
$links{'view_friends_page'} =
|
||
|
|
"<a href='"
|
||
|
|
. $remote->journal_base
|
||
|
|
. "/read'>$BML::ML{'web.controlstrip.links.viewreadingpage'}</a>";
|
||
|
|
$links{'add_friend'} =
|
||
|
|
"<a href='$LJ::SITEROOT/circle/$journal->{user}/edit'>$BML::ML{'web.controlstrip.links.addtocircle'}</a>";
|
||
|
|
$links{'edit_friend'} =
|
||
|
|
"<a href='$LJ::SITEROOT/circle/$journal->{user}/edit'>$BML::ML{'web.controlstrip.links.modifycircle'}</a>";
|
||
|
|
$links{'track_user'} =
|
||
|
|
"<a href='$LJ::SITEROOT/manage/tracking/user?journal=$journal->{user}'>$BML::ML{'web.controlstrip.links.trackuser'}</a>";
|
||
|
|
|
||
|
|
if ( $journal->is_syndicated ) {
|
||
|
|
$links{'add_friend'} =
|
||
|
|
"<a href='$LJ::SITEROOT/circle/$journal->{user}/edit?action=subscribe'>$BML::ML{'web.controlstrip.links.addfeed'}</a>";
|
||
|
|
$links{'remove_friend'} =
|
||
|
|
"<a href='$LJ::SITEROOT/circle/$journal->{user}/edit?action=remove'>$BML::ML{'web.controlstrip.links.removefeed'}</a>";
|
||
|
|
}
|
||
|
|
if ( $journal->is_community ) {
|
||
|
|
$links{'join_community'} =
|
||
|
|
"<a href='$LJ::SITEROOT/circle/$journal->{user}/edit'>$BML::ML{'web.controlstrip.links.joincomm'}</a>"
|
||
|
|
unless $journal->is_closed_membership;
|
||
|
|
$links{'leave_community'} =
|
||
|
|
"<a href='$LJ::SITEROOT/circle/$journal->{user}/edit'>$BML::ML{'web.controlstrip.links.leavecomm'}</a>";
|
||
|
|
$links{'watch_community'} =
|
||
|
|
"<a href='$LJ::SITEROOT/circle/$journal->{user}/edit?action=subscribe'>$BML::ML{'web.controlstrip.links.watchcomm'}</a>";
|
||
|
|
$links{'unwatch_community'} =
|
||
|
|
"<a href='$LJ::SITEROOT/circle/$journal->{user}/edit'>$BML::ML{'web.controlstrip.links.removecomm'}</a>";
|
||
|
|
$links{'post_to_community'} =
|
||
|
|
"<a href='$LJ::SITEROOT/update?usejournal=$journal->{user}'>$BML::ML{'web.controlstrip.links.postcomm'}</a>";
|
||
|
|
$links{'edit_community_profile'} =
|
||
|
|
"<a href='$LJ::SITEROOT/manage/profile/?authas=$journal->{user}'>$BML::ML{'web.controlstrip.links.editcommprofile'}</a>";
|
||
|
|
$links{'edit_community_invites'} =
|
||
|
|
"<a href='"
|
||
|
|
. $journal->community_invite_members_url
|
||
|
|
. "'>$BML::ML{'web.controlstrip.links.managecomminvites'}</a>";
|
||
|
|
$links{'edit_community_members'} =
|
||
|
|
"<a href='"
|
||
|
|
. $journal->community_manage_members_url
|
||
|
|
. "'>$BML::ML{'web.controlstrip.links.editcommmembers'}</a>";
|
||
|
|
$links{'track_community'} =
|
||
|
|
"<a href='$LJ::SITEROOT/manage/tracking/user?journal=$journal->{user}'>$BML::ML{'web.controlstrip.links.trackcomm'}</a>";
|
||
|
|
$links{'queue'} =
|
||
|
|
"<a href='"
|
||
|
|
. $journal->moderation_queue_url
|
||
|
|
. "'>$BML::ML{'web.controlstrip.links.queue'}</a>";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
my $journal_display = $journal->ljuser_display;
|
||
|
|
my %statustext = (
|
||
|
|
'yourjournal' => $BML::ML{'web.controlstrip.status.yourjournal'},
|
||
|
|
'yourfriendspage' => $BML::ML{'web.controlstrip.status.yourreadingpage'},
|
||
|
|
'yourfriendsfriendspage' => $BML::ML{'web.controlstrip.status.yournetworkpage'},
|
||
|
|
'personal' => BML::ml( 'web.controlstrip.status.personal', { 'user' => $journal_display } ),
|
||
|
|
'personalfriendspage' => BML::ml(
|
||
|
|
'web.controlstrip.status.personalreadingpage', { 'user' => $journal_display }
|
||
|
|
),
|
||
|
|
'personalfriendsfriendspage' => BML::ml(
|
||
|
|
'web.controlstrip.status.personalnetworkpage', { 'user' => $journal_display }
|
||
|
|
),
|
||
|
|
'community' =>
|
||
|
|
BML::ml( 'web.controlstrip.status.community', { 'user' => $journal_display } ),
|
||
|
|
'syn' => BML::ml( 'web.controlstrip.status.syn', { 'user' => $journal_display } ),
|
||
|
|
'other' => BML::ml( 'web.controlstrip.status.other', { 'user' => $journal_display } ),
|
||
|
|
'mutualtrust' =>
|
||
|
|
BML::ml( 'web.controlstrip.status.mutualtrust', { 'user' => $journal_display } ),
|
||
|
|
'mutualtrust_mutualwatch' => BML::ml(
|
||
|
|
'web.controlstrip.status.mutualtrust_mutualwatch',
|
||
|
|
{ 'user' => $journal_display }
|
||
|
|
),
|
||
|
|
'mutualtrust_watch' =>
|
||
|
|
BML::ml( 'web.controlstrip.status.mutualtrust_watch', { 'user' => $journal_display } ),
|
||
|
|
'mutualtrust_watchedby' => BML::ml(
|
||
|
|
'web.controlstrip.status.mutualtrust_watchedby',
|
||
|
|
{ 'user' => $journal_display }
|
||
|
|
),
|
||
|
|
'mutualwatch' =>
|
||
|
|
BML::ml( 'web.controlstrip.status.mutualwatch', { 'user' => $journal_display } ),
|
||
|
|
'trust_mutualwatch' =>
|
||
|
|
BML::ml( 'web.controlstrip.status.trust_mutualwatch', { 'user' => $journal_display } ),
|
||
|
|
'trust_watch' =>
|
||
|
|
BML::ml( 'web.controlstrip.status.trust_watch', { 'user' => $journal_display } ),
|
||
|
|
'trust_watchedby' =>
|
||
|
|
BML::ml( 'web.controlstrip.status.trust_watchedby', { 'user' => $journal_display } ),
|
||
|
|
'trustedby_mutualwatch' => BML::ml(
|
||
|
|
'web.controlstrip.status.trustedby_mutualwatch',
|
||
|
|
{ 'user' => $journal_display }
|
||
|
|
),
|
||
|
|
'trustedby_watch' =>
|
||
|
|
BML::ml( 'web.controlstrip.status.trustedby_watch', { 'user' => $journal_display } ),
|
||
|
|
'trustedby_watchedby' => BML::ml(
|
||
|
|
'web.controlstrip.status.trustedby_watchedby', { 'user' => $journal_display }
|
||
|
|
),
|
||
|
|
'maintainer' =>
|
||
|
|
BML::ml( 'web.controlstrip.status.maintainer', { 'user' => $journal_display } ),
|
||
|
|
'memberwatcher' =>
|
||
|
|
BML::ml( 'web.controlstrip.status.memberwatcher', { 'user' => $journal_display } ),
|
||
|
|
'watcher' => BML::ml( 'web.controlstrip.status.watcher', { 'user' => $journal_display } ),
|
||
|
|
'member' => BML::ml( 'web.controlstrip.status.member', { 'user' => $journal_display } ),
|
||
|
|
'trusted' => BML::ml( 'web.controlstrip.status.trusted', { 'user' => $journal_display } ),
|
||
|
|
'watched' => BML::ml( 'web.controlstrip.status.watched', { 'user' => $journal_display } ),
|
||
|
|
'trusted_by' =>
|
||
|
|
BML::ml( 'web.controlstrip.status.trustedby', { 'user' => $journal_display } ),
|
||
|
|
'watched_by' =>
|
||
|
|
BML::ml( 'web.controlstrip.status.watchedby', { 'user' => $journal_display } ),
|
||
|
|
);
|
||
|
|
|
||
|
|
# Vars for controlstrip.tt
|
||
|
|
my $template_args = {
|
||
|
|
'view' => $view,
|
||
|
|
'userpic_html' => '',
|
||
|
|
'logo_html' => ( LJ::Hooks::run_hook( 'control_strip_logo', $remote, $journal ) || '' ),
|
||
|
|
'show_login_form' => 0,
|
||
|
|
'links' => \%links,
|
||
|
|
'statustext' => '',
|
||
|
|
|
||
|
|
# remote # only set if logged in
|
||
|
|
# .user => "plainname"
|
||
|
|
# .sessid => integer
|
||
|
|
# .display => "<span class="ljuser">..."
|
||
|
|
# .is_validated => bool
|
||
|
|
# .is_identity => bool
|
||
|
|
'actionlinks' => [],
|
||
|
|
|
||
|
|
# filters # only set if viewing reading or network page
|
||
|
|
# .all => []
|
||
|
|
# .selected => ""
|
||
|
|
'viewoptions' => [],
|
||
|
|
'search_html' => LJ::Widget::Search->render,
|
||
|
|
|
||
|
|
# url of the rendered page, for the login/logout form to redirect back to
|
||
|
|
'returnto' => $baseuri,
|
||
|
|
};
|
||
|
|
|
||
|
|
# Shortcuts for the two nested array refs that get repeatedly dereferenced later
|
||
|
|
my $actionlinks = $template_args->{'actionlinks'};
|
||
|
|
my $viewoptions = $template_args->{'viewoptions'};
|
||
|
|
|
||
|
|
if ($remote) {
|
||
|
|
my $userpic = $remote->userpic;
|
||
|
|
$template_args->{'remote'} = {
|
||
|
|
'sessid' => $remote->session->id || 0,
|
||
|
|
'user' => $remote->user,
|
||
|
|
'display' => $remote->ljuser_display,
|
||
|
|
'is_validated' => $remote->is_validated,
|
||
|
|
'is_identity' => $remote->is_identity,
|
||
|
|
};
|
||
|
|
if ($userpic) {
|
||
|
|
my $wh = $userpic->img_fixedsize( width => 43, height => 43 );
|
||
|
|
$template_args->{'userpic_html'} =
|
||
|
|
"<a href='$LJ::SITEROOT/manage/icons'><img src='"
|
||
|
|
. $userpic->url
|
||
|
|
. "' alt=\"$BML::ML{'web.controlstrip.userpic.alt'}\" title=\"$BML::ML{'web.controlstrip.userpic.title'}\" $wh /></a>";
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
my $tinted_nouserpic_img = "";
|
||
|
|
|
||
|
|
if ( $journal->prop('stylesys') == 2 ) {
|
||
|
|
my $ctx = $LJ::S2::CURR_CTX;
|
||
|
|
my $custom_nav_strip =
|
||
|
|
S2::get_property_value( $ctx, "custom_control_strip_colors" );
|
||
|
|
|
||
|
|
if ( $custom_nav_strip ne "off" ) {
|
||
|
|
my $linkcolor = S2::get_property_value( $ctx, "control_strip_linkcolor" );
|
||
|
|
|
||
|
|
if ( $linkcolor ne "" ) {
|
||
|
|
$tinted_nouserpic_img =
|
||
|
|
S2::Builtin::LJ::palimg_modify( $ctx, "controlstrip/nouserpic.gif",
|
||
|
|
[ S2::Builtin::LJ::PalItem( $ctx, 0, $linkcolor ) ] );
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if ( $tinted_nouserpic_img eq "" ) {
|
||
|
|
$tinted_nouserpic_img = "$LJ::IMGPREFIX/controlstrip/nouserpic.gif";
|
||
|
|
}
|
||
|
|
$template_args->{'userpic_html'} =
|
||
|
|
"<a href='$LJ::SITEROOT/manage/icons'><img src='$tinted_nouserpic_img' alt=\"$BML::ML{'web.controlstrip.nouserpic.alt'}\" title=\"$BML::ML{'web.controlstrip.nouserpic.title'}\" height='43' width='43' /></a>";
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( $remote->equals($journal) ) {
|
||
|
|
if ( $view_is->("read") ) {
|
||
|
|
$template_args->{'statustext'} = $statustext{'yourfriendspage'};
|
||
|
|
}
|
||
|
|
elsif ( $view_is->("network") ) {
|
||
|
|
$template_args->{'statustext'} = $statustext{'yourfriendsfriendspage'};
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
$template_args->{'statustext'} = $statustext{'yourjournal'};
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( $view_is->("read") || $view_is->("network") ) {
|
||
|
|
my @filters = (
|
||
|
|
"all", $BML::ML{'web.controlstrip.select.friends.all'},
|
||
|
|
"showpeople", $BML::ML{'web.controlstrip.select.friends.journals'},
|
||
|
|
"showcommunities", $BML::ML{'web.controlstrip.select.friends.communities'},
|
||
|
|
"showsyndicated", $BML::ML{'web.controlstrip.select.friends.feeds'}
|
||
|
|
);
|
||
|
|
|
||
|
|
# content_filters returns an array of content filters this user had, sorted by sortorder
|
||
|
|
# since this is only shown if $remote->equals( $journal ) , we don't have to care whether a filter is public or not
|
||
|
|
my @custom_filters = $journal->content_filters;
|
||
|
|
|
||
|
|
# Making as few changes to existing behaviour
|
||
|
|
my $default_filter = "default view";
|
||
|
|
foreach my $f (@custom_filters) {
|
||
|
|
|
||
|
|
# Both 'default' and 'default view' are default filters
|
||
|
|
$default_filter = "default" if lc( $f->name ) eq "default";
|
||
|
|
push @filters, "filter:" . lc( $f->name ), $f->name;
|
||
|
|
}
|
||
|
|
|
||
|
|
my $selected = "all";
|
||
|
|
|
||
|
|
# first, change the selection state to reflect any filter in use;
|
||
|
|
# if we have no default filter or if the named filter somehow
|
||
|
|
# fails to exist, this will effectively select nothing
|
||
|
|
if ( $r->uri =~ /^\/read\/?(.+)?/i ) {
|
||
|
|
my $filter = $1 || $default_filter;
|
||
|
|
$selected = "filter:" . LJ::durl( lc($filter) );
|
||
|
|
|
||
|
|
# but don't select the filter if the query string contains filter=0
|
||
|
|
# (fun fact: named filter + filter=0 returns a 404 error)
|
||
|
|
$selected = "all" if $r->query_string && $r->query_string =~ /\bfilter=0\b/;
|
||
|
|
}
|
||
|
|
|
||
|
|
# next, change the selection state to reflect showtypes from getargs;
|
||
|
|
# note this will override the implicit default filter or filter=0 selection
|
||
|
|
# if a match is found, but not a filter explicitly named in the URL.
|
||
|
|
# (of course you can use both! we're just competing for the
|
||
|
|
# state of the pop-up menu in the control strip here)
|
||
|
|
if ( ( $r->uri eq "/read" || $r->uri eq "/network" )
|
||
|
|
&& $r->query_string
|
||
|
|
&& $r->query_string ne "" )
|
||
|
|
{
|
||
|
|
$selected = "showpeople" if $r->query_string =~ /\bshow=P\b/;
|
||
|
|
$selected = "showcommunities" if $r->query_string =~ /\bshow=C\b/;
|
||
|
|
$selected = "showsyndicated" if $r->query_string =~ /\bshow=F\b/;
|
||
|
|
}
|
||
|
|
|
||
|
|
push( @$actionlinks, $links{'manage_friends'} );
|
||
|
|
|
||
|
|
# Data for the reading list filter drop-down:
|
||
|
|
$template_args->{'filters'} = {
|
||
|
|
'all' => \@filters,
|
||
|
|
'selected' => $selected,
|
||
|
|
};
|
||
|
|
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
push( @$actionlinks,
|
||
|
|
$links{'recent_comments'},
|
||
|
|
$links{'manage_entries'},
|
||
|
|
$links{'invite_friends'} );
|
||
|
|
}
|
||
|
|
}
|
||
|
|
elsif ( $journal->is_personal || $journal->is_identity ) {
|
||
|
|
my $trusted = $remote->trusts($journal);
|
||
|
|
my $trusted_by = $journal->trusts($remote);
|
||
|
|
my $mutual_trust = $trusted && $trusted_by ? 1 : 0;
|
||
|
|
my $watched = $remote->watches($journal);
|
||
|
|
my $watched_by = $journal->watches($remote);
|
||
|
|
my $mutual_watch = $watched && $watched_by ? 1 : 0;
|
||
|
|
|
||
|
|
if ( $mutual_trust && $mutual_watch ) {
|
||
|
|
$template_args->{'statustext'} = $statustext{mutualtrust_mutualwatch};
|
||
|
|
push( @$actionlinks, $links{edit_friend} );
|
||
|
|
}
|
||
|
|
elsif ( $mutual_trust && $watched ) {
|
||
|
|
$template_args->{'statustext'} = $statustext{mutualtrust_watch};
|
||
|
|
push( @$actionlinks, $links{edit_friend} );
|
||
|
|
}
|
||
|
|
elsif ( $mutual_trust && $watched_by ) {
|
||
|
|
$template_args->{'statustext'} = $statustext{mutualtrust_watchedby};
|
||
|
|
push( @$actionlinks, $links{edit_friend} );
|
||
|
|
}
|
||
|
|
elsif ( $trusted && $mutual_watch ) {
|
||
|
|
$template_args->{'statustext'} = $statustext{trust_mutualwatch};
|
||
|
|
push( @$actionlinks, $links{edit_friend} );
|
||
|
|
}
|
||
|
|
elsif ( $trusted_by && $mutual_watch ) {
|
||
|
|
$template_args->{'statustext'} = $statustext{trustedby_mutualwatch};
|
||
|
|
push( @$actionlinks, $links{edit_friend} );
|
||
|
|
}
|
||
|
|
elsif ($mutual_trust) {
|
||
|
|
$template_args->{'statustext'} = $statustext{mutualtrust};
|
||
|
|
push( @$actionlinks, $links{edit_friend} );
|
||
|
|
}
|
||
|
|
elsif ($mutual_watch) {
|
||
|
|
$template_args->{'statustext'} = $statustext{mutualwatch};
|
||
|
|
push( @$actionlinks, $links{edit_friend} );
|
||
|
|
}
|
||
|
|
elsif ( $trusted && $watched ) {
|
||
|
|
$template_args->{'statustext'} = $statustext{trust_watch};
|
||
|
|
push( @$actionlinks, $links{edit_friend} );
|
||
|
|
}
|
||
|
|
elsif ( $trusted && $watched_by ) {
|
||
|
|
$template_args->{'statustext'} = $statustext{trust_watchedby};
|
||
|
|
push( @$actionlinks, $links{edit_friend} );
|
||
|
|
}
|
||
|
|
elsif ( $trusted_by && $watched ) {
|
||
|
|
$template_args->{'statustext'} = $statustext{trustedby_watch};
|
||
|
|
push( @$actionlinks, $links{edit_friend} );
|
||
|
|
}
|
||
|
|
elsif ( $trusted_by && $watched_by ) {
|
||
|
|
$template_args->{'statustext'} = $statustext{trustedby_watchedby};
|
||
|
|
push( @$actionlinks, $links{add_friend} );
|
||
|
|
}
|
||
|
|
elsif ($trusted) {
|
||
|
|
$template_args->{'statustext'} = $statustext{trusted};
|
||
|
|
push( @$actionlinks, $links{edit_friend} );
|
||
|
|
}
|
||
|
|
elsif ($trusted_by) {
|
||
|
|
$template_args->{'statustext'} = $statustext{trusted_by};
|
||
|
|
push( @$actionlinks, $links{add_friend} );
|
||
|
|
}
|
||
|
|
elsif ($watched) {
|
||
|
|
$template_args->{'statustext'} = $statustext{watched};
|
||
|
|
push( @$actionlinks, $links{edit_friend} );
|
||
|
|
}
|
||
|
|
elsif ($watched_by) {
|
||
|
|
$template_args->{'statustext'} = $statustext{watched_by};
|
||
|
|
push( @$actionlinks, $links{add_friend} );
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
if ( $view_is->("read") ) {
|
||
|
|
$template_args->{'statustext'} = $statustext{'personalfriendspage'};
|
||
|
|
}
|
||
|
|
elsif ( $view_is->("network") ) {
|
||
|
|
$template_args->{'statustext'} = $statustext{'personalfriendsfriendspage'};
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
$template_args->{'statustext'} = $statustext{'personal'};
|
||
|
|
}
|
||
|
|
push( @$actionlinks, $links{'add_friend'} );
|
||
|
|
}
|
||
|
|
push( @$actionlinks, $links{'track_user'} );
|
||
|
|
}
|
||
|
|
elsif ( $journal->is_community ) {
|
||
|
|
my $watching = $remote->watches($journal);
|
||
|
|
my $memberof = $remote->member_of($journal);
|
||
|
|
my $haspostingaccess = $remote->can_post_to($journal);
|
||
|
|
my $isclosedcommunity = $journal->is_closed_membership;
|
||
|
|
|
||
|
|
if ( $remote->can_manage_other($journal) ) {
|
||
|
|
$template_args->{'statustext'} = "$statustext{maintainer}";
|
||
|
|
push( @$actionlinks, $links{post_to_community} )
|
||
|
|
if $haspostingaccess;
|
||
|
|
|
||
|
|
if ( $journal->prop('moderated') ) {
|
||
|
|
push( @$actionlinks, "$links{queue} [" . $journal->get_mod_queue_count . "]" );
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
push( @$actionlinks, $links{edit_community_profile} );
|
||
|
|
}
|
||
|
|
|
||
|
|
push( @$actionlinks,
|
||
|
|
$links{edit_community_invites},
|
||
|
|
$links{edit_community_members} );
|
||
|
|
|
||
|
|
}
|
||
|
|
elsif ( $watching && $memberof ) {
|
||
|
|
$template_args->{'statustext'} = $statustext{memberwatcher};
|
||
|
|
push( @$actionlinks, $links{post_to_community} )
|
||
|
|
if $haspostingaccess;
|
||
|
|
push( @$actionlinks, $links{leave_community} );
|
||
|
|
push( @$actionlinks, $links{track_community} );
|
||
|
|
|
||
|
|
}
|
||
|
|
elsif ($watching) {
|
||
|
|
$template_args->{'statustext'} = $statustext{watcher};
|
||
|
|
push( @$actionlinks, $links{post_to_community} )
|
||
|
|
if $haspostingaccess;
|
||
|
|
push( @$actionlinks,
|
||
|
|
$isclosedcommunity
|
||
|
|
? "This is a closed community"
|
||
|
|
: $links{join_community} );
|
||
|
|
push( @$actionlinks, $links{unwatch_community} );
|
||
|
|
push( @$actionlinks, $links{track_community} );
|
||
|
|
|
||
|
|
}
|
||
|
|
elsif ($memberof) {
|
||
|
|
$template_args->{'statustext'} = $statustext{member};
|
||
|
|
push( @$actionlinks, $links{post_to_community} )
|
||
|
|
if $haspostingaccess;
|
||
|
|
push( @$actionlinks,
|
||
|
|
$links{watch_community}, $links{'leave_community'},
|
||
|
|
$links{track_community} );
|
||
|
|
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
$template_args->{'statustext'} = $statustext{community};
|
||
|
|
push( @$actionlinks, $links{post_to_community} )
|
||
|
|
if $haspostingaccess;
|
||
|
|
push( @$actionlinks,
|
||
|
|
$isclosedcommunity
|
||
|
|
? "This is a closed community"
|
||
|
|
: $links{join_community} );
|
||
|
|
push( @$actionlinks, $links{watch_community}, $links{track_community} );
|
||
|
|
}
|
||
|
|
}
|
||
|
|
elsif ( $journal->is_syndicated ) {
|
||
|
|
$template_args->{'statustext'} = $statustext{syn};
|
||
|
|
if ( $remote && !$remote->watches($journal) ) {
|
||
|
|
push( @$actionlinks, $links{add_friend} );
|
||
|
|
}
|
||
|
|
elsif ( $remote && $remote->watches($journal) ) {
|
||
|
|
push( @$actionlinks, $links{remove_friend} );
|
||
|
|
}
|
||
|
|
push( @$actionlinks, $links{syndicated_list} );
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
$template_args->{'statustext'} = $statustext{other};
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
$template_args->{'userpic_html'} =
|
||
|
|
LJ::Hooks::run_hook( 'control_strip_loggedout_userpic_contents', $euri ) || "";
|
||
|
|
|
||
|
|
my $show_login_form = LJ::Hooks::run_hook( "show_control_strip_login_form", $journal );
|
||
|
|
$show_login_form = 1 if !defined $show_login_form;
|
||
|
|
|
||
|
|
$template_args->{'show_login_form'} = $show_login_form;
|
||
|
|
|
||
|
|
if ( $journal->is_personal || $journal->is_identity ) {
|
||
|
|
if ( $view_is->("read") ) {
|
||
|
|
$template_args->{'statustext'} = $statustext{'personalfriendspage'};
|
||
|
|
}
|
||
|
|
elsif ( $view_is->("network") ) {
|
||
|
|
$template_args->{'statustext'} = $statustext{'personalfriendsfriendspage'};
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
$template_args->{'statustext'} = $statustext{'personal'};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
elsif ( $journal->is_community ) {
|
||
|
|
$template_args->{'statustext'} = $statustext{'community'};
|
||
|
|
}
|
||
|
|
elsif ( $journal->is_syndicated ) {
|
||
|
|
$template_args->{'statustext'} = $statustext{'syn'};
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
$template_args->{'statustext'} = $statustext{'other'};
|
||
|
|
}
|
||
|
|
|
||
|
|
push( @$actionlinks, $links{'login'} ) unless $show_login_form;
|
||
|
|
push( @$actionlinks, $links{'create_account'}, $links{'learn_more'} );
|
||
|
|
}
|
||
|
|
|
||
|
|
# search box and ?style=mine/?style=light/?style=original/?style=site options
|
||
|
|
# determine whether style is "mine", and define new uri variable to manipulate
|
||
|
|
# note: all expressions case-insensitive
|
||
|
|
my $current_style = determine_viewing_style( $r->get_args, $view, $remote );
|
||
|
|
|
||
|
|
# a quick little routine to use when cycling through the options
|
||
|
|
# to create the style links for the nav bar
|
||
|
|
my $make_style_link = sub {
|
||
|
|
return LJ::ehtml(
|
||
|
|
create_url(
|
||
|
|
$uri,
|
||
|
|
host => $host,
|
||
|
|
cur_args => $argshash,
|
||
|
|
|
||
|
|
# change the style arg
|
||
|
|
'args' => { 'style' => $_[0] },
|
||
|
|
|
||
|
|
# keep any other existing arguments
|
||
|
|
'keep_args' => 1,
|
||
|
|
)
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
# cycle through all possibilities, add the valid ones
|
||
|
|
foreach my $view_type (qw( mine site light original )) {
|
||
|
|
|
||
|
|
# only want to offer this option if user is logged in and it's not their own journal, since
|
||
|
|
# original will take care of that
|
||
|
|
if ( $view_type eq "mine"
|
||
|
|
and $current_style ne $view_type
|
||
|
|
and $remote
|
||
|
|
and not $remote->equals($journal) )
|
||
|
|
{
|
||
|
|
push @$viewoptions,
|
||
|
|
"<a href='"
|
||
|
|
. $make_style_link->($view_type) . "'>"
|
||
|
|
. LJ::Lang::ml('web.controlstrip.reloadpage.mystyle2') . "</a>";
|
||
|
|
}
|
||
|
|
elsif (
|
||
|
|
$view_type eq "site"
|
||
|
|
and $current_style ne $view_type
|
||
|
|
and defined $view
|
||
|
|
and {
|
||
|
|
entry => 1,
|
||
|
|
reply => 1,
|
||
|
|
icons => 1,
|
||
|
|
}->{$view}
|
||
|
|
)
|
||
|
|
{
|
||
|
|
push @$viewoptions,
|
||
|
|
"<a href='"
|
||
|
|
. $make_style_link->($view_type) . "'>"
|
||
|
|
. LJ::Lang::ml('web.controlstrip.reloadpage.sitestyle') . "</a>";
|
||
|
|
}
|
||
|
|
elsif ( $view_type eq "light" and $current_style ne $view_type ) {
|
||
|
|
push @$viewoptions,
|
||
|
|
"<a href='"
|
||
|
|
. $make_style_link->($view_type) . "'>"
|
||
|
|
. LJ::Lang::ml('web.controlstrip.reloadpage.lightstyle2') . "</a>";
|
||
|
|
}
|
||
|
|
elsif ( $view_type eq "original" and $current_style ne $view_type ) {
|
||
|
|
push @$viewoptions,
|
||
|
|
"<a href='"
|
||
|
|
. $make_style_link->($view_type) . "'>"
|
||
|
|
. LJ::Lang::ml('web.controlstrip.reloadpage.origstyle2') . "</a>";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return DW::Template->template_string( 'journal/controlstrip.tt', $template_args );
|
||
|
|
}
|
||
|
|
|
||
|
|
sub control_strip_js_inject {
|
||
|
|
my %opts = @_;
|
||
|
|
my $user = delete $opts{user} || '';
|
||
|
|
|
||
|
|
my $ret;
|
||
|
|
|
||
|
|
my $r = DW::Request->get;
|
||
|
|
my $host = $r->host;
|
||
|
|
my $uri = $r->uri;
|
||
|
|
my $args = LJ::eurl( $r->query_string ) || '';
|
||
|
|
my $view = $r->note('view') || '';
|
||
|
|
|
||
|
|
$ret .= qq{
|
||
|
|
<script type='text/javascript'>
|
||
|
|
jQuery(function(jQ){
|
||
|
|
if (jQ("#lj_controlstrip").length == 0) {
|
||
|
|
jQ.getJSON("/$user/__rpc_controlstrip?user=$user&host=$host&uri=$uri&args=$args&view=$view", {},
|
||
|
|
function(data) {
|
||
|
|
jQ("<div></div>").html(data.control_strip).prependTo("body");
|
||
|
|
}
|
||
|
|
);
|
||
|
|
}
|
||
|
|
})
|
||
|
|
</script>};
|
||
|
|
|
||
|
|
return $ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
# For the Rich Text Editor
|
||
|
|
# Set JS variables for use by the RTE
|
||
|
|
sub rte_js_vars {
|
||
|
|
my ($remote) = @_;
|
||
|
|
|
||
|
|
my $ret = '';
|
||
|
|
|
||
|
|
# The JS var canmakepoll is used by fckplugin.js to change the behaviour
|
||
|
|
# of the poll button in the RTE.
|
||
|
|
# Also remove any RTE buttons that have been set to disabled.
|
||
|
|
my $canmakepoll = "true";
|
||
|
|
$canmakepoll = "false" if ( $remote && !$remote->can_create_polls );
|
||
|
|
$ret .= "<script type='text/javascript'>\n";
|
||
|
|
$ret .= " var RTEdisabled = new Array();\n";
|
||
|
|
my $rte_disabled = $LJ::DISABLED{rte_buttons} || {};
|
||
|
|
foreach my $key ( keys %$rte_disabled ) {
|
||
|
|
$ret .= " RTEdisabled['$key'] = true;" if $rte_disabled->{$key};
|
||
|
|
}
|
||
|
|
|
||
|
|
$ret .= qq^
|
||
|
|
var canmakepoll = $canmakepoll;
|
||
|
|
|
||
|
|
function removeDisabled(ToolbarSet) {
|
||
|
|
for (var i=0; i<ToolbarSet.length; i++) {
|
||
|
|
for (var j=0; j<ToolbarSet[i].length; j++) {
|
||
|
|
if (RTEdisabled[ToolbarSet[i][j]] == true) ToolbarSet[i].splice(j,1);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
var SiteConfig = new Object();
|
||
|
|
</script>^;
|
||
|
|
|
||
|
|
return $ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
# prints out UI for subscribing to some events
|
||
|
|
sub subscribe_interface {
|
||
|
|
my ( $u, %opts ) = @_;
|
||
|
|
|
||
|
|
croak "subscribe_interface wants a \$u" unless LJ::isu($u);
|
||
|
|
|
||
|
|
my $categories = delete $opts{categories} || [];
|
||
|
|
my $journalu = delete $opts{journal} || LJ::get_remote();
|
||
|
|
my $def_notes = delete $opts{default_selected_notifications} || [];
|
||
|
|
my $num_per_page = delete $opts{num_per_page} || 250;
|
||
|
|
my $page = delete $opts{page} || 1;
|
||
|
|
my $settings_page = delete $opts{settings_page} || 0;
|
||
|
|
|
||
|
|
croak "Invalid user object passed to subscribe_interface" unless LJ::isu($journalu);
|
||
|
|
|
||
|
|
croak "Invalid options passed to subscribe_interface" if ( scalar keys %opts );
|
||
|
|
|
||
|
|
my @notify_classes = LJ::NotificationMethod->all_classes;
|
||
|
|
return "No notification methods" unless @notify_classes;
|
||
|
|
|
||
|
|
my $page_vars = { settings_page => $settings_page };
|
||
|
|
$page_vars->{ntypeids} = join ',', map { $_->ntypeid } @notify_classes;
|
||
|
|
|
||
|
|
# skip the inbox type; it's always on
|
||
|
|
@notify_classes = grep { $_ ne 'LJ::NotificationMethod::Inbox' } @notify_classes;
|
||
|
|
$page_vars->{notify_classes} = \@notify_classes;
|
||
|
|
|
||
|
|
my @catids;
|
||
|
|
my $catid = 0;
|
||
|
|
my @catdata; # this is eventually passed to the display template
|
||
|
|
my @prev_cats;
|
||
|
|
|
||
|
|
my %num_subs_by_type = (); # for the subscription_stats hook
|
||
|
|
|
||
|
|
# pagination variables
|
||
|
|
my $displayed_tracking_start = ( $page - 1 ) * $num_per_page;
|
||
|
|
my $displayed_tracking_end = $displayed_tracking_start + $num_per_page - 1;
|
||
|
|
my $displayed_tracking_count = 0;
|
||
|
|
|
||
|
|
foreach my $cat_hash (@$categories) {
|
||
|
|
my ( $category, $cat_events ) = %$cat_hash;
|
||
|
|
|
||
|
|
# is this category the tracking category?
|
||
|
|
my $is_tracking_category = $category eq "Subscription Tracking";
|
||
|
|
|
||
|
|
my $cat_data = { catid => $catid };
|
||
|
|
push @catids, $catid;
|
||
|
|
my $cat_empty = 1;
|
||
|
|
|
||
|
|
my $cat_title_key = lc($category);
|
||
|
|
$cat_title_key =~ s/ /-/g;
|
||
|
|
$cat_data->{title_key} = $cat_title_key;
|
||
|
|
|
||
|
|
# add notifytype headings
|
||
|
|
$cat_data->{notify_headers} = [];
|
||
|
|
|
||
|
|
foreach my $notify_class (@notify_classes) {
|
||
|
|
my $title = eval { $notify_class->title($u) } or next;
|
||
|
|
my $ntypeid = $notify_class->ntypeid or next;
|
||
|
|
|
||
|
|
# create the checkall box for this event type.
|
||
|
|
my $disabled = !$notify_class->configured_for_user($u);
|
||
|
|
|
||
|
|
if ( $notify_class->disabled_url && $disabled ) {
|
||
|
|
$title = "<a href='" . $notify_class->disabled_url . "'>$title</a>";
|
||
|
|
}
|
||
|
|
elsif ( $notify_class->url ) {
|
||
|
|
$title = "<a href='" . $notify_class->url . "'>$title</a>";
|
||
|
|
}
|
||
|
|
$title .= " " . LJ::help_icon( $notify_class->help_url ) if $notify_class->help_url;
|
||
|
|
|
||
|
|
push @{ $cat_data->{notify_headers} },
|
||
|
|
{ ntypeid => $ntypeid, title => $title, disabled => $disabled };
|
||
|
|
}
|
||
|
|
|
||
|
|
# build list of subscriptions to show the user
|
||
|
|
$cat_data->{pending_subs} = [];
|
||
|
|
|
||
|
|
my @pending_subscriptions =
|
||
|
|
$is_tracking_category
|
||
|
|
? @$cat_events
|
||
|
|
: $u->subscription_event_filter( $cat_events, $journalu, $settings_page );
|
||
|
|
|
||
|
|
# inbox method
|
||
|
|
my $special_subs = 0;
|
||
|
|
my $unavailable_subs = 0;
|
||
|
|
my $sub_count = 0;
|
||
|
|
foreach my $pending_sub (@pending_subscriptions) {
|
||
|
|
my $sub_data = $u->pending_sub_data($pending_sub);
|
||
|
|
next unless $sub_data;
|
||
|
|
|
||
|
|
$sub_data->{altrow_class} = $sub_count % 2 == 1 ? "odd" : "even";
|
||
|
|
$sub_data->{inactive_class} = $sub_data->{inactive} ? "inactive" : "";
|
||
|
|
$sub_data->{disabled_class} = $sub_data->{disabled} ? "disabled" : "";
|
||
|
|
|
||
|
|
$sub_data->{hidden_style} = $sub_data->{hidden} ? " style='visibility: hidden;'" : "";
|
||
|
|
|
||
|
|
if ( $sub_data->{special_sub} ) {
|
||
|
|
$special_subs++;
|
||
|
|
|
||
|
|
push @{ $cat_data->{pending_subs} }, $sub_data;
|
||
|
|
$sub_count++;
|
||
|
|
next;
|
||
|
|
}
|
||
|
|
|
||
|
|
$unavailable_subs++ if $sub_data->{disabled};
|
||
|
|
|
||
|
|
my $evt_class = $pending_sub->event_class or next;
|
||
|
|
next if !$evt_class->is_visible && $settings_page;
|
||
|
|
|
||
|
|
# override disabled below if always_checked (can't uncheck)
|
||
|
|
my $always_checked = eval { "$evt_class"->always_checked; };
|
||
|
|
$sub_data->{disabled} = 1 if $always_checked;
|
||
|
|
|
||
|
|
if ($is_tracking_category) {
|
||
|
|
next if $u->tracked_event_exclude( $pending_sub, \@prev_cats );
|
||
|
|
|
||
|
|
my $do_show = 1;
|
||
|
|
$do_show = 0
|
||
|
|
unless $displayed_tracking_count >= $displayed_tracking_start
|
||
|
|
&& $displayed_tracking_count <= $displayed_tracking_end;
|
||
|
|
$displayed_tracking_count++;
|
||
|
|
|
||
|
|
if ( $do_show && $sub_data->{subscribed} ) {
|
||
|
|
my $subid = $pending_sub->id;
|
||
|
|
|
||
|
|
$sub_data->{subid} = $subid;
|
||
|
|
$sub_data->{auth_token} = $u->ajax_auth_token(
|
||
|
|
"/__rpc_esn_subs",
|
||
|
|
subid => $subid,
|
||
|
|
action => 'delsub'
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
$sub_data->{do_show} = $do_show;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
$sub_data->{do_show} = 1;
|
||
|
|
|
||
|
|
next unless eval { $evt_class->subscription_applicable($pending_sub) };
|
||
|
|
next
|
||
|
|
if $u->equals($journalu)
|
||
|
|
&& $pending_sub->journalid
|
||
|
|
&& $pending_sub->journalid != $u->{userid};
|
||
|
|
}
|
||
|
|
|
||
|
|
$cat_empty = 0; # no more nexts
|
||
|
|
|
||
|
|
# this hook also populates %num_subs_by_type
|
||
|
|
$sub_data->{notif_options} = LJ::Hooks::run_hook(
|
||
|
|
'subscription_notif_options',
|
||
|
|
u => $u,
|
||
|
|
sub_data => $sub_data,
|
||
|
|
pending_sub => $pending_sub,
|
||
|
|
def_notes => $def_notes,
|
||
|
|
is_tracking_category => $is_tracking_category,
|
||
|
|
num_subs_by_type => \%num_subs_by_type,
|
||
|
|
notify_classes => \@notify_classes,
|
||
|
|
);
|
||
|
|
|
||
|
|
push @{ $cat_data->{pending_subs} }, $sub_data;
|
||
|
|
$sub_count++;
|
||
|
|
}
|
||
|
|
|
||
|
|
# show blurb if not tracking anything
|
||
|
|
$cat_data->{empty_blurb} = $cat_empty && $is_tracking_category;
|
||
|
|
$cat_data->{special_subs} = $special_subs;
|
||
|
|
$cat_data->{show_upgrade_note} = !$u->is_paid && ( $special_subs || $unavailable_subs );
|
||
|
|
|
||
|
|
push @catdata, $cat_data;
|
||
|
|
push @prev_cats, $cat_hash;
|
||
|
|
|
||
|
|
$catid++;
|
||
|
|
}
|
||
|
|
|
||
|
|
$page_vars->{catdata} = \@catdata;
|
||
|
|
$page_vars->{catids} = join ',', @catids;
|
||
|
|
|
||
|
|
# show how many subscriptions we have active / inactive
|
||
|
|
$page_vars->{subscription_stats} =
|
||
|
|
LJ::Hooks::run_hook( 'subscription_stats', $u, \%num_subs_by_type );
|
||
|
|
|
||
|
|
$page_vars->{pagination} = LJ::paging_bar(
|
||
|
|
$page,
|
||
|
|
ceil( $displayed_tracking_count / $num_per_page ),
|
||
|
|
{
|
||
|
|
self_link => sub {
|
||
|
|
return LJ::create_url(
|
||
|
|
undef,
|
||
|
|
args => { page => $_[0] },
|
||
|
|
keep_args => 1,
|
||
|
|
no_blank => 1,
|
||
|
|
fragment => "category-subscription-tracking"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
);
|
||
|
|
|
||
|
|
return DW::Template->template_string( 'tracking/subscribe-interface.tt', $page_vars );
|
||
|
|
}
|
||
|
|
|
||
|
|
# returns a placeholder link
|
||
|
|
sub placeholder_link {
|
||
|
|
my (%opts) = @_;
|
||
|
|
|
||
|
|
my $placeholder_html = LJ::ejs_all( delete $opts{placeholder_html} || '' );
|
||
|
|
my $width = delete $opts{width} || 100;
|
||
|
|
my $height = delete $opts{height} || 100;
|
||
|
|
my $width_unit = delete $opts{width_unit} || "px";
|
||
|
|
my $height_unit = delete $opts{height_unit} || "px";
|
||
|
|
my $link = delete $opts{link} || '';
|
||
|
|
my $url = delete $opts{url} || '';
|
||
|
|
my $linktext = delete $opts{linktext} || '';
|
||
|
|
my $img = delete $opts{img} || "$LJ::IMGPREFIX/videoplaceholder.png";
|
||
|
|
|
||
|
|
my $direct_link =
|
||
|
|
defined $url
|
||
|
|
? '<div class="embed_link"><a href="' . $url . '">' . $linktext . '</a></div>'
|
||
|
|
: '';
|
||
|
|
return qq {
|
||
|
|
<div class="LJ_Placeholder_Container" style="width: ${width}${width_unit}; height: ${height}${height_unit};">
|
||
|
|
<div class="LJ_Placeholder_HTML" style="display: none;">$placeholder_html</div>
|
||
|
|
<div class="LJ_Container"></div>
|
||
|
|
<a href="$link">
|
||
|
|
<img src="$img" class="LJ_Placeholder" title="Click to show embedded content" />
|
||
|
|
</a>
|
||
|
|
</div>
|
||
|
|
$direct_link
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
# this returns the right max length for a VARCHAR(255) database
|
||
|
|
# column. but in HTML, the maxlength is characters, not bytes, so we
|
||
|
|
# have to assume 3-byte chars and return 80 instead of 255. (80*3 ==
|
||
|
|
# 240, approximately 255). However, we special-case Russian where
|
||
|
|
# they often need just a little bit more, and make that 100. because
|
||
|
|
# their bytes are only 2, so 100 * 2 == 200. as long as russians
|
||
|
|
# don't enter, say, 100 characters of japanese... but then it'd get
|
||
|
|
# truncated or throw an error. we'll risk that and give them 20 more
|
||
|
|
# characters.
|
||
|
|
sub std_max_length {
|
||
|
|
my $lang = eval { BML::get_language() };
|
||
|
|
return 80 if !$lang || $lang =~ /^en/;
|
||
|
|
return 100 if $lang =~ /\b(hy|az|be|et|ka|ky|kk|lt|lv|mo|ru|tg|tk|uk|uz)\b/i;
|
||
|
|
return 80;
|
||
|
|
}
|
||
|
|
|
||
|
|
sub final_head_html {
|
||
|
|
my $ret = "";
|
||
|
|
|
||
|
|
if ( my $pagestats_obj = LJ::PageStats->new ) {
|
||
|
|
$ret .= $pagestats_obj->render_head;
|
||
|
|
}
|
||
|
|
|
||
|
|
return $ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
# returns HTML which should appear before </body>
|
||
|
|
sub final_body_html {
|
||
|
|
my $before_body_close = "";
|
||
|
|
LJ::Hooks::run_hooks( 'insert_html_before_body_close', \$before_body_close );
|
||
|
|
|
||
|
|
if ( my $pagestats_obj = LJ::PageStats->new ) {
|
||
|
|
$before_body_close .= $pagestats_obj->render;
|
||
|
|
}
|
||
|
|
|
||
|
|
return $before_body_close;
|
||
|
|
}
|
||
|
|
|
||
|
|
# return a unique per pageview string based on the remote's unique cookie
|
||
|
|
sub pageview_unique_string {
|
||
|
|
my $cached_uniq = $LJ::REQ_GLOBAL{pageview_unique_string};
|
||
|
|
return $cached_uniq if $cached_uniq;
|
||
|
|
|
||
|
|
my $uniq = LJ::UniqCookie->current_uniq . time() . LJ::rand_chars(8);
|
||
|
|
$uniq = Digest::SHA1::sha1_hex($uniq);
|
||
|
|
|
||
|
|
$LJ::REQ_GLOBAL{pageview_unique_string} = $uniq;
|
||
|
|
return $uniq;
|
||
|
|
}
|
||
|
|
|
||
|
|
# returns canonical link for use in header of journal pages
|
||
|
|
sub canonical_link {
|
||
|
|
my ( $url, $tid ) = @_;
|
||
|
|
if ( $tid += 0 ) { # sanitize input
|
||
|
|
$url .= "?thread=$tid" . LJ::Talk::comment_anchor($tid);
|
||
|
|
}
|
||
|
|
return qq{<link rel="canonical" href="$url" />\n};
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
# Takes a string as input and returns a canonicalized slug. This is used in
|
||
|
|
# the logslugs table for URL generation.
|
||
|
|
sub canonicalize_slug {
|
||
|
|
return undef unless defined $_[0];
|
||
|
|
|
||
|
|
# If you change this, please update htdocs/stc/js/jquery.postform.js
|
||
|
|
# to keep the regular expressions in the toSlug function in sync.
|
||
|
|
my $str = LJ::trim( lc shift );
|
||
|
|
$str =~ s/\s+/-/g;
|
||
|
|
$str =~ s/[^a-z0-9_-]//gi;
|
||
|
|
$str =~ s/-+/-/g;
|
||
|
|
$str =~ s/^-|-$//g;
|
||
|
|
$str = LJ::text_trim( $str, 255, 100 );
|
||
|
|
|
||
|
|
return $str;
|
||
|
|
}
|
||
|
|
|
||
|
|
1;
|