mourningdove/cgi-bin/LJ/Widget/ThemeChooser.pm

310 lines
12 KiB
Perl
Raw Permalink Normal View History

2026-05-24 01:03:05 +00:00
# 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::Widget::ThemeChooser;
use strict;
use base qw(LJ::Widget);
use Carp qw(croak);
use LJ::S2Theme;
use LJ::Customize;
sub ajax { 1 }
sub authas { 1 }
sub need_res { qw( stc/widgets/themechooser.css ) }
sub need_res_opts { priority => $LJ::OLD_RES_PRIORITY }
sub render_body {
my $class = shift;
my %opts = @_;
my $u = $class->get_effective_remote();
die "Invalid user." unless LJ::isu($u);
my $remote = LJ::get_remote();
my $getextra = $u->user ne $remote->user ? "?authas=" . $u->user : "";
my $getsep = $getextra ? "&" : "?";
my %cats = LJ::Customize->get_cats($u);
warn %opts;
# filter criteria
$opts{cat} //= "";
$opts{layoutid} //= 0;
$opts{designer} //= "";
$opts{search} //= "";
$opts{page} //= 1;
$opts{show} //= 12;
if ( $u->user ne $remote->user ) {
$opts{authas} = $u->user;
}
my $cat_title;
my @themes;
my $current = LJ::Customize->get_current_theme($u);
if ( $opts{cat} eq "all" ) {
@themes = LJ::S2Theme->load_all($u);
$cat_title = LJ::Lang::ml('widget.themechooser.header.all');
}
elsif ( $opts{cat} eq "custom" ) {
@themes = LJ::S2Theme->load_by_user($u);
$cat_title = LJ::Lang::ml('widget.themechooser.header.custom');
}
elsif ( $opts{cat} eq "base" ) {
@themes = LJ::S2Theme->load_default_themes();
$cat_title = $cats{'base'}->{text};
}
elsif ( $opts{cat} ) {
@themes = LJ::S2Theme->load_by_cat( $opts{cat} );
my $cat = $opts{cat};
$cat_title = $cats{$cat}->{text};
}
elsif ( $opts{layoutid} ) {
@themes = LJ::S2Theme->load_by_layoutid( $opts{layoutid}, $u );
my $layout_name = LJ::Customize->get_layout_name( $opts{layoutid}, user => $u );
$cat_title = LJ::ehtml($layout_name);
}
elsif ( $opts{designer} ) {
@themes = LJ::S2Theme->load_by_designer( $opts{designer} );
$cat_title = LJ::ehtml( $opts{designer} );
}
elsif ( $opts{search} ) {
@themes = LJ::S2Theme->load_by_search( $opts{search}, $u );
$cat_title = LJ::Lang::ml( 'widget.themechooser.header.search',
{ 'term' => LJ::ehtml( $opts{search} ) } );
}
else { # category is "featured"
@themes = LJ::S2Theme->load_by_cat("featured");
$cat_title = $cats{'featured'}->{text};
}
if ( $opts{cat} eq "base" ) {
# sort alphabetically by layout
@themes = sort { lc $a->layout_name cmp lc $b->layout_name } @themes;
}
else {
# sort themes with custom at the end, then alphabetically by theme
@themes =
sort { $a->is_custom <=> $b->is_custom }
sort { lc $a->name cmp lc $b->name } @themes;
}
# remove any themes from the array that are not defined or whose layout or theme is not active
for ( my $i = 0 ; $i < @themes ; $i++ ) {
my $layout_is_active = LJ::Hooks::run_hook( "layer_is_active", $themes[$i]->layout_uniq );
my $theme_is_active = LJ::Hooks::run_hook( "layer_is_active", $themes[$i]->uniq );
unless ( ( defined $themes[$i] )
&& ( !defined $layout_is_active || $layout_is_active )
&& ( !defined $theme_is_active || $theme_is_active ) )
{
splice( @themes, $i, 1 );
$i--; # we just removed an element from @themes
}
}
@themes = LJ::Customize->remove_duplicate_themes(@themes);
my $max_page =
$opts{show} ne "all" ? POSIX::ceil( scalar(@themes) / $opts{show} ) || 1 : 1;
if ( $opts{show} ne "all" ) {
my $i_first = $opts{show} * ( $opts{page} - 1 );
my $i_last = ( $opts{show} * $opts{page} ) - 1;
@themes = splice( @themes, $i_first, $opts{show} );
}
my @theme_data = ();
for my $theme (@themes) {
my $current_theme = ( $theme->themeid && ( $theme->themeid == $current->themeid ) )
|| ( ( $theme->layoutid == $current->layoutid )
&& !$theme->themeid
&& !$current->themeid ) ? 1 : 0;
my $no_layer_edit = LJ::Hooks::run_hook( "no_theme_or_layer_edit", $u );
push @theme_data, get_theme_data( $theme, $current_theme, $no_layer_edit );
}
my $vars = {
cat_title => $cat_title,
max_page => $max_page,
themes => \@theme_data,
qargs => \%opts
};
return DW::Template->template_string( 'widget/themechooser.tt', $vars );
}
sub get_theme_data {
my ( $theme, $current, $no_layer_edit ) = @_;
my $tmp = {
imgurl => $theme->preview_imgurl,
layoutid => $theme->layoutid,
themeid => $theme->themeid,
name => $theme->{'name'},
designer => $theme->designer,
layout => $theme->{'layout_name'},
};
$tmp->{'designer_link'} = LJ::create_url(
"/customize",
keep_args => [ 'show', 'authas' ],
args => { designer => $theme->designer }
) if $theme->designer;
$tmp->{'layout_link'} = LJ::create_url(
"/customize",
keep_args => [ 'show', 'authas' ],
args => { layoutid => $theme->layoutid }
);
$tmp->{'current'} = $current;
if ( $current && !$no_layer_edit && $theme->is_custom ) {
$tmp->{can_edit_layout} = $theme->layoutid && !$theme->{layout_uniq} ? 1 : 0;
$tmp->{can_edit_theme} = $theme->themeid && !$theme->{uniq} ? 1 : 0;
}
if ( $theme->themeid ) {
$tmp->{preview_url} = LJ::create_url( "/customize/preview_redirect",
args => { 'themeid' => $theme->themeid } );
}
else {
$tmp->{preview_url} = LJ::create_url( "/customize/preview_redirect",
args => { 'layoutid' => $theme->layoutid } );
}
return $tmp;
}
sub handle_post {
my $class = shift;
my $post = shift;
my %opts = @_;
my $u = $class->get_effective_remote();
die "Invalid user." unless LJ::isu($u);
my $themeid = $post->{apply_themeid} + 0;
my $layoutid = $post->{apply_layoutid} + 0;
# we need to load sponsor's themes for sponsored users
my $substitue_user = LJ::Hooks::run_hook( "substitute_s2_layers_user", $u );
my $effective_u = defined $substitue_user ? $substitue_user : $u;
my $theme;
if ($themeid) {
$theme = LJ::S2Theme->load_by_themeid( $themeid, $effective_u );
}
elsif ($layoutid) {
$theme = LJ::S2Theme->load_custom_layoutid( $layoutid, $effective_u );
}
else {
die "No theme id or layout id specified.";
}
LJ::Customize->apply_theme( $u, $theme );
LJ::Hooks::run_hooks( 'apply_theme', $u );
return;
}
sub js {
q [
initWidget: function () {
var self = this;
var filter_links = DOM.getElementsByClassName(document, "theme-cat");
filter_links = filter_links.concat(DOM.getElementsByClassName(document, "theme-layout"));
filter_links = filter_links.concat(DOM.getElementsByClassName(document, "theme-designer"));
filter_links = filter_links.concat(DOM.getElementsByClassName(document, "theme-page"));
page_links = document.querySelectorAll(".pagination a");
page_links.forEach(link => {
filter_links.push(link);
})
// add event listeners to all of the category, layout, designer, and page links
// adding an event listener to page is done separately because we need to be sure to use that if it is there,
// and we will miss it if it is there but there was another arg before it in the URL
filter_links.forEach(function (filter_link) {
console.log(filter_link);
var getArgs = LiveJournal.parseGetArgs(filter_link.href);
if (getArgs["page"]) {
DOM.addEventListener(filter_link, "click", function (evt) { Customize.ThemeNav.filterThemes(evt, "page", getArgs["page"]) });
} else {
for (var arg in getArgs) {
if (!getArgs.hasOwnProperty(arg)) continue;
if (arg == "authas" || arg == "show") continue;
DOM.addEventListener(filter_link, "click", function (evt) { Customize.ThemeNav.filterThemes(evt, arg, getArgs[arg]) });
break;
}
}
});
// add event listeners to all of the apply theme forms
var apply_forms = DOM.getElementsByClassName(document, "theme-form");
apply_forms.forEach(function (form) {
DOM.addEventListener(form, "submit", function (evt) { self.applyTheme(evt, form) });
});
// add event listeners to the preview links
var preview_links = DOM.getElementsByClassName(document, "theme-preview-link");
preview_links.forEach(function (preview_link) {
DOM.addEventListener(preview_link, "click", function (evt) { self.previewTheme(evt, preview_link.href) });
});
// add event listener to the page and show dropdowns
//DOM.addEventListener($('.pagination a'), "click", function (evt) { Customize.ThemeNav.filterThemes(evt, "page", $('page_dropdown_top').value) });
// DOM.addEventListener($('page_dropdown_bottom'), "change", function (evt) { Customize.ThemeNav.filterThemes(evt, "page", $('page_dropdown_bottom').value) });
DOM.addEventListener($('show_dropdown_top'), "change", function (evt) { Customize.ThemeNav.filterThemes(evt, "show", $('show_dropdown_top').value) });
DOM.addEventListener($('show_dropdown_bottom'), "change", function (evt) { Customize.ThemeNav.filterThemes(evt, "show", $('show_dropdown_bottom').value) });
},
applyTheme: function (evt, form) {
var given_themeid = form["Widget[ThemeChooser]_apply_themeid"].value + "";
var given_layoutid = form["Widget[ThemeChooser]_apply_layoutid"].value + "";
$("theme_btn_" + given_layoutid + given_themeid).disabled = true;
DOM.addClassName($("theme_btn_" + given_layoutid + given_themeid), "theme-button-disabled disabled");
this.doPost({
apply_themeid: given_themeid,
apply_layoutid: given_layoutid
});
Event.stop(evt);
},
onData: function (data) {
Customize.ThemeNav.updateContent({
method: "GET",
cat: Customize.cat,
layoutid: Customize.layoutid,
designer: Customize.designer,
search: Customize.search,
page: Customize.page,
show: Customize.show,
theme_chooser_id: $('theme_chooser_id').value
});
alert(Customize.ThemeChooser.confirmation);
LiveJournal.run_hook("update_other_widgets", "ThemeChooser");
},
previewTheme: function (evt, href) {
window.open(href, 'theme_preview', 'resizable=yes,status=yes,toolbar=no,location=no,menubar=no,scrollbars=yes');
Event.stop(evt);
},
onRefresh: function (data) {
this.initWidget();
}
];
}
1;