mourningdove/cgi-bin/LJ/S2Theme.pm

1131 lines
32 KiB
Perl
Raw 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::S2Theme;
use strict;
use Carp qw(croak);
use LJ::Customize;
use LJ::ModuleLoader;
LJ::ModuleLoader->require_subclasses("LJ::S2Theme");
sub init {
1;
}
##################################################
# Class Methods
##################################################
# FIXME: This should be configurable
sub default_themes {
my $class = $_[0];
my %default_themes;
%default_themes = (
abstractia => 'abstractia/darkcarnival',
bases => 'bases/tropical',
basicboxes => 'basicboxes/green',
bannering => 'bannering/overthehills',
blanket => 'blanket/peach',
boxesandborders => 'boxesandborders/gray',
brittle => 'brittle/rust',
ciel => 'ciel/cloudydays',
compartmentalize => 'compartmentalize/poppyfields',
core2base => 'core2base/testing',
corinthian => 'corinthian/deepseas',
crisped => 'crisped/freshcotton',
crossroads => 'crossroads/lettuce',
database => 'database/blue',
drifting => 'drifting/blue',
dustyfoot => 'dustyfoot/dreamer',
easyread => 'easyread/green',
goldleaf => 'goldleaf/elegantnotebook',
fantaisie => 'fantaisie/unrelentingroutine',
fiveam => 'fiveam/earlyedition',
fluidmeasure => 'fluidmeasure/spice',
forthebold => 'forthebold/tealeaves',
funkycircles => 'funkycircles/darkpurple',
hibiscus => 'hibiscus/tropical',
headsup => 'headsup/caturdaygreytabby',
leftovers => 'leftovers/fruitsalad',
lefty => 'lefty/greenmachine',
librariansdream => 'librariansdream/grayscalelight',
lineup => 'lineup/modernity',
marginless => 'marginless/mars',
mobility => 'mobility/ivoryalcea',
modular => 'modular/mediterraneanpeach',
motion => 'motion/blue',
negatives => 'negatives/black',
nouveauoleanders => 'nouveauoleanders/sienna',
paletteable => 'paletteable/descending',
paperme => 'paperme/newleaf',
patsy => 'patsy/retro',
pattern => 'pattern/foundinthedesert',
planetcaravan => 'planetcaravan/cheerfully',
practicality => 'practicality/warmth',
refriedtablet => 'refriedtablet/refriedclassic',
seamless => 'seamless/pinkenvy',
skittlishdreams => 'skittlishdreams/orange',
snakesandboxes => 'snakesandboxes/pinkedout',
steppingstones => 'steppingstones/purple',
strata => 'strata/springmorning',
summertime => 'summertime/tenniscourt',
tectonic => 'tectonic/fission',
tranquilityiii => 'tranquilityiii/nightsea',
trifecta => 'trifecta/handlewithcare',
wideopen => 'wideopen/koi',
venture => 'venture/radiantaqua',
zesty => 'zesty/white',
);
my %local_default_themes =
eval "use LJ::S2Theme_local; 1;"
? $class->local_default_themes
: ();
%default_themes = ( %default_themes, %local_default_themes );
return %default_themes;
}
# returns the uniq of the default theme for the given layout id or uniq (for lazy migration)
sub default_theme {
my $class = shift;
my $layout = shift;
my %opts = @_;
# turn the given $layout into a uniq if it's an id
my $pub = LJ::S2::get_public_layers();
if ( $layout =~ /^\d+$/ ) {
$layout = $pub->{$layout}->{uniq};
}
# return if this is a custom layout
return "" unless ref $pub->{$layout};
# remove the /layout part of the uniq to just get the layout name
$layout =~ s/\/layout$//;
my %default_themes = $class->default_themes;
my $default_theme = $default_themes{$layout};
die "Default theme for layout $layout does not exist." unless $default_theme;
return $default_theme;
}
sub load {
my $class = shift;
my %opts = @_;
# load a single given theme by theme id
# will check user themes if user opt is specified and themeid is not a system theme
if ( $opts{themeid} ) {
return $class->load_by_themeid( $opts{themeid}, $opts{user} );
# load all themes of a single given layout id
# will check user themes in addition to system themes if user opt is specified
}
elsif ( $opts{layoutid} ) {
return $class->load_by_layoutid( $opts{layoutid}, $opts{user} );
# load the default theme of a single given layout id
}
elsif ( $opts{default_of} ) {
return $class->load_default_of( $opts{default_of} );
# load all themes of a single given uniq (layout or theme)
}
elsif ( $opts{uniq} ) {
return $class->load_by_uniq( $opts{uniq} );
# load all themes of a single given category
}
elsif ( $opts{cat} ) {
return $class->load_by_cat( $opts{cat} );
# load all themes by a particular designer
}
elsif ( $opts{designer} ) {
return $class->load_by_designer( $opts{designer} );
# load all custom themes of the user
}
elsif ( $opts{user} ) {
return $class->load_by_user( $opts{user} );
# load all themes that match a particular search term
}
elsif ( $opts{search} ) {
return $class->load_by_search( $opts{search}, $opts{user} );
# load custom layout with themeid of 0
}
elsif ( $opts{custom_layoutid} ) {
return $class->load_custom_layoutid( $opts{custom_layoutid}, $opts{user} );
# load all themes
# will load user themes in addition to system themes if user opt is specified
}
elsif ( $opts{all} ) {
return $class->load_all( $opts{user} );
}
# no valid option given
die
"Must pass one or more of the following options to theme loader: themeid, layoutid, default_of, uniq, cat, designer, user, custom_layoutid, all";
}
sub load_by_themeid {
my $class = shift;
my $themeid = shift;
my $u = shift;
return $class->new( themeid => $themeid, user => $u );
}
sub load_by_layoutid {
my $class = shift;
my $layoutid = shift;
my $u = shift;
my @themes;
my $pub = LJ::S2::get_public_layers();
my $children = $pub->{$layoutid}->{children};
foreach my $themeid (@$children) {
next unless $pub->{$themeid}->{type} eq "theme";
push @themes, $class->new( themeid => $themeid );
}
if ($u) {
my $userlay = LJ::S2::get_layers_of_user($u);
foreach my $layer ( keys %$userlay ) {
my $layer_type = $userlay->{$layer}->{type};
# custom themes of the given layout
if ( $layer_type eq "theme" && $userlay->{$layer}->{b2lid} == $layoutid ) {
push @themes, $class->new( themeid => $layer, user => $u );
# custom layout that is the given layout (no theme)
}
elsif ( $layer_type eq "layout" && $userlay->{$layer}->{s2lid} == $layoutid ) {
push @themes, $class->new_custom_layout( layoutid => $layer, user => $u );
}
}
}
return @themes;
}
sub load_default_of {
my $class = shift;
my $layoutid = shift;
my %opts = @_;
my $default_theme = $class->default_theme( $layoutid, %opts );
return $default_theme ? $class->load_by_uniq($default_theme) : undef;
}
sub load_default_themes {
my $class = $_[0];
my @themes;
my %default_themes = $class->default_themes;
return unless %default_themes;
foreach my $uniq ( values %default_themes ) {
my $theme = $class->load_by_uniq( $uniq, silent_failure => 1 );
push @themes, $theme if $theme;
}
return @themes;
}
sub load_by_uniq {
my ( $class, $uniq, %opts ) = @_;
my $pub = LJ::S2::get_public_layers();
if ( $pub->{$uniq} && $pub->{$uniq}->{type} eq "theme" ) {
return $class->load_by_themeid( $pub->{$uniq}->{s2lid} );
}
elsif ( $pub->{$uniq} && $pub->{$uniq}->{type} eq "layout" ) {
return $class->load_by_layoutid( $pub->{$uniq}->{s2lid} );
}
my $msg = "Given uniq is not a valid layout or theme: $uniq";
if ( $opts{silent_failure} ) {
warn $msg;
return undef;
}
else {
die $msg;
}
}
sub load_by_cat {
my $class = shift;
my $cat = shift;
my @themes;
my $pub = LJ::S2::get_public_layers();
foreach my $layer ( keys %$pub ) {
next unless $layer =~ /^\d+$/;
next unless $pub->{$layer}->{type} eq "theme";
my $theme = $class->new( themeid => $layer );
# we have a theme, now see if it's in the given category
foreach my $possible_cat ( $theme->cats ) {
next unless $possible_cat eq $cat;
push @themes, $theme;
last;
}
}
return @themes;
}
sub load_by_designer {
my $class = shift;
my $designer = shift;
# decode and lowercase and remove spaces
$designer = LJ::durl($designer);
$designer = lc $designer;
$designer =~ s/\s//g;
my @themes;
my $pub = LJ::S2::get_public_layers();
foreach my $layer ( keys %$pub ) {
next unless $layer =~ /^\d+$/;
next unless $pub->{$layer}->{type} eq "theme";
my $theme = $class->new( themeid => $layer );
# we have a theme, now see if it's made by the given designer
my $theme_designer = lc $theme->designer;
$theme_designer =~ s/\s//g;
push @themes, $theme if $theme_designer eq $designer;
}
return @themes;
}
sub load_by_user {
my $class = shift;
my $u = shift;
die "Invalid user object." unless LJ::isu($u);
my @themes;
my $userlay = LJ::S2::get_layers_of_user($u);
foreach my $layer ( keys %$userlay ) {
my $layer_type = $userlay->{$layer}->{type};
if ( $layer_type eq "theme" ) {
push @themes, $class->new( themeid => $layer, user => $u );
}
elsif ( $layer_type eq "layout" ) {
push @themes, $class->new_custom_layout( layoutid => $layer, user => $u );
}
}
return @themes;
}
sub load_by_search {
my $class = shift;
my $term = shift;
my $u = shift;
# decode and lowercase and remove spaces
$term = LJ::durl($term);
$term = lc $term;
$term =~ s/\s//g;
my @themes_ret;
my @themes = $class->load_all($u);
foreach my $theme (@themes) {
my $theme_name = lc $theme->name;
$theme_name =~ s/\s//g;
my $layout_name = lc $theme->layout_name;
$layout_name =~ s/\s//g;
my $designer_name = lc $theme->designer;
$designer_name =~ s/\s//g;
if ( $theme_name =~ /\Q$term\E/
|| $layout_name =~ /\Q$term\E/
|| $designer_name =~ /\Q$term\E/ )
{
push @themes_ret, $theme;
}
}
return @themes_ret;
}
sub load_custom_layoutid {
my $class = shift;
my $layoutid = shift;
my $u = shift;
return $class->new_custom_layout( layoutid => $layoutid, user => $u );
}
sub load_all {
my $class = shift;
my $u = shift;
my @themes;
my $pub = LJ::S2::get_public_layers();
foreach my $layer ( keys %$pub ) {
next unless $layer =~ /^\d+$/;
next unless $pub->{$layer}->{type} eq "theme";
next if LJ::S2::is_public_internal_layer($layer);
push @themes, $class->new( themeid => $layer );
}
if ($u) {
push @themes, $class->load_by_user($u);
}
return @themes;
}
# custom layouts without themes need special treatment when creating an S2Theme object
sub new_custom_layout {
my $class = shift;
my $self = {};
my %opts = @_;
my $layoutid = $opts{layoutid} + 0;
die "No layout id given." unless $layoutid;
my $u = $opts{user};
die "Invalid user object." unless LJ::isu($u);
my %outhash = ();
my $userlay = LJ::S2::get_layers_of_user($u);
unless ( ref $userlay->{$layoutid} ) {
LJ::S2::load_layer_info( \%outhash, [$layoutid] );
die "Given layout id does not correspond to a layer usable by the given user."
unless $outhash{$layoutid}->{is_public};
}
my $using_layer_info = scalar keys %outhash;
die "Given layout id does not correspond to a layout."
unless $using_layer_info
? $outhash{$layoutid}->{type} eq "layout"
: $userlay->{$layoutid}->{type} eq "layout";
my $layer;
if ($using_layer_info) {
$layer = LJ::S2::load_layer($layoutid);
}
$self->{s2lid} = 0;
$self->{b2lid} = $layoutid;
$self->{name} = LJ::Lang::ml('s2theme.themename.notheme');
$self->{uniq} = undef;
$self->{is_custom} = 1;
$self->{coreid} = $using_layer_info ? $layer->{b2lid} + 0 : $userlay->{$layoutid}->{b2lid} + 0;
$self->{layout_name} = LJ::Customize->get_layout_name( $layoutid, user => $u );
$self->{layout_uniq} = undef;
bless $self, $class;
return $self;
}
sub new {
my $class = shift;
my $self = {};
my %opts = @_;
my $themeid = $opts{themeid} + 0;
die "No theme id given." unless $themeid;
return $LJ::CACHE_S2THEME{$themeid} if exists $LJ::CACHE_S2THEME{$themeid};
my $layers = LJ::S2::get_public_layers();
my $is_custom = 0;
my %outhash = ();
unless ( $layers->{$themeid} && $layers->{$themeid}->{uniq} ) {
if ( $opts{user} ) {
my $u = $opts{user};
die "Invalid user object." unless LJ::isu($u);
$layers = LJ::S2::get_layers_of_user($u);
unless ( ref $layers->{$themeid} ) {
LJ::S2::load_layer_info( \%outhash, [$themeid] );
return undef if $opts{undef_if_missing} && !exists $outhash{$themeid};
die "Given theme id does not correspond to a layer usable by the given user."
unless $outhash{$themeid}->{is_public};
}
$is_custom = 1;
}
else {
die "Given theme id does not correspond to a system layer.";
}
}
my $using_layer_info = scalar keys %outhash;
if ( $opts{undef_if_missing} ) {
return undef
unless $using_layer_info
? exists $outhash{$themeid}
: exists $layers->{$themeid}->{type};
}
die "Given theme id does not correspond to a theme."
unless $using_layer_info
? $outhash{$themeid}->{type} eq "theme"
: $layers->{$themeid}->{type} eq "theme";
my $layer;
if ($using_layer_info) {
$layer = LJ::S2::load_layer($themeid);
}
$self->{s2lid} = $themeid;
$self->{b2lid} = $using_layer_info ? $layer->{b2lid} + 0 : $layers->{$themeid}->{b2lid} + 0;
$self->{name} = $using_layer_info ? $layer->{name} : $layers->{$themeid}->{name};
$self->{uniq} = $is_custom ? undef : $layers->{$themeid}->{uniq};
$self->{is_custom} = $is_custom;
$self->{name} = LJ::Lang::ml( 's2theme.themename.default', { themeid => "#$themeid" } )
unless $self->{name};
# get the coreid by first checking the user layers and then the public layers for the layout
my $pub = LJ::S2::get_public_layers();
my $userlay = $opts{user} ? LJ::S2::get_layers_of_user( $opts{user} ) : "";
if ($using_layer_info) {
my $layout_layer = LJ::S2::load_layer( $self->{b2lid} );
$self->{coreid} = $layout_layer->{b2lid};
}
else {
$self->{coreid} = $userlay->{ $self->{b2lid} }->{b2lid} + 0
if ref $userlay && $userlay->{ $self->{b2lid} };
$self->{coreid} = $pub->{ $self->{b2lid} }->{b2lid} + 0 unless $self->{coreid};
}
# layout name
$self->{layout_name} = LJ::Customize->get_layout_name( $self->{b2lid}, user => $opts{user} );
# layout uniq
$self->{layout_uniq} = $pub->{ $self->{b2lid} }->{uniq}
if $pub->{ $self->{b2lid} } && $pub->{ $self->{b2lid} }->{uniq};
# package name for the theme
my $theme_class = $self->{uniq};
if ($theme_class) {
$theme_class =~ s/-/_/g;
$theme_class =~ s/\//::/;
$theme_class = "LJ::S2Theme::$theme_class";
}
# package name for the layout
my $layout_class = $self->{uniq} || $self->{layout_uniq} || '';
$layout_class =~ s/\/.+//;
$layout_class =~ s/-/_/g;
$layout_class = "LJ::S2Theme::$layout_class";
# make this theme an object of the lowest level class that's defined
if ( $theme_class && eval { $theme_class->init } ) {
bless $self, $theme_class;
}
elsif ( eval { $layout_class->init } ) {
bless $self, $layout_class;
}
else {
bless $self, $class;
}
$LJ::CACHE_S2THEME{$themeid} = $self;
return $self;
}
##################################################
# Object Methods
##################################################
sub s2lid {
return $_[0]->{s2lid};
}
*themeid = \&s2lid;
sub b2lid {
return $_[0]->{b2lid};
}
*layoutid = \&b2lid;
sub coreid {
return $_[0]->{coreid};
}
sub name {
return $_[0]->{name};
}
sub layout_name {
return $_[0]->{layout_name};
}
sub uniq {
return $_[0]->{uniq} || "";
}
sub layout_uniq {
return $_[0]->{layout_uniq};
}
*is_system_layout = \&layout_uniq; # if the theme's layout has a uniq, then it's a system layout
sub is_custom {
return $_[0]->{is_custom};
}
sub preview_imgurl {
my $self = shift;
my $imgurl = "$LJ::IMGPREFIX/customize/previews/";
$imgurl .= $self->uniq ? $self->uniq : "custom-layer";
$imgurl .= ".png";
return $imgurl;
}
sub available_to {
my $self = shift;
my $u = shift;
# theme isn't available to $u if the layout isn't
return LJ::S2::can_use_layer( $u, $self->uniq )
&& LJ::S2::can_use_layer( $u, $self->layout_uniq );
}
# wizard-layoutname
sub old_style_name_for_theme {
my $self = shift;
return "wizard-" . ( ( split( "/", $self->uniq ) )[0] || $self->layoutid );
}
# wizard-layoutname/themename
sub new_style_name_for_theme {
my $self = shift;
return "wizard-" . ( $self->uniq || $self->themeid || $self->layoutid );
}
# find the appropriate styleid for this theme
# if a style for the layout but not the theme exists, rename it to match the theme
sub get_styleid_for_theme {
my $self = shift;
my $u = shift;
my $style_name_old = $self->old_style_name_for_theme;
my $style_name_new = $self->new_style_name_for_theme;
my $userstyles = LJ::S2::load_user_styles($u);
foreach my $styleid ( keys %$userstyles ) {
my $style_name = $userstyles->{$styleid};
next unless $style_name eq $style_name_new || $style_name eq $style_name_old;
# lazy migration of style names from wizard-layoutname to wizard-layoutname/themename
LJ::S2::rename_user_style( $u, $styleid, $style_name_new )
if $style_name eq $style_name_old;
return $styleid;
}
return 0;
}
sub get_custom_i18n_layer_for_theme {
my $self = shift;
my $u = shift;
my $userlay = LJ::S2::get_layers_of_user($u);
my $layoutid = $self->layoutid;
my $i18n_layer = 0;
# scan for a custom i18n layer
foreach my $layer ( values %$userlay ) {
last
if $layer->{b2lid} == $layoutid
&& $layer->{type} eq 'i18n'
&& ( $i18n_layer = $layer->{s2lid} );
}
return $i18n_layer;
}
sub get_custom_user_layer_for_theme {
my $self = shift;
my $u = shift;
my $userlay = LJ::S2::get_layers_of_user($u);
my $layoutid = $self->layoutid;
my $user_layer = 0;
# scan for a custom user layer
# ignore auto-generated user layers, since they're not custom layers
foreach my $layer ( values %$userlay ) {
last
if $layer->{b2lid} == $layoutid
&& $layer->{type} eq 'user'
&& $layer->{name} ne 'Auto-generated Customizations'
&& ( $user_layer = $layer->{s2lid} );
}
return $user_layer;
}
sub get_preview_styleid {
my $self = shift;
my $u = shift;
# get the styleid of the _for_preview style
my $styleid = $u->prop('theme_preview_styleid');
my $style = $styleid ? LJ::S2::load_style($styleid) : undef;
if ( !$styleid || !$style ) {
$styleid = LJ::S2::create_style( $u, "_for_preview" );
$u->set_prop( 'theme_preview_styleid', $styleid );
}
return "" unless $styleid;
# if we already have a style for this theme, copy it to the _for_preview style and use it
# -- don't re-use the theme layer though, since this might be a layout (old format) style
# instead of a theme (new format) style
my $theme_styleid = $self->get_styleid_for_theme($u);
if ($theme_styleid) {
my $style = LJ::S2::load_style($theme_styleid);
my %layers;
foreach my $layer (qw( core i18nc layout i18n user )) {
$layers{$layer} = $style->{layer}->{$layer};
}
$layers{theme} = $self->themeid;
LJ::S2::set_style_layers( $u, $styleid, %layers );
return $styleid;
}
# we don't have a style for this theme, so get the new layers and set them to _for_preview directly
my %style = LJ::S2::get_style($u);
my $i18n_layer = $self->get_custom_i18n_layer_for_theme($u);
# for the i18nc layer, match the user's preferences if they're not switching cores
# if they are switching cores, we don't know what the equivalent should be
my $i18nc_layer = ( $self->coreid == $style{core} ) ? $style{i18nc} : undef;
my %layers = (
core => $self->coreid,
i18nc => $i18nc_layer,
layout => $self->layoutid,
i18n => $i18n_layer,
theme => $self->themeid,
user => 0,
);
LJ::S2::set_style_layers( $u, $styleid, %layers );
return $styleid;
}
sub all_categories {
my ( undef, %args ) = @_;
my $all = 1;
$all = $args{all} if exists $args{all};
my $post_filter = sub {
my %data = map { $_ => 1 } @_;
$data{featured} = 1 if $args{special};
delete $data{featured} unless $args{special};
my %order = ( featured => -1 );
return sort { ( $order{$a} || 0 ) <=> ( $order{$b} || 0 ) || $a cmp $b } keys %data;
};
my $memkey = "s2categories" . ( $all ? ":all" : "" );
my $minfo = LJ::MemCache::get($memkey);
return $post_filter->(@$minfo) if $minfo;
my $dbr = LJ::get_db_reader();
my $cats = $dbr->selectall_arrayref(
"SELECT k.keyword AS keyword "
. "FROM s2categories AS c, sitekeywords AS k WHERE "
. "c.kwid = k.kwid "
. ( $all ? "" : "AND c.active = 1 " )
. "GROUP BY keyword",
undef
);
my @rv = map { $_->[0] } @$cats;
LJ::MemCache::set( $memkey, \@rv );
return $post_filter->(@rv);
}
sub clear_global_cache {
LJ::MemCache::delete("s2categories");
LJ::MemCache::delete("s2categories:all");
}
sub metadata {
my $self = $_[0];
return $self->{metadata} if exists $self->{metadata};
my $VERSION_DATA = 1;
my $memkey = [ $self->s2lid, "s2meta:" . $self->s2lid ];
my ( $info, $minfo );
my $load_info_from_cats = sub {
my $cats = $_[0];
$cats->{featured}->{order} = -1;
$cats->{featured}->{special} = 1;
$info->{cats} = $cats;
$info->{active_cats} = [ grep { $cats->{$_}->{active} } keys %$cats ];
};
if ( $minfo = LJ::MemCache::get($memkey) ) {
if ( ref $minfo eq 'HASH'
|| $minfo->[0] != $VERSION_DATA )
{
# old data in the cache. delete.
LJ::MemCache::delete($memkey);
}
else {
my ( undef, $catstr, $cat_active ) = @$minfo;
my %id_map;
my $cats = {};
my ( $pos, $nulpos );
$pos = $nulpos = 0;
while ( ( $nulpos = index( $catstr, "\0", $pos ) ) > 0 ) {
my $kw = substr( $catstr, $pos, $nulpos - $pos );
my $id = unpack( "N", substr( $catstr, $nulpos + 1, 4 ) );
$pos = $nulpos + 5; # skip NUL + 4 bytes.
$cats->{$kw} = {
kwid => $id,
keyword => $kw,
};
$id_map{$id} = $cats->{$kw};
}
while ( length $cat_active >= 4 ) {
my ($id) = unpack "N", substr( $cat_active, 0, 4, '' );
$id_map{$id}->{active} = 1;
}
$load_info_from_cats->($cats);
}
}
unless ($info) {
my $dbr = LJ::get_db_reader();
my $cats = $dbr->selectall_hashref(
"SELECT c.kwid AS kwid, k.keyword AS keyword, c.active AS active "
. "FROM s2categories AS c, sitekeywords AS k WHERE "
. "s2lid = ? AND c.kwid = k.kwid",
'keyword', undef, $self->s2lid
);
$cats->{featured} ||= {
keyword => 'featured',
kwid => LJ::get_sitekeyword_id( 'featured', 1 ),
active => 0,
};
$load_info_from_cats->($cats);
$minfo = [
$VERSION_DATA,
join( '', map { pack( "Z*N", $_, $cats->{$_}->{kwid} ) } keys %$cats ) || "",
join( '',
map { pack( "N", $cats->{$_}->{kwid} ) }
grep { $cats->{$_}->{active} } keys %$cats )
|| "",
];
LJ::MemCache::set( $memkey, $minfo );
}
return $self->{metadata} = $info;
}
##################################################
# Methods for admin pages
##################################################
sub clear_cache {
my $self = $_[0];
delete $self->{metadata};
LJ::MemCache::delete( [ $self->s2lid, "s2meta:" . $self->s2lid ] );
}
##################################################
# Methods that return data from DB, *DO NOT OVERIDE*
##################################################
sub cats { # categories that the theme is in
return @{ $_[0]->metadata->{active_cats} };
}
##################################################
# Can be overriden if required
##################################################
sub designer { # designer of the theme
return $_[0]->{designer} if exists $_[0]->{designer};
my $id = $_[0]->s2lid;
my $bid = $_[0]->b2lid;
my $li = {};
LJ::S2::load_layer_info( $li, [ $id, $bid ] );
my $rv =
$li->{$id}->{author_name}
|| $li->{$bid}->{author_name}
|| "";
$_[0]->{designer} = $rv;
return $rv;
}
##################################################
# Methods that get overridden by child packages
##################################################
sub layouts {
( "1" => 1 )
} # theme layout/sidebar placement options ( layout type => property value or 1 if no property )
sub layout_prop { "" } # property that controls the layout/sidebar placement
sub show_sidebar_prop { "" } # property that controls whether a sidebar shows or not
sub linklist_support_tab {
"";
} # themes that don't use the linklist_support prop will have copy pointing them to the correct tab
# for appending layout-specific props to global props
sub _append_props {
my $self = shift;
my $method = shift;
my @props = @_;
my @defaults = eval "LJ::S2Theme->$method";
return ( @defaults, @props );
}
# props that shouldn't be shown in the wizard UI
sub hidden_props {
qw(
custom_control_strip_colors
control_strip_bgcolor
control_strip_fgcolor
control_strip_bordercolor
control_strip_linkcolor
);
}
# props by category heading
sub display_option_props {
qw(
num_items_recent
num_items_reading
num_items_icons
page_recent_items
page_friends_items
view_entry_disabled
use_journalstyle_entry_page
use_shared_pic
linklist_support
);
}
sub page_props {
qw (
color_page_background
color_page_text
color_page_link
color_page_link_active
color_page_link_hover
color_page_link_visited
color_page_border
color_page_details_text
font_base
font_fallback
font_base_size
font_base_units
image_background_page_group
image_background_page_url
image_background_page_repeat
image_background_page_position
);
}
sub module_props {
qw (
color_module_background
color_module_text
color_module_link
color_module_link_active
color_module_link_hover
color_module_link_visited
color_module_title_background
color_module_title
color_module_border
font_module_heading
font_module_heading_size
font_module_heading_units
font_module_text
font_module_text_size
font_module_text_units
image_background_module_group
image_background_module_url
image_background_module_repeat
image_background_module_position
text_module_userprofile
text_module_links
text_module_syndicate
text_module_tags
text_module_popular_tags
text_module_pagesummary
text_module_active_entries
text_module_customtext
text_module_customtext_url
text_module_customtext_content
text_module_credit
text_module_search
text_module_cuttagcontrols
text_module_subscriptionfilters
);
}
sub navigation_props {
qw (
text_view_recent
text_view_archive
text_view_friends
text_view_friends_comm
text_view_friends_filter
text_view_network
text_view_tags
text_view_memories
text_view_userinfo
);
}
sub header_props {
qw (
color_header_background
color_header_link
color_header_link_active
color_header_link_hover
color_header_link_visited
color_page_title
font_journal_title
font_journal_title_size
font_journal_title_units
font_journal_subtitle
font_journal_subtitle_size
font_journal_subtitle_units
image_background_header_group
image_background_header_url
image_background_header_repeat
image_background_header_position
image_background_header_height
);
}
sub footer_props {
qw (
color_footer_background
color_footer_link
color_footer_link_active
color_footer_link_hover
color_footer_link_visited
);
}
sub entry_props {
qw (
color_entry_link
color_entry_background
color_entry_text
color_entry_link_active
color_entry_link_hover
color_entry_link_visited
color_entry_title_background
color_entry_title
color_entry_interaction_links_background
color_entry_interaction_links
color_entry_interaction_links_active
color_entry_interaction_links_hover
color_entry_interaction_links_visited
color_entry_border
font_entry_title
font_entry_title_size
font_entry_title_units
image_background_entry_group
image_background_entry_url
image_background_entry_repeat
image_background_entry_position
text_edit_entry
text_edit_tags
text_mem_add
text_tell_friend
text_watch_comments
text_unwatch_comments
text_read_comments
text_read_comments_friends
text_read_comments_screened_visible
text_read_comments_screened
text_post_comment
text_post_comment_friends
text_permalink
text_entry_prev
text_entry_next
text_meta_groups
text_meta_location
text_meta_mood
text_meta_music
text_meta_xpost
text_tags
text_stickyentry_subject
text_nosubject
);
}
sub comment_props {
qw (
color_comment_title_background
color_comment_title
font_comment_title
font_comment_title_size
font_comment_title_units
);
}
sub archive_props {
qw (
);
}
1;