mourningdove/htdocs/update.bml

668 lines
28 KiB
Text
Raw Permalink Normal View History

2026-05-24 01:03:05 +00:00
<?_c
# 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.
_c?>
<?_code
{
use strict;
use vars qw(%GET %POST %ML);
use DW::External::Page;
BML::decl_params(_default => qr/./);
# $_[1] is a pre-request scratch area
# put variables here so that we can access them later
# outside of this _code block
my $title = \$_[1]->{'title'};
my $head = \$_[1]->{'head'};
my $body = \$_[1]->{'body'};
my $bodyopts = \$_[1]->{'bodyopts'};
my $onload = \$_[1]->{'onload'};
$$title = $ML{'.title2'};
# invalid text input?
unless (LJ::text_in(\%POST)) {
$$body = "<?badinput?>";
return;
}
# invalid usejournal
if ($GET{usejournal} && !LJ::load_user($GET{usejournal})) {
$$body = $ML{'.error.invalidusejournal'};
return;
}
# get remote and see if they can post right now
my $remote = LJ::get_remote();
# Errors that are unlikely to change between starting
# to compose an entry and submitting it.
if ($remote) {
return BML::redirect( LJ::create_url( "/entry/new", cur_args => \%GET, keep_args => 1 ) )
if ! LJ::did_post() && LJ::BetaFeatures->user_in_beta( $remote => "updatepage" );
if ($remote->identity) {
$$title = $ML{'Sorry'};
$$body = BML::ml('.error.nonusercantpost', {'sitename' => $LJ::SITENAME});
return;
}
if (! $remote->can_post ) {
$$title = $ML{'.error.cantpost.title'};
$$body = $LJ::MSG_NO_POST || $ML{'.error.cantpost'};
return;
}
}
my %res = ();
# see if we need to do any transformations
LJ::Hooks::run_hooks("transform_update_$POST{transform}", \%GET, \%POST) if $POST{transform};
LJ::need_res( { priority => $LJ::OLD_RES_PRIORITY }, 'stc/entry.css' );
LJ::need_res('stc/display_none.css', 'stc/lj_base.css', 'js/6alib/inputcomplete.js');
## figure out times
my $now = DateTime->now;
# if user has timezone, use it!
if ($remote && $remote->prop("timezone")) {
my $tz = $remote->prop("timezone");
$tz = $tz ? eval { DateTime::TimeZone->new(name => $tz); } : undef;
$now = eval { DateTime->from_epoch(epoch => time(), time_zone => $tz); }
if $tz;
}
my ($year, $mon, $mday, $hour, $min) = ($now->year,
sprintf("%02d", $now->month),
sprintf("%02d", $now->day),
$now->hour,
sprintf("%02d", $now->minute));
my $subject = $POST{'subject'} || $GET{'subject'};
my $event = $POST{'event'} || $GET{'event'};
my $tags = $POST{'prop_taglist'} || $GET{'prop_taglist'};
# if a share url was passed in, fill in the fields with the appropriate text
if ( $GET{share} && ( my $page = DW::External::Page->new( url => $GET{share} ) ) ) {
$subject = LJ::ehtml( $page->title );
$event = '<a href="' . $page->url . '">' . ( LJ::ehtml( $page->description ) || $subject || $page->url ) . "</a>\n\n";
}
# try to call a hook to fill in the fields
my $override_fields = LJ::Hooks::run_hook('update_fields', \%GET);
my $opt_preformatted = 0;
if ($override_fields) {
$event = $override_fields->{'event'} if exists($override_fields->{'event'});
$subject = $override_fields->{'subject'} if exists($override_fields->{'subject'});
$tags = $override_fields->{'tags'} if exists($override_fields->{'tags'});
$opt_preformatted = $override_fields->{'prop_opt_preformatted'} if exists($override_fields->{'prop_opt_preformatted'});
}
### define some bools with common logic ###
my $did_post = LJ::did_post() && !$POST{transform}; # transforms aren't posts
my $user_is_remote = $remote && $remote->{'user'} eq $POST{'user'}; # user is remote
my $auth_as_remote = $remote && (! $GET{'altlogin'} || $user_is_remote); # auth as remote
my $auth_missing = $POST{'user'} &&
! $POST{'password'} &&
! $user_is_remote &&
! $POST{'response'}; # user w/o password
# which authentication option do we display by default?
my $altlogin_display = 'none';
my $remotelogin_display = 'none';
if ($auth_as_remote) {
$remotelogin_display = 'block';
} else {
$altlogin_display = 'block';
}
# Check for errors, store in hash to render later
my $errors;
my $showform = $POST{'showform'} || $auth_missing; # show entry form
my $preview = $POST{'action:preview'};
# are we spellchecking before we post?
my $did_spellcheck; my $spellcheck_html;
if ($LJ::SPELLER && $POST{'action:spellcheck'}) {
$did_spellcheck++;
my $s = new LJ::SpellCheck { 'spellcommand' => $LJ::SPELLER,
'color' => '<?hotcolor?>', };
$spellcheck_html = $s->check_html(\$event);
$spellcheck_html = "<?inerr $ML{'entryform.spellcheck.noerrors'} inerr?>" unless $spellcheck_html ne "";
my $date = LJ::html_datetime_decode({ 'name' => "date_ymd", }, \%POST);
($year, $mon, $mday) = split( /\D/, $date);
$hour = $POST{'hour'};
$min = $POST{'min'};
}
my $print_entry_form = sub {
my $opts = shift;
# authentication box
my $auth = '';
if ($altlogin_display eq 'none') {
$auth.= "<p id='remotelogin' class='pkg'>\n";
$auth .= "<label for='current_username' class='left'>" . BML::ml('entryform.postas') . "</label>\n";
$auth .= "<strong id='current_username'>" . $remote->{user} . "</strong> <a href='$LJ::SITEROOT/update?altlogin=1' id='remotelogin_content' class='small'>$ML{'entryform.switchuser'}</a>\n";
$auth .= "</p>\n\n";
}
# table with username/password fields
$auth .= "<div id='altlogin_wrapper' style='display: $altlogin_display;'>";
$auth .= "<p class='pkg'>\n";
$auth .= "<label for='altlogin_username' class='left'>$ML{'.username'}</label>\n";
$auth .= LJ::html_text({ 'name' => 'user', 'id' => 'altlogin_username', 'class' => 'text', 'size' => '15',
'maxlength' => '25', 'tabindex' => '5', 'value' => $POST{'user'} || $GET{'user'} }) . "\n";
$auth .= "</p>\n";
$auth .= "<p class='pkg'>\n";
$auth .= "<label for='altlogin_password' class='left'>$ML{'.password'}</label>\n";
$auth .= LJ::html_text({ 'type' => 'password', 'id' => 'altlogin_password', 'class' => 'text',
'name' => 'password', 'tabindex' => '6', 'size' => '15', 'maxlength' => $LJ::PASSWORD_MAXLENGTH }) . "\n";
# posted with a user, but no password
if ($did_post && $auth_missing) {
$auth .= "<br /><?inerr $ML{'.error.nopass'} inerr?>";
}
$auth .= "</p>\n\n";
$auth .= "</div>";
# if they submit the form and are spellchecking, remember
# their settings from the GET requests
my $getextra = '?';
$getextra .= "altlogin=1&" if $GET{'altlogin'};
chop $getextra;
# if crossposter values were checked, remember them
my %xpost_vals;
foreach ( grep { /^prop_xpost_/ } ( keys %POST, keys %GET ) ) {
$xpost_vals{ $_ } = $POST{ $_ } || $GET{ $_ };
}
my $entry = {
'mode' => "update",
'auth_as_remote' => $auth_as_remote,
'subject' => $subject,
'event' => $event,
'prop_taglist' => $tags,
'datetime' => "$year-$mon-$mday $hour:$min",
'usejournal' => LJ::canonical_username($POST{'usejournal'} || $GET{'usejournal'}),
'auth' => $auth,
'remote' => $remote,
'spellcheck_html' => $spellcheck_html,
'clientversion' => "WebUpdate/2.0.0",
'richtext' => LJ::is_enabled('richtext'),
'richtext_default' => $remote ? $remote->new_entry_editor eq 'rich' ? 1 : 0 # User setting
: $LJ::DEFAULT_EDITOR eq 'rich' ? 1 : 0, # Site default
'include_insert_object' => $GET{'insobj'},
'altlogin' => $GET{altlogin} ? 1 : 0,
'prop_opt_preformatted' => $opt_preformatted ? 1 : 0,
%xpost_vals,
};
if ($remote) {
$entry->{prop_opt_default_noemail} = $remote->prop('opt_gettalkemail');
$entry->{prop_opt_default_nocomments} = $remote->prop('opt_showtalklinks');
$entry->{prop_opt_default_screening} = $remote->prop('opt_whoscreened');
$entry->{prop_last_fm_user} = $remote->prop('last_fm_user');
}
if ($did_post) {
$entry->{$_} = $POST{$_} foreach keys %POST;
# Copy things over from the transform
} elsif (LJ::did_post()) {
foreach (qw(event_format richtext_default)) {
$entry->{$_} = $POST{$_} if defined $POST{$_};
}
}
# If they got an error, or spellchecked, and we're in rich text mode, enable rich text mode:
if ($did_post && $POST{'switched_rte_on'}) {
$entry->{richtext_default} = 1;
}
if (LJ::isu($remote) && (!$did_post || $did_spellcheck) && $remote->readonly) {
$$body .= "<div id='readonly'><?warningbar ";
if ($LJ::HELPURL{'readonly'}) {
$$body .= BML::ml('.rowarn', {
'a_open' => "<a href='$LJ::HELPURL{readonly}'>",
'a_close' => "</a>"}
);
} else {
$$body .= BML::ml('.rowarn', {
'a_open' => '',
'a_close' => ''}
);
}
$$body .= " warningbar?><br /></div>";
}
$$body .= "\n\n<form method='post' action='update$getextra' id='updateForm' name='updateForm'>\n\n";
$$body .= LJ::form_auth();
$$body .= LJ::entry_form($entry, \$$head, $onload, $errors);
$$body .= "</form><!-- end #updateForm -->\n";
return;
};
my $okay_formauth = !$remote || LJ::check_form_auth();
if ($did_post && !$did_spellcheck && !$showform && !$preview &&
$okay_formauth && !$POST{'moreoptsbtn'} )
{
# what's our authentication scheme for subsequent protocol actions?
my $flags = {};
my ($u, $user);
if ($POST{'user'} && # user argument given
! $user_is_remote && # user != remote
(!$remote || $GET{'altlogin'})) { # user has clicked alt auth
$user = $POST{'user'};
$u = LJ::load_user($user);
# Verify entered password, if it is present.
my $ok = LJ::auth_okay($u, $POST{password});
$flags = { 'noauth' => 1, 'u' => $u } if $ok;
} elsif ($remote && LJ::check_referer()) {
# assume remote if we have it
$flags = { 'noauth' => 1, 'u' => $remote };
$user = $remote->{'user'};
$u = $remote;
}
# Check if the account they're posting to is read-only
my $uj = $POST{'usejournal'} ? LJ::load_user($POST{'usejournal'}) : $u;
if ($uj && $uj->readonly) {
# Tell the user they can't post since read only
$$body .= "<?errorbar ";
$$body .= "<strong>$ML{'.error.update'}</strong> ";
$$body .= $LJ::MSG_READONLY_USER;
$$body .= " errorbar?><br />";
$print_entry_form->();
return
}
# do a login action
my $login_message;
{
# build a clientversion string
my $clientversion = "Web/2.0.0";
$clientversion .= 's' if $did_spellcheck;
# build a request object
my %req = ( 'mode' => 'login',
'ver' => $LJ::PROTOCOL_VER,
'clientversion' => $clientversion,
'user' => $user,
);
my %res;
LJ::do_request(\%req, \%res, $flags);
# error logging in ?
unless ($res{'success'} eq 'OK') {
$errors->{'auth'} = $ML{'.error.login'} . " " . LJ::ehtml($res{'errmsg'});
}
# server login message for user?
$login_message = LJ::auto_linkify(LJ::ehtml($res{'message'}))
if $res{'message'};
}
# any messages from the server?
if ($login_message) {
$$body .= "<?p <strong>$ML{'.loggingin'}</strong> $ML{'.servermsg'} p?><div style='margin-left: 40px'><b>$login_message</b></div>";
}
my %req = ( 'mode' => 'postevent',
'ver' => $LJ::PROTOCOL_VER,
'user' => $user,
'password' => $POST{'password'},
'usejournal' => $POST{'usejournal'},
'tz' => 'guess',
'xpost' => '0'
);
LJ::entry_form_decode(\%req, \%POST);
if ($req{'event'} eq "") {
$errors->{'entry'} = $ML{'.error.noentry'};
}
my %res;
LJ::do_request(\%req, \%res, $flags);
# check for spam domains
LJ::Hooks::run_hooks('spam_check', $u, \%req, 'entry');
if (!$errors) {
# examine response
my $protocol_message;
if ($res{'success'} eq "OK" && $res{'message'}) {
$protocol_message = LJ::auto_linkify(LJ::ehtml($res{'message'}));
}
if ($res{'success'} ne 'OK') {
# update failed?
$$body .= "<?p <strong>$ML{'.update.status.failed'}</strong> ";
$$body .= "<br /><?errorbar <strong>$ML{'.error.update'}</strong> ";
$$body .= LJ::ehtml($res{'errmsg'}) . " errorbar?>";
$$body .= "<br /> p?>";
} else {
# update succeeded
$$body .= "<?p <strong>$ML{'.update.status.succeeded'}</strong> ";
# persist the default value of the disable auto-formatting option
$u->disable_auto_formatting( $POST{event_format} ? 1 : 0 );
# Clear out a draft
$remote->set_prop('entry_draft', '')
if $remote;
# Store what editor they last used
unless (!$remote || $remote->prop('entry_editor') =~ /^always_/) {
$POST{'switched_rte_on'} ?
$remote->set_prop('entry_editor', 'rich') :
$remote->set_prop('entry_editor', 'plain');
}
$$body .= "<table summary='' width='100%'><tr valign='top'><td>\n\n";
my ($ju, $itemlink);
# short bail if this was posted moderated or some other special case (no itemid but a message)
if (!defined $res{itemid} && $res{message}) {
$$body .= "<br />$res{message} p?>";
} else {
# update success
my $updatemessage = '.update.success2';
if ($POST{'usejournal'}) {
$ju = LJ::load_user($POST{'usejournal'}); # posting as community
} elsif ($user) {
$ju = LJ::load_user($user); # posting not as user from form
} else {
$ju = $remote; # posting as remote
};
if ( $ju->is_community ) { # changes the update message if journal is from a community
$updatemessage = '.update.success2.community';
}
$$body .= BML::ml( $updatemessage, {'aopts' => "href='" . $ju->journal_base . "/'"} );
$$body .= "<br />$protocol_message" if $protocol_message;
my $juser = $ju->{'user'};
my ($itemid, $anum) = ($res{'itemid'}, $res{'anum'});
$itemlink = LJ::item_link($ju, $itemid, $anum);
my $orig_itemid = $itemid;
$itemid = $itemid * 256 + $anum;
my $edititemlink = "/editjournal?journal=$juser&itemid=$itemid";
# crosspost if we're posting to our own journal and have
# selected crosspost.
if ($ju == $remote && ($POST{prop_xpost_check} || $GET{prop_xpost_check})) {
my ($xpost_successes, $xpost_errors) =
LJ::Protocol::schedule_xposts($remote, $itemid, 0,
sub {
my $acctid = (shift)->acctid;
($POST{"prop_xpost_$acctid"} || $GET{"prop_xpost_$acctid"},
{password => $POST{"prop_xpost_password_$acctid"}
|| $GET{"prop_xpost_password_$acctid"},
auth_challenge => $POST{"prop_xpost_chal_$acctid"}
|| $GET{"prop_xpost_chal_$acctid"},
auth_response => $POST{"prop_xpost_resp_$acctid"}
|| $GET{"prop_xpost_resp_$acctid"}})
});
$$body .= "<ul>\n";
$$body .= join("\n",
map { "<li>"
. BML::ml('xpost.request.success2',
{ account => $_->displayname, sitenameshort => $LJ::SITENAMESHORT })
. "</li>" }
@{$xpost_successes});
$$body .= join("\n",
map { "<li><div style='color: red;'><strong>"
. BML::ml('xpost.request.failed',
{account => $_->displayname,
editurl => $edititemlink })
. " </strong></div></li>" }
@{$xpost_errors});
$$body .= "</ul>\n";
$$body .= "<br/>";
}
my @after_entry_post_extra_options = LJ::Hooks::run_hooks('after_entry_post_extra_options', user => $ju, itemlink => $itemlink);
my $after_entry_post_extra_options = join('', map {$_->[0]} @after_entry_post_extra_options) || '';
my $security_ml;
my $filternames = '';
my $c_or_p = $ju->is_community ? 'c' : 'p';
if ( $req{"security"} eq "private" ) {
$security_ml = "post.security.private.$c_or_p";
} elsif ( $req{"security"} eq "usemask" ) {
if ( $req{"allowmask"} == 0 ) { # custom security with no group -- essentially private
$security_ml = "post.security.private.$c_or_p";
} elsif ( $req{"allowmask"} > 1 ) { # custom group
$filternames = $ju->security_group_display( $req{"allowmask"} );
$security_ml = "post.security.custom";
} else { # access list
$security_ml = "post.security.access.$c_or_p";
}
} else { # public
$security_ml = "post.security.public";
}
$$body .= " p?><?p " . BML::ml( $security_ml, { filters => $filternames } );
my $subject = $req{subject};
if ( length $subject > 0 ) {
# use the HTML cleaner on the entry subject,
# then display it without escaping
LJ::CleanHTML::clean_subject( \$subject );
} else {
$subject = $ML{'.extradata.subject.no_subject'};
}
$$body .= sprintf( " p?><?p %s %s", $ML{'.extradata.subj'}, $subject );
$$body .= " p?><?p $ML{'.success.links'} p?><ul>" .
"<li><a href=\"$itemlink\">$ML{'.success.links.view'}</a></li>" .
"<li><a href=\"$edititemlink\">$ML{'.success.links.edit'}</a></li>" .
"<li><a href=\"/tools/memadd?journal=$juser&itemid=$itemid\">$ML{'.success.links.memories'}</a></li>" .
"<li><a href=\"/edittags?journal=$juser&itemid=$itemid\">$ML{'.success.links.tags'}</a></li>" .
$after_entry_post_extra_options .
"</ul>";
}
$$body .= "</td><td style=\"padding-left: 2em;\">";
$$body .= "</td></tr></table>";
$$body .= LJ::Hooks::run_hook('after_entry_post_extra_html', user => $ju, itemlink => $itemlink, request => \%req);
return;
}
}
}
$$body .= "<table summary='' width='100%'><tr valign='top'><td>";
$print_entry_form->();
$$body .= "</td>";
$$body .= "<td>";
$$body .= "</td></tr></table>";
return;
}
_code?><?page
title=> <?_code return $_[1]->{'title'}; _code?>
body=> <?_code return $_[1]->{'body'}; _code?>
bodyopts=><?_code return $_[1]->{'bodyopts'}; _code?>
head<=
<?_code
{
use strict;
use Storable;
my $ret = $_[1]->{'head'};
LJ::need_res(qw(
js/6alib/core.js
js/6alib/dom.js
js/6alib/httpreq.js
js/livejournal.js
js/entry.js
js/poll.js
js/browserdetect.js
js/md5.js
js/xpost.js
));
# draft autosave and restore
# The $draft variable contains the draft itself. The hash contains the various
# fields of the user's draft properties (subject, tags, music, etc.)
my $remote = LJ::get_remote();
my $draft = '""';
my %draft_properties;
my $draft_subject_raw = "";
if ($remote) {
# Here we get the value of the userprop 'draft_properties', containing
# a frozen Storable string, which we then thaw into a hash by the same
# name.
$draft = LJ::ejs_string($remote->prop('entry_draft'));
%draft_properties = $remote->prop( 'draft_properties' ) ?
%{ Storable::thaw( $remote->prop( 'draft_properties' ) ) } : ();
# store raw for later use; will be escaped later
$draft_subject_raw = $draft_properties{subject};
%draft_properties = map { $_ => LJ::ejs_string( $draft_properties{$_} ) }
qw( subject userpic taglist moodid mood location1 music adultreason commentset commentscr adultcnt );
}
my $eMLautosave = LJ::ejs(BML::ml('.draft.autosave', { 'time' => '[[time]]' }));
my $eMLrestored = LJ::ejs($ML{'.draft.restored'});
# not enough to just escape the draft_subject, we want to escape the entire thing, just in case the translation text
# for.draft.confirm2 contains JS-breaking characters such as apostrophes
my $eMLconfirm = LJ::ejs_string( BML::ml( '.draft.confirm2',
{ subjectline => "\"$draft_subject_raw\"" } ) );
# Setup draft saving and try to restore from a draft
# unless we did a post action
my $initDraft = '';
if ( $remote && LJ::is_enabled('update_draft') ) {
# While transforms aren't considered posts, we don't want to
# prompt the user to restore from a draft on a transform
if (!LJ::did_post()) {
$initDraft = 'initDraft(true);';
} else {
$initDraft = 'initDraft(false);';
}
}
my $pageload = $LJ::SPELLER && $POST{'action:spellcheck'} ? "pageload(0);" : "pageload(1);";
# JS vars for the RTE
$ret .= LJ::rte_js_vars($remote);
# Turning off BML parsing for the rest of this code block
# The draft might contain BML like syntax and cause problems
BML::noparse();
$ret .= qq^
<script type="text/javascript">
//<![CDATA[
// These JS variables contain the contents of the various draft fields.
var restoredDraft = $draft;
var restoredSubject = $draft_properties{subject};
var restoredUserpic = $draft_properties{userpic};
var restoredTaglist = $draft_properties{taglist};
var restoredMoodID = $draft_properties{moodid};
var restoredMood = $draft_properties{mood};
var restoredLocation = $draft_properties{location1};
var restoredMusic = $draft_properties{music};
var restoredAdultReason = $draft_properties{adultreason};
var restoredCommentSet = $draft_properties{commentset};
var restoredCommentScr = $draft_properties{commentscr};
var restoredAdultCnt = $draft_properties{adultcnt};
function init_update_bml() {
$initDraft
$_[1]->{'onload'}
$pageload
}
// TODO: make these function calls
LJDraft.autoSaveInterval = $LJ::AUTOSAVE_DRAFT_INTERVAL;
LJDraft.savedMsg = '$eMLautosave';
function initDraft(askToRestore) {
if (askToRestore && restoredDraft) {
if (confirm($eMLconfirm)) {
// If the user wants to restore the draft, we place the
// values of their saved draft into the form.
\$("draft").value = restoredDraft;
\$("draftstatus").value = '$eMLrestored ';
\$("subject").value = restoredSubject;
if ( \$("prop_picture_keyword") ) {
\$("prop_picture_keyword").selectedIndex = restoredUserpic;
}
\$("prop_taglist").value = restoredTaglist;
\$("prop_current_moodid").selectedIndex = restoredMoodID;
\$("prop_current_mood").value = restoredMood;
\$("prop_current_location").value = restoredLocation;
\$("prop_current_music").value = restoredMusic;
\$("prop_adult_content_reason").value = restoredAdultReason;
\$("comment_settings").selectedIndex = restoredCommentSet;
\$("prop_opt_screening").selectedIndex = restoredCommentScr;
\$("prop_adult_content").selectedIndex = restoredAdultCnt;
} else {
// Clear out their current draft
LJDraft.save('');
LJDraft.clearProperties();
}
}
LJDraft.startTimer();
}
//]]>
</script>^;
my $chalresp_js = qq{
<script type="text/javascript" src="$LJ::JSPREFIX/md5.js"></script>
};
$ret .= (! $LJ::REQ_HEAD_HAS{'chalresp_js'}++) ? $chalresp_js : "";
return $ret;
}
_code?>
<=head
page?>