148 lines
4.4 KiB
Perl
148 lines
4.4 KiB
Perl
#!/usr/bin/perl
|
|
#
|
|
# DW::Controller::Admin::FileEdit
|
|
#
|
|
# Frontend for editing site content stored in local files. Note that
|
|
# any edits are saved in the includetext table, not in the actual file.
|
|
# (File contents are loaded using the LJ::load_include function.)
|
|
#
|
|
# Authors:
|
|
# Jen Griffin <kareila@livejournal.com>
|
|
#
|
|
# Copyright (c) 2020 by Dreamwidth Studios, LLC.
|
|
#
|
|
# This program is free software; you may redistribute it and/or modify it under
|
|
# the same terms as Perl itself. For a copy of the license, please reference
|
|
# 'perldoc perlartistic' or 'perldoc perlgpl'.
|
|
#
|
|
|
|
package DW::Controller::Admin::FileEdit;
|
|
|
|
use strict;
|
|
|
|
use DW::Controller;
|
|
use DW::Controller::Admin;
|
|
use DW::Routing;
|
|
use DW::Template;
|
|
|
|
DW::Routing->register_string( "/admin/fileedit/index", \&index_controller, app => 1 );
|
|
DW::Controller::Admin->register_admin_page(
|
|
'/',
|
|
path => 'fileedit',
|
|
ml_scope => '/admin/fileedit/index.tt',
|
|
privs => ['fileedit']
|
|
);
|
|
|
|
sub index_controller {
|
|
my ( $ok, $rv ) = controller( form_auth => 1, privcheck => ['fileedit'] );
|
|
return $rv unless $ok;
|
|
|
|
my $scope = '/admin/fileedit/index.tt';
|
|
|
|
my $r = DW::Request->get;
|
|
my $form_args = $r->did_post ? $r->post_args : $r->get_args;
|
|
my $vars = {};
|
|
|
|
my $valid_filename = sub { return ( $_[0] =~ /^[a-zA-Z0-9-\_]{1,80}$/ ) };
|
|
|
|
{ # construct sorted list of files visible to remote
|
|
|
|
my $remote = $rv->{remote};
|
|
my %files = $remote->priv_args("fileedit");
|
|
my $INC_DIR = "$LJ::HTDOCS/inc";
|
|
|
|
if ( $files{'*'} ) {
|
|
|
|
# if user has access to edit all files, find what those files are!
|
|
delete $files{'*'};
|
|
opendir( DIR, $INC_DIR );
|
|
while ( my $f = readdir(DIR) ) {
|
|
$files{$f} = 1;
|
|
}
|
|
closedir(DIR);
|
|
}
|
|
|
|
# get rid of any listed files that don't match our safe pattern
|
|
my @fn = keys %files;
|
|
foreach my $f (@fn) {
|
|
delete $files{$f} unless $valid_filename->($f);
|
|
}
|
|
|
|
$vars->{files} = \%files;
|
|
$vars->{file_menu} = [ map { $_, $_ } ( sort keys %files ) ];
|
|
}
|
|
|
|
my $DEF_ROW = 30;
|
|
my $DEF_COL = 80;
|
|
|
|
my $mode = $form_args->{mode};
|
|
$mode ||= $form_args->{file} ? "edit" : "pick";
|
|
|
|
if ( $mode eq "pick" ) {
|
|
$vars->{formdata} = { r => $DEF_ROW, c => $DEF_COL };
|
|
return DW::Template->render_template( 'admin/fileedit/index.tt', $vars );
|
|
}
|
|
|
|
# all other modes require a file argument that needs validation
|
|
$vars->{file} = $form_args->{file};
|
|
|
|
return error_ml("$scope.error.nofile")
|
|
unless defined $vars->{file} && $vars->{files}->{ $vars->{file} };
|
|
|
|
if ( $mode eq "edit" ) {
|
|
my $load_file = sub {
|
|
my ($filename) = @_;
|
|
return undef unless $valid_filename->($filename);
|
|
return LJ::load_include($filename);
|
|
};
|
|
|
|
my $contents = $load_file->( $vars->{file} );
|
|
|
|
return error_ml( "$scope.error.noload", { filename => $vars->{file} } )
|
|
unless defined $contents;
|
|
|
|
# this is escaped by form.textarea in the template
|
|
$vars->{contents} = $contents;
|
|
|
|
$vars->{txt} = {
|
|
r => ( $form_args->{r} || $DEF_ROW ) + 0,
|
|
c => ( $form_args->{c} || $DEF_COL ) + 0,
|
|
w => ( $form_args->{w} ? "SOFT" : "OFF" ),
|
|
};
|
|
|
|
return DW::Template->render_template( 'admin/fileedit/editform.tt', $vars );
|
|
}
|
|
|
|
if ( $mode eq "save" ) {
|
|
return error_ml("bml.requirepost") unless $r->did_post;
|
|
|
|
my $save_file = sub {
|
|
my ( $filename, $content ) = @_;
|
|
return 0 unless $valid_filename->($filename);
|
|
|
|
my $dbh = LJ::get_db_writer();
|
|
$dbh->do(
|
|
"REPLACE INTO includetext (incname, inctext, updatetime) "
|
|
. "VALUES (?, ?, UNIX_TIMESTAMP())",
|
|
undef, $filename, $content
|
|
);
|
|
return 0 if $dbh->err;
|
|
|
|
LJ::MemCache::set( "includefile:$filename", $content );
|
|
return 1;
|
|
};
|
|
|
|
if ( $save_file->( $vars->{file}, $form_args->{contents} ) ) {
|
|
return DW::Controller->render_success( 'admin/fileedit/editform.tt',
|
|
{ file => $vars->{file} } );
|
|
}
|
|
else {
|
|
return error_ml("$scope.error.nosave");
|
|
}
|
|
}
|
|
|
|
# if we got here, we were passed a form mode other than "save"
|
|
return error_ml("$scope.error.mode");
|
|
}
|
|
|
|
1;
|